0x01 漏洞描述
Apache dubbo是一个是基于Java的高性能开源RPC框架。它支持dubbo,http,rmi,hessian等协议。本次问题出现在dubbo开启http协议后,会将消费者提交的request
请求,在无安全校验的情况下直接交给了spring-web.jar
进行处理,最终request.getInputStream()
被反序列化,故存在反序列化漏洞。下面我们来调试分析代码。
0x02 影响范围
- 2.7.0 <= Apache Dubbo <= 2.7.4
- 2.6.0 <= Apache Dubbo <= 2.6.7
- Apache Dubbo = 2.5.x
0x03 环境搭建
- OS: Mac OSX
- JDK: 1.8.0_191
- Dubbo: 2.7.3
环境搭建这里我选择官方的samples
中的dubbo-samples-http
https://github.com/apache/dubbo-samples/
源码下载后将pom.xml
中指定的dubbo版本修改为2.7.3
,同时加入commons-collections4-4.0.jar
方便测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <properties> <source.level>1.8</source.level> <target.level>1.8</target.level> <dubbo.version>2.7.3</dubbo.version> <spring.version>4.3.16.RELEASE</spring.version> <junit.version>4.12</junit.version> ... <dependencies> ... <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> </dependencies>
|
同时dubbo依赖zookeeper,请自行安装!
0x04 漏洞分析
dubbo启用http协议后,所有的请求都会通过org.apache.dubbo.rpc.protocol.http.HttpProtocol$InternalHandler
类的handle
方法进行处理。我们在这打断点,并发送poc开始跟踪分析。
首先handle
方法会获取请求路径,然后通过这个路径去skeletonMap
里获取到该接口对应的处理对象,来处理当前request
请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String uri = request.getRequestURI(); HttpInvokerServiceExporter skeleton = (HttpInvokerServiceExporter)HttpProtocol.this.skeletonMap.get(uri); if (!request.getMethod().equalsIgnoreCase("POST")) { response.setStatus(500); } else { RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); try { skeleton.handleRequest(request, response); } catch (Throwable var6) { throw new ServletException(var6); } } }
|
第二步中存储接口地址和处理对象的skeletonMap
处理对象是HttpInvokerServiceExporter
类对象,它负责获取远程调用对象,并执行获取结果返回给客户端。跟进它的handleRequest
方法,request
对象被传入readRemoteInvocation
方法中来获取RemoteInvocation
远程调用对象
1 2 3 4 5 6 7 8 9 10 11
| public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { RemoteInvocation invocation = this.readRemoteInvocation(request); RemoteInvocationResult result = this.invokeAndCreateResult(invocation, this.getProxy()); this.writeRemoteInvocationResult(request, response, result); } catch (ClassNotFoundException var5) { throw new NestedServletException("Class not found during deserialization", var5); } }
|
readRemoteInvocation
方法将request.getInputStream()
(我们提交的序列化内容)传入createObjectInputStream
方法,封装为一个ObjectInputStream
。该对象又被传入doReadRemoteInvocation
方法中,进行最终的获取操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException { return this.readRemoteInvocation(request, request.getInputStream()); }
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is) throws IOException, ClassNotFoundException { ObjectInputStream ois = this.createObjectInputStream(this.decorateInputStream(request, is));
RemoteInvocation var4; try { var4 = this.doReadRemoteInvocation(ois); } finally { ois.close(); } return var4; }
|
在doReadRemoteInvocation
方法中,ObjectInputStream
类对象ois
直接被反序列化了。这个过程中没有进行任何过滤,导致我们传入的恶意序列化对象可以被反序列化创建,漏洞触发!
1 2 3 4 5 6 7 8 9 10
| protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) throws IOException, ClassNotFoundException { Object obj = ois.readObject(); if (!(obj instanceof RemoteInvocation)) { throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocation.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj)); } else { return (RemoteInvocation)obj; } }
|
0x05 漏洞修复
漏洞出现的原因dubbo
HTTP接口将携带有恶意反序列化数据的request
,在无安全校验的情况下直接交给了spring-web.jar
的HttpInvokerServiceExporter
进行处理,导致存在反序列化漏洞。按理说这个漏洞不仅仅只是dubbo
自身的问题,还是spring
的问题。
在2.7.4.1版本开始,dubbo处理HTTP接口的调用请求交给了jsonrpc4j.jar
的JsonRpcServer
去处理了。
跟踪分析JsonRpcServer
类的handle
方法后,request.getInputStream()
没有再被反序列化了。所以原来的利用方法失效了。
0x06 漏洞总结
该漏洞利用虽然简单粗暴,但在黑盒情况下利用难点有两个,一是我们无法得知web服务是否是dubbo http接口。二是如何获取接口路径,该路径可以在服务器上的zookeeper
和http-provider
配置文件中找到,如果不配合其他漏洞是不容易获取的。
0x07 参考文章
文章来源: https://gv7.me/articles/2020/cve-2019-17564-dubbo-http-deserialization-vulnerability/
如有侵权请联系:admin#unsafe.sh