In recent external pen tests, we have come across several Apache Struts instances that are vulnerable to a remote code execution (RCE) vulnerability. Our usual procedure for any RCE vulnerability that we are going to exploit is to upload a small web shell which allows us to run OS commands and receive the output. In the case of Apache Struts, that’d be a JSP web shell. From this web shell, we can download and execute any tools we want to run.
Super small JSP web shells have existed for a while, so it’s no issue to find one that can fit in a URL parameter for the Struts exploit. Where we ran into trouble was a Struts instance running on a Solaris server without outbound internet access.
- No outbound internet access
- No output to files from web shell (>, >>, |, etc. didn’t work)
- Very limited Java libraries
- Very limited upload size of web shell or other files
Without internet access, we couldn’t run commands to download reconnaissance or escalation tools (like port scanners, memory dumpers, or port forwarders). Without being able to echo to files we couldn’t easily upload scripts or tools chunk by chunk. With limited Java libraries and upload size for the web shell, we were unable to find a JSP file that supported file uploading.
The first solution we had some success with was to use native Java commands with the RCE vulnerability to output and append text to a file. We were not able to write many special characters (URL encoding wasn’t enough), so we resorted to base64 encoding our upload file, splitting it into chunks to append together, and then base64 decoding it on the server. This worked, but was slow and tedious, and if one of many requests didn’t succeed, then the entire uploaded file was junk.
The next and much better solution was to create a smaller, more powerful JSP web shell. Here were our requirements:
- Command execution and output
- File upload capability without size limits
- Less than 1kb
- No Java libraries outside of the core of 1.6
Keeping the file small involved changing everything to 1 letter variables, writing functions for anything long that got called more than once, removing absolutely any whitespace we could, and finding the shortest syntax for every single task. The file was essentially unreadable at this point, so we kept 2 running copies for testing any changes: the shortened one, and one with proper whitespace and variable names.
The New cmd.jsp
In the end, we created a JSP web shell with file upload and command execution/output abilities that totaled only 976 bytes. Packaged as a .war file, it is only 662 bytes.
- Command output is properly html encoded so there is none of the wonky formatting that we often get.
- Files to be uploaded are base64 encoded client side and decoded server side so file format or contents is no issue.
- Uploaded files are read in as a byte stream by the JSP page which circumvents most server defined maximum request sizes.