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/