java web反序列化开坑之旅!
环境搭建
IDEA创建maven
项目:
输入项目名:
继续next:
点击finish完成创建:
在src/main
下创建java
和resources
两个文件夹。并且将resources
文件夹设置为资源文件:
配置pom.xml
文件,加载各个包(commons-collections 3.1存在漏洞):
1 | <properties> |
配置web.xml
文件:
1 |
|
在src/main/webapp/WEB-INF
文件夹下创建views
文件夹以及dispatcher-servlet.xml
文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
配置tomcat
服务器:
部署war
包以及路径:
创建home
控制器:
在views
下创建相应的jsp文件:
访问页面如下,搭建完毕:
漏洞分析
该漏洞的最终触发点在org/apache/commons/collections/functors/InvokerTransformer.java
中的transform
方法:
这里很明显利用java的反射机制执行input
方法。如此一来,按照正常的漏洞挖掘思路,应该在当前包中进行全局搜索该方法,看看哪些地方用到,并且参数可控。
可以在org/apache/commons/collections/functors/ChainedTransformer.java
中看到一个transform
方法,遍历Transformer
,调用每一个Transformer
元素的transform
方法。并且将每一个元素的返回对象传入下一个元素继续进行反射调用,也就是说我们可以利用这一点形成一个反射调用链。这里也用于解决Runtime
实例化对象不允许进行序列化的问题。这时候就可以想到当Transformer
元素为InvokerTransformer
类的对象,那就可以调用InvokerTransformer
类中的transform
方法。至此我们可以继续向上回溯transform
方法,得益于前辈们的分析我们可以直接定位到LazyMap
类。
在LazyMap
类中调用transform
方法,但遗憾的是这里的get
方法并不是类似php中的魔术方法,并不能直接进行调用,因此我们需要继续向上延长我们的pop链。并且这里的factory
并不是我们直接可控的,而是需要通过LazyMap
中的decorate
方法对其进行一个赋值。
在org/apache/commons/collections/keyvalue/TiedMapEntry.java
中存在getValue
方法,在该方法中调用get
方法,而getValue
方法在同类的toString
方法中被调用。java中的toString
方法和php中的__toString
方法相同,都是在对象被当作字符串进行操作时自动调用。顺着这条思路继续,我们接下来就应该寻找TiedMapEntry
类被当作字符串的场景,或者说寻找存在某个对象调用toString
方法。
在javax/management/BadAttributeValueExpException.java
类中可以找到两处toString
方法,一处在其构造方法中,另一处就是我所定位的为止。巧合的是,恰好在重写的readObject
方法中调用了toString
方法。而众所周知,readObject
方法可以用于反序列化的触发。在这里我们符合System.getSecurityManager() == null
这一表达式,这才导致toString
方法的执行。至此完整的反序列化pop链已经呼之欲出。
1 | BadAttributeValueExpException.readObject-> |
在构造具体的POC时需要注意,在BadAttributeValueExpException
类中我们需要控制val
属性。其实不光光时这里,所有的pop链的构造其本质都是面向属性编程,换句话来说我们可以默认为对象属性可控。但是这里的val
属性是私有属性,并不能直接访问赋值,因此我们需要用这种方法来保证属性的可控:
1 | Field valfield = poc.getClass().getDeclaredField("val"); |
完整POC如下:
1 | public class POC { |
最后成功弹出计算器:
针对几个重要的类我这里再借用其他师傅的一些说明:
invokeTransformer
通过反射返回一个对象。
ChainedTransformer
构造一条Transformer链,将指定的transformers连接在一起。
ConstantTransformer
把一个对象转换为一个常量并返回。
首次接触java的反序列化,确实感受到其和php反序列化的不同之处,但也有其异曲同工之妙。初一上手还挺难理解不过在参考了诸多师傅的分析以后加上自己的调试渐渐摸到一些门路。有一点比较坑,就是IDEA会提前帮我们计算参数值,就导致可能还没有执行到具体命令执行的代码就产生代码执行,这里需要多理解一下。
参考链接:
https://p0sec.net/index.php/archives/121/
https://xz.aliyun.com/t/2028#toc-2
https://badcode.cc/2018/03/15/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BCommons-Collections/