前言
1 | 最近审计遇到了一个环境存在反序列化漏洞但是没有利用链,于是尝试挖了下利用链,虽然最后没挖到,但是也对利用链挖掘的思路有了一些了解,在我挖掘的过程中也找了下文章,这方面的文章确实比较少,因此打算写篇文章介绍如何挖掘反序列化利用链,希望能对后来者提供一些帮助。 |
确定sink
因为我也是第一次接触利用链的挖掘,刚上手也是感觉到无所适从,最好的方法当然是分析已有的利用链,下面是我总结了常见利用链的sink。
1 | AspectJWeaver:FileOutputStream#write |
下面我们对sink
进行归类
- 触发FileOuputStream#write写文件操作:实际上比较少,一般也不知道网站路径,所以相对来说也比较鸡肋。
- 触发JNDI请求:主要是触发
javax.naming.Context#lookup
实现类的lookup
方法,一般在程序中使用JNDI相对来说也比较少,因此只有少数的框架有这种利用链。 - 触发Method#invoke调用:一般挖掘反序列化利用链都是基础库,多多少少都会使用反射,但是并不是所有的反射都可以利用的。一般来说要么我们可以通过
控制参数
或者属性
才能完成调用。这里要注意,如果是通过控制参数来完成反射调用,那么这个实现类是不需要实现Serializable
接口的。但是如果我们要通过控制属性来完成任意方法的调用,则实现类必须要实现Serializable
接口,除非属性的填充是在反序列化的过程中完成的,比如可以通过构造方法填充属性,在反序列化的过程中会调用构造方法帮我们完成赋值。
通过上面的总结不难看出大部分的利用链都是以Method#invoke
为终点的,同样以Method#invoke
作为sink,我觉得其实还可以再进行一些细化。
- 通过控制属性或者方法传参调用
invoke
任意方法:CC1 - 通过控制属性或方法传参
invoke
调用任意类的的Getter
方法:beanutils、Hibernate - 还有一种就比较复杂,虽然本质上也是调用了
invoke
,但是这些框架本身提供了一些代码执行的能力,还要对框架的代码执行的原理比较清楚才能挖到调用链:BeanShell1
、Groovy1
、Click1
、Jpython
- 在
InvocationHandler
的实现类中的Invoke
:这里的invoke
操作的method
需要是我们可以从属性中获取的,而不是直接传入的方法名称。具体可以参考JDK7
那条利用链。
失败的实践
InvocationHandler
InvocationHandler
实现类要作为反序列化链中的sink的条件是需要实现Serializable
,原因是我们这里想要通过控制属性来获取Method
。
举个栗子,在jdk中有个java.beans.EventHandler
,它的Method的获取是通过target
和action
属性控制的,所以我们只要通过控制这两个属性就可以实现利用。
但是由于EventHandler
没有实现Serializable
接口,因此非常遗憾的不能作为我们的利用链。
下面是tabby的挖掘语法,排查了下大部分都没有实现Serializable
接口
1 | match (m1:Method)<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*] |
JNDI
下面我们寻找JNDI的sink
- javax.naming.Context#lookup
- java.naming.InitialContext#lookup
- javax.naming.Context#bind
1 | match (m2:Method)<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*] |
调用JNDI操作的本来就比较少,基本上都不可控或者找不到调用,因此JNDI的利用也只能放弃。
Invoke
Invoke的调用比较多,可以直接通过下面的语法挖调用链,然后一步步排除加黑名单排除。
1 | match (source:Method) where source.NAME in ["compare","compareTo","hashCode"] |
也可以先通过下面的语法找到合适的sink再去看调用。
1 | match (m1:Method)<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*] |
下面是几个失败的调用链挖掘。
JFinalJson#beanToJson
直接控制model为TemplatesImpl对象就可以完成调用。
下面我们找beanToJson
的调用
最终定位到com.jfinal.json.JFinalJson#toJson
,但是JFinalJson
不可序列化,所以要么找到可以在反序列化过程中实例化JfinalJson
的点,要么就不可以利用了。
GetterMethodFieldGetter#get
target
可以通过参数直接控制但是method
需要通过属性控制,所以需要找调用链中会不会初始化GetterMethodFiledGetter
继续看上层调用,只有在com.jfinal.template.expr.ast.Field#eval
中调用了。而target也是由eval的返回结果决定的。
再通过tabby找调用,分析了一大圈,感觉好像不可控。
ReflectUtil#invoke
可以直接通过控制传参调用任意方法,所以不需要实现Serializable
只有execute和clone两个点,但是clone不能控制函数名称。execute似乎是计划任务,也不太好调用
1 | match (source:Method) where source.NAME in ["compare","compareTo","hashCode"] |
cn.hutool.core.bean.DynaBean
实现了Serializable
接口,并且可以反射调用Getter
方法。
我们构造一个TemplatesImpl
进行测试发现获取不到outputProperties
的getter方法。
经过调试发现获取getter方法时没有去掉_所以匹配不到,这个点真是最可惜的点了。
总结
找source的过程就不讲了,因为我连合适的sink都没找到。