书接上回,上一节中我们分析了GI的工作流程,
- ASM 框架的vistor 遍历机制:类、方法、接口实现关系表的构建
- Passthrough 程序内污点分析的实现:ASM和逆拓扑排序
- Callgraph 程序间污点分析的实现:利用passthrough生成ret污点
- GadegetChain BSF实现
在此基础上,主要针对以下问题再深入分析分析:
- su18师傅与Lango师傅都有提到的一些问题及改进点
- Java的反射、多态问题是如何处理的等等
- 已知链的覆盖及漏报情况
先来过一遍su18师傅提到的几个改进点
0x01 su18师傅的改进点
1.1 继承方法
前提知识:当一个类的方法A.method1 污点可以传播给B.method2,那么A的子类subA,如果subA没有重写method1,那么subA.method1 也可以把污点传播给B.method2。
这个很好理解,但是在GI的整个过程中
- 在CallGraph 阶段,ASM在遍历class的时候,只会找出所有的A.method call B.method,生成对应的关系
- 在GadgetChain 阶段,也只会遍历接口方法的具体实现
并没有做类似的传播。
解决
1.2 路径爆炸
su18师傅只是简单提到了路径爆炸的情况,并没有给出哪种情况会出现路径爆炸。
看swing师傅的代码,是在GadgetChain#discover 的while循环外做的,疑惑。
解决
1.3 transient 字段
这个倒是容易忽略的一个点,直接引用su18师傅的结论吧。
实际上,transient 字段只代表开发人员不希望 Java 原生序列化流程来处理它,不代表开发人员自己不会处理它,可能通过自定义 writeObject/readObject 方法内的逻辑来还原对象的状态,因此 transient 修饰字段的对象不会被自动反序列化,但是可以参与反序列化流程,前提是用户在调用前对其进行了处理。
GI 是在Passthrough 和CallGraph 两阶段都对transient做了过滤,具体在visitFieldInsn 内部,简单解释就是认为当操作this.field的时候,如果field 属性,那就忽略对this 0的污染。
解决
1.4 反射
众所周知,Java反射是静态分析的一大难点,GI是这么实现的:
- 在TaintTrackingMethodVisitor 中,内置了以下两条反射的规则,当作传播函数
1 | { "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", 0, 1 }, |
- Class.getMethod 的程序间内污点返回为0,1,也就是this和arg0会污染ret
- Class.getMethods 的程序间内污点返回为0,也就是this可以污染ret
- 在GadgetChain中,将Method.invoke 当作sink点
1 | if (method.getClassReference().getName().equals("java/lang/reflect/Method") |
su18师傅这里是针对1中的传递函数讲的,意思大致是需要class和method都可控才能当作污点,而这个规则是class 或者method 分别都会被当作污点,这里会造成误报。
1.5 动态代理
GI是对代理是这么实现的:
- 在SimpleSourceDiscovery 中,将InvocationHandler的子类invoke方法都当作source节点
1 | // Using the proxy trick, anything extending serializable and invocation handler is tainted. |
su18: InvocationHandler 在反序列化构造中可以起到相当大的作用,既能当 Source,又能当 Chain,还能当 Sink,因此需要对其进行格外的处理。
所以这块只考虑了Source,没考虑其他Chain和Sink。
解决
1.6 Sink/Source 点增强
这里su18师傅归纳为两点:补充、sink前置、source后置。
比如:
- 前置sink点org.apache.commons.collections.Transformer#transform
浅蓝师傅的那一波json,可以补充一波了。
解决
1.7 Agent 技术加持
静态分析无法生成动态加载类,比如rmi stub
su18 师傅提到在动态运行时获取完整的类,作为补充,的确是个很新的想法。
动静结合,肯定是之后的趋势,不过目前都还存在各自的问题。
解决
0x02 Longofo 师傅的问题
总结下Longofo师傅 反馈的GI几点问题。
- 反射,与上文相同
- 特殊语法,入lamda
- 匿名内部类
- callgraph 生成不完整,师傅没有给出例子,不知道是不是后来tabby指出的未关注污点对参数的影响
- GadgetChain 搜索不完整
0x03 GI其他问题解决
李越、谭添老师们有提到关于Java 静态分析的两大难点:
- Hard Language Feature: Java Reflection
- Hard Language Feature: Native Code
GI对于第一点”反射“在上文中有讲到,是很简单的处理,但是容易造成误报。除此之外,我们还关注GI 在其他通用问题上的处理。
3.1 多态
维护了inheritanceMap.dat,在GadgetGetChain中生成了methodimple.dat 维护接口类的实现关系。在GadgetChain中会对每个接口实现类都进行传播。
3.2 图搜索
3.2.1 Passthrough 过程
在Passthrough中用到逆拓扑图,确保每个节点都访问到,且不形成回环,具体可见上节内容。
注意这里methods.dat 可能存在重复,并不是你拓扑排序造成遗漏。
3.2.1 GadgetChain 过程
在搜索Gadget过程中,通过BFS 广度优先进行搜索,温习下
- source 进入methodsToExplore
- 处理source,查找source 相关的callgraph:source-cmed1,source-cmed2,source-cmed3。判断,source-cmed1非sink
- source-cmed1 add进入methodsToExplore 队尾
- cmed1 进入exploredMethods白名单
- cmed2,cmed3 类似
- 处理source-cmed1,查找cmed1相关的callgraph:cmed1-cmed4, cmed1-cmed5,重复步骤2
这里存在的风险是
- 单Source搜索情况,环形调用才能触发,不确定存不存在这种case
- 多source情况,exploredMethods是共用的,这里很可能存在漏报,不过还没case支撑
0x04 效果
4.1 Ysoserial 覆盖
官网有提到能够检测到的几个case:
- CC1
- Clojure
- scala
- jython
4.2 漏报总结
0x05 总结
0x06 参考
- [1] 高效挖掘反序列化漏洞——GadgetInspector改造(by:su18)
- [2] us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf (by:网飞 Platform Security Team)
- [3] https://github.com/kezibei/Urldns(by:珂字辈)
- [4] java反序列化利用链自动挖掘工具gadgetinspector源码浅析(by:threedr3am)
- [5] Java 反序列化工具 gadgetinspector 初窥(by:Longofo)
- [6] GadgetInspector源码分析(by:fynch3r)
- [7] gadgetinspector源码浅析(by:kingkk)