Dubbo反序列化(CVE-2020-1948)绕过之二
最近的Dubbo漏洞
一, 根据https://www.mail-archive.com/[email protected]/msg06544.html 最近的Dubbo反序列化漏洞CVE-2020-1948
作者提供的poc是
resp = client.send_request_and_return_response(
service_name='org.apache.dubbo.spring.boot.demo.consumer.DemoService'
method_name='rce',
args=[toStringBean])
是将恶意对象放到args
这个数组的一个元素中。
二,最近两天预警的dubbo反序列化漏洞中提到绕过2.7.7版本的点是将method_name
换成$invoke
、$invokeAsync
和$echo
。但是我没有复现成功。
三,于是参考作者rui0的文章,尝试其他的利用方式。参考:
http://rui0.cn/archives/1338
其评论中提供的思路,
这里需要注意一下,因为最近的Dubbo反序列化漏洞公布了。所以需要补充一下,因为主题的关系文章中只提到了Dubbo的toString触发点,但是Dubbo实际上在刚传入序列化值时也有一个触发点,漏洞公布的邮件里并没有涉及全部的细节以及poc,所以一些版本如果在代码层面只去掉输出arguments处理,仍然还有其他触发位置可能存在风险。建议各位开发者尽量采取升级措施,或在代码层面去掉arguments的输出同时对Hessian进行加固。
发现除了args
属性之外,其他属性比如service_name
、method_name
、service_version
都可作为利用点。
漏洞原理
new Exception("test" + obj)时,会对字符串进行拼接(StringBuilder#append
), 为了拿到obj的值,会使用
String.valueOf(obj)
=>obj.toString()
去取。需要找到某个类其重写的toString方法可以rce,而com.rometools.rome.feed.impl.ToStringBean
就是这样的类。因为在其重写的toString方法中(=>toString(String prefix)) 会调用指定类对象所有属性的get方法。
com.rometools.rome.feed.impl.ToStringBean#toString
这里是通过构造com.sun.rowset.JdbcRowSetImpl
对象,调用其getDatabaseMetaData
方法,这样会调用com.sun.rowset.JdbcRowSetImpl#connect
方法,加上poc中设置的其dataSource属性(jndi的url),实现rce。
这种利用方式的调用栈为(以service_version
为例):
lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
valueOf:2994, String (java.lang)
append:131, StringBuilder (java.lang)
expect:3564, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readString:1883, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readUTF:88, Hessian2ObjectInput (org.apache.dubbo.common.serialize.hessian2)
decode:109, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:80, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:57, DecodeHandler (org.apache.dubbo.remoting.transport)
received:44, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
而version < 2.7.7的调用栈为:(以2.7.1为例)
toString:105, ToStringBean (com.rometools.rome.feed.impl)
valueOf:2994, String (java.lang)
toString:4571, Arrays (java.util)
toString:211, RpcInvocation (org.apache.dubbo.rpc)
valueOf:2994, String (java.lang)
append:131, StringBuilder (java.lang)
getInvoker:248, DubboProtocol (org.apache.dubbo.rpc.protocol.dubbo)
reply:102, DubboProtocol$1 (org.apache.dubbo.rpc.protocol.dubbo)
handleRequest:103, HeaderExchangeHandler (org.apache.dubbo.remoting.exchange.support.header)
received:200, HeaderExchangeHandler (org.apache.dubbo.remoting.exchange.support.header)
received:51, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
可以从下面这个方法看出两个触发点的不同(以2.7.1版本为例):org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation#decode(Channel channel, InputStream input)
可以看出公开的poc触发的点是在读取arguments的时候(对应poc中args
属性)
而绕过方式的触发点是在读取service_name
、method_name
、service_version
时触发,即
org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectInput#readUTF
在2.7.7修复的地方之前就已经触发了。
而其模型都是:
new Exception("string" + obj)
具体地, version < 2.7.7是在:org\apache\dubbo\dubbo\2.7.1\dubbo-2.7.1.jar!\org\apache\dubbo\rpc\protocol\dubbo\DubboProtocol#getInvoker(Channel channel, Invocation inv)的这行代码
// inv为DecodeableRpcInvocation类的恶意对象
throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + this.exporterMap.keySet() + ", may be version or group mismatch , channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);
version = 2.7.7版本是在:
// obj为DecodeableRpcInvocation类的恶意对象
this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " " + obj.getClass().getName() + " (" + obj + ")")
protected IOException error(String message) {
return this._method != null ? new HessianProtocolException(this._method + ": " + message) : new HessianProtocolException(message);
}
Demo
tips
为了避免在调试反序列化漏洞toString方法的时候多次触发计算器,或者误触发了本不会触发的计算器,可以在IDEA中进行设置: 参考:http://rui0.cn/archives/1338
telnet服务结合fastjson实现RCE
其解析字符串默认使用的是fastjson,可以结合fastjson实现RCE。
关键代码在:
org\apache\dubbo\dubbo\2.7.7\dubbo-2.7.7.jar!\org\apache\dubbo\qos\legacy\InvokeTelnetHandler#telnet(Channel channel, String message)
调用栈为:
telnet:81, InvokeTelnetHandler (org.apache.dubbo.qos.legacy)
telnet:59, TelnetHandlerAdapter (org.apache.dubbo.remoting.telnet.support)
received:187, HeaderExchangeHandler (org.apache.dubbo.remoting.exchange.support.header)
received:51, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
Demo
参考:
https://cloud.tencent.com/developer/article/1587681
http://dubbo.apache.org/zh-cn/docs/user/references/telnet.html