import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import sun.misc.BASE64Decoder; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.lang.annotation.IncompleteAnnotationException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.URLDecoder; //this import is only for java 1.8 //import java.util.Base64; import java.security.Key; import java.util.zip.GZIPInputStream; /** * Simples Servidor HTTP que desserializa dados recebidos nos seguintes formatos: * * 1) via HTTP POST em formato binário (ou seja, \xAC\xED) * 2) via HTTP POST como valor de algum parâmetro (eg. "ViewState") nos formatos 1) base64 (rO0...) ou 2) gzip+base64 (H4sI...) * 3) via cookies (header cookie) nos formatos base64 (rO0) ou gzip+base64 (H4sI) (eg. Cookie: JSESSIONID=rO0... ou Cookie: JSESSIONID=H4sI...) * 4) via Cookie rememberMe (like Apache Shiro), criptografado com aes-128-cbc e chave hardcoded * 5) via XML para explorar o XStream * * Após a desserialização, ele tenta fazer um cast para Integer, a fim de simular o que * ocorre em um servidor "real" (erro de casting após a desserialização) * * * OBS: Sobre Apache Shiro, ver: * https://github.com/apache/shiro/blob/master/crypto/cipher/src/main/java/org/apache/shiro/crypto/JcaCipherService.java * https://github.com/apache/shiro/blob/8acc82ab4775b3af546e3bbde928f299be62dc23/integration-tests/guice3/src/main/webapp/WEB-INF/shiro.ini * Para geracao do payload, use CommonsCollections2 ou CommonsCollections4 do ysoserial e criptografe com aes-128-cbc * Se preferir, existem mtos sccripts prontos para geracao do payload, veja: * ex: https://github.com/leveryd/vulndocker/blob/78ba54edbd2dd81f09bb6d3f03a446555e6b7614/vuln/shiro/shirotest.py * Análise: http://www.freebuf.com/articles/system/125187.html * * ----------------------------------------------------------------------- * Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine: * https://www.h2hc.com.br/revista/ * ----------------------------------------------------------------------- * * **** USAGE **** * * Compilando: * $ javac VulnerableHTTPServer.java -XDignore.symbol.file * * Executando * $ java VulnerableHTTPServer * * Ou, caso deseje testar payloads para explorar gadgets de bibliotecas específicas, use o -cp. Exs: * $ java -cp .:commons-collections-3.2.1.jar VulnerableHTTPServer * $ java -cp .:xstream-1.4.6.jar:commons-collections-3.2.1.jar VulnerableHTTPServer * * @author @joaomatosf */ public class VulnerableHTTPServer { public static void banner(){ System.out.println("* =============================================================== *"); System.out.println("* Simple Java HTTP Server for Deserialization Lab v0.01 *"); System.out.println("* https://github.com/joaomatosf/JavaDeserH2HC *"); System.out.println("* =============================================================== *"); System.out.println("You can inject java serialized objects in the following formats:"); System.out.println( "\n 1) Binary in HTTP POST (ie \\xAC\\xED). Ex:\n" + " $ curl 127.0.0.1:8000 --data-binary @ObjectFile.ser\n"+ "\n 2) Base64 or Gzip+Base64 via HTTP POST parameters. Ex:\n" + " $ curl 127.0.0.1:8000 -d \"ViewState=rO0ABXNy...\"\n"+ " $ curl 127.0.0.1:8000 -d \"ViewState=H4sICAeH...\"\n"+ "\n 3) Base64 or Gzip+Base64 in cookies. Ex:\n"+ " $ curl 127.0.0.1:8000 -H \"Cookie: JSESSIONID=rO0ABXNy...\"\n"+ " $ curl 127.0.0.1:8000 -H \"Cookie: JSESSIONID=H4sICAeH...\"\n"+ "\n 4) Base64 of AES-CBC encrypted with hardcoded Apache Shiro key. Ex:\n" + " $ curl 127.0.0.1:8000 -H \"Cookie: rememberMe=MTIzNDU2Nzg...\"\n"+ "\n 5) XML for XStream RCE vulnerability/serialization. Ex:\n" + " $ curl 127.0.0.1:8000 -d @file.xml\n -H \"Content-Type: application/xml\""); System.out.println("OBS: To test gadgets in specific libraries, run with -cp param. Ex:\n" + "$ java -cp .:commons-collections-3.2.1.jar VulnerableHTTPServer"); System.out.println("=================================================================="); } public static void main(String[] args) throws IOException { banner(); int port = 8000; HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); server.createContext("/", new HTTPHandler()); server.setExecutor(null); // creates a default executor server.start(); System.out.println("\nJRE Version: "+System.getProperty("java.version")); System.out.println("[INFO]: Listening on port "+port); System.out.println(); } static class HTTPHandler implements HttpHandler { String aesHardedCodeKey = "kPH+bIxk5D2deZiIxcaaaA=="; public void handle(HttpExchange t) throws IOException { System.out.println("[INFO]: Received "+t.getRequestMethod()+" "+t.getRequestURI()+" from: "+t.getRemoteAddress()); String responseMsg = null; boolean containsCookie = t.getRequestHeaders().containsKey("cookie"); // if there's a cookie with serialized java object if (containsCookie){ String object = t.getRequestHeaders().get("cookie").get(0); object = getObjectValue(object); if (object.startsWith("H4sI") || object.startsWith("rO0") ) responseMsg = deserialize(object); else { // try deserialize aes-cbc encrypted object byte[] plainText = decryptAES(object,aesHardedCodeKey); if (plainText == null) responseMsg = "\nAn error ocurred when decrypting the stream.\n"; else responseMsg = deserialize(new ByteArrayInputStream(plainText)); } } else if (t.getRequestMethod().equals("POST")){ InputStream input = t.getRequestBody(); // take 2 bytes from header to check if it is a raw object PushbackInputStream pbis = new PushbackInputStream( input, 2 ); byte [] header = new byte[2]; int len = pbis.read(header); pbis.unread( header, 0, len ); StringBuffer headerResult = new StringBuffer(); for (byte b: header) headerResult.append(String.format("%02x", b)); // deserialize raw if (headerResult.toString().equals("aced")) responseMsg = deserialize(pbis); // deserialize RAW else{ // deserialize H4sI, rO0,... // read input into string InputStreamReader isr = new InputStreamReader(pbis, "utf-8"); BufferedReader br = new BufferedReader(isr); String body = br.readLine(); String paramName = ""; String object = getObjectValue(body); if (object.startsWith("H4sI") || object.startsWith("rO0") ) responseMsg = deserialize(object); // deserialize H4sI, rO0... else if (object.startsWith("<") ) responseMsg = deserializeXStream(object); // xtream } }// end if POST else{ responseMsg = "" + "\n