Apache Solr反序列化远程代码执行漏洞分析(CVE-2019-0192)

 

0x01 漏洞描述

Solr 是Apache软件基金会开源的搜索引擎框架,其中定义的ConfigAPI允许设置任意的jmx.serviceUrl,它将创建一个新的JMXConnectorServerFactory工厂类实例对象,并通过对目标RMI / LDAP服务器的’bind’操作触发调用。 远程恶意RMI服务器可以响应任意的对象,Solr端使用java中ObjectInputStream类将接收到的对象进行不安全的反序列化过程。使用ysoserial工具可以利用此类漏洞,根据目标Classpath环境,攻击者可以使用其中特定的“gadget chains”在Solr端触发远程代码执行。

风险等级:High

影响版本:5.0.0 to 5.5.5      6.0.0 to 6.6.5

 

0x02 漏洞分析

Solr初始化及启动过程:研究solr启动过程,首先需要从War包中加载的配置文件web.xml入手,其中所有的访问请求统一路由到SolrDispatchFilter类中处理,其主要的作用包括加载solr配置文件、初始化各个core、初始化各个requestHandler和component:

查看了该类的继承结构:

BaseSolrFilter,是一个实现Filter接口的抽象类,功能很简单,就是判断当前程序是否开启正常的日志记录功能:

作为具体类的SolrDispatchFilter必须实现Filter接口中定义的如下三个抽象方法:

其中doFilter方法拦截所有的http请求,下面跟进下SolrDispatchFilter类中的doFilter具体方法:

首先定位到用于处理请求数据的接口SolrRequestHandler,其中定义的抽象方法handleRequest中分别传入了用于处理请求的接口SolrQueryRequest和处理响应的接口SolrQueryResponse,在IDEA中查看接口的实现类,发现只有抽象类RequestHandlerBase实现了上述接口:

跟进抽象类RequestHandlerBase,其重写了抽象方法handleRequest,并在其中调用了handleRequestBody抽象方法:

为进一步了解handleRequestBody方法的实现过程,根据继承关系图发现,SolrConfigHandler具体类继承了抽象类RequestHandlerBase:

进一步跟进SolrConfigHandler具体类,发现其重写了上述的handleRequestBody抽象方法,并在方法体中首先判断HTTP请求参数是否为POST,若一致则调用command.handlePOST()方法:

根据公开POC表明漏洞利用过程是通过POST方式触发,则进一步跟进command.handlePOST()方法:

跟进其调用的handleCommands(opsCopy, overlay)方法,其中使用switch函数对传入的方法名做了选择判断,SET_PROPERTY常量字符串对应于set-property方法:

由于公开POC显示在构造恶意Json数据时使用了set-property属性:

所以继续跟进上图case分支中的applySetProp(op, overlay)方法:

跟进setProperty方法,方法体中将POST参数转换后,调用有参类型的ConfigOverlay构方法生成新的ConfigOverlay对象,方法调用结束后通过return函数将其返回到上文调用方法handleCommands的overlay变量中:

上文handleCommands方法在接收到正常的overlay返回值后,调用break子句结束了for循环遍历操作符过程,继续向下执行:

Solr资源加载器通过当前请求对象的getCore().getResourceLoader()方法创建了新的加载器对象loader。通过if条件判断loader是否为zookeeper分布式集群ZkSolrResourceLoader类的实例对象。受本地环境限制,此时会执行else代码块中的单机模式。继续跟进persistConfLocally(loader, ConfigOverlay.RESOURCE_NAME, overlay.toByteArray())方法,其中传入的实参RESOURCE_NAME是如下常量字符串:

调用File类的构造函数创建confFile实例对象,并通过OutputStream输出流中的write方法将configoverlay.json文件内容在磁盘中做持久化存储:

反向追踪RESOURCE_NAME常量的调用过程,发现SolrConfig类加载了该配置文件:

为了清晰的了解Solr容器的启动过程,需要从org/apache/solr/core/CoreContainer类中定义的reload(String name)方法入手:

其中传递的参数String name是需要重新加载的SolrCore,当重新加载时,其所属的配置文件也将从新加载,继续跟进getConfig方法:

其中调用的createSolrConfig方法用于重新加载配置文件,跟进SolrConfig类结构,发现其定义了重载的构造方法:

当非初始化时,调用getOverlay()方法:

跟进getConfigOverlay方法,其资源加载器确实加载了上文中定义的配置文件,并和上述的方法调用过程相吻合:

继续执行构造函数,通过JmxConfiguration构造函数接收用户传递参数并赋值给jmxConfig对象:

逆向追踪jmxConfig调用过程:

发现SolrCore中通过if条件对是否开启JMX做了判断,若为false,则通过JmxMonitoredMap构造方法返回一个新的对象:

跟进JmxMonitoredMap有参构造函数,其中通过JMXConnectorServerFactory工厂类创建了JMXConnectorServer类的实例对象并调用start()方法向远程RMI服务器发送请求:

当获取远程RMI服务器恶意序列化对象后的反序列化调用链可以参考ysoserial的7u21的漏洞利用过程。

 

0x03 漏洞利用

  1. 靶机中开启Solr服务,并监听本地8983端口:

  1. 攻击机使用ysoserial攻击创建RMI服务并监听1099端口:

java -cp ysoserial-master-ff59523eb6-1.jar ysoserial.exploit.JRMPListener 1099 Jdk7u “touch /tmp/pwn.txt”

  1. 攻击机发送POC请求,Solr端返回了500状态码:

  1. 攻击机监听端口收到回连请求,并成功在远程靶机上执行系统命令创建文件:

 

0x04 修复方案

* 升级到Apache Solr 7.0或更高版本。

* 通过设置系统属性值 “disable.configEdit=true”禁用未被使用的ConfigAPI。

* 若上述升级或禁用Config API不是可行的解决方案,请下载安全公告[1]中的补丁SOLR-13301.patch并重新编译Solr,在新发布的补丁中发现已经禁用了JMX中的serviceUrl属性:

* 确保配置了正确的网络访问控制策略设置,只允许受信任的流量进入或退出运行Solr服务的主机。

 

0x05 Reference

[1] https://issues.apache.org/jira/browse/SOLR-13301

[2] https://wiki.apache.org/solr/SolrSecurity

[3] http://mail-archives.us.apache.org/mod_mbox/www-announce/201903.mbox/%3CCAECwjAV1buZwg%2BMcV9EAQ19MeAWztPVJYD4zGK8kQdADFYij1w%40mail.gmail.com%3E

[4] https://lucene.apache.org/solr/guide/6_6/config-api.html#ConfigAPI-CommandsforCommonProperties

[5] https://github.com/mpgn/CVE-2019-0192

(完)