漏洞详情:https://issues.apache.org/jira/browse/SOLR-13301
漏洞POC:https://github.com/mpgn/CVE-2019-0192
影响版本:5.0.0 ~ 5.5.5、6.0.0 ~ 6.6.5
安全版本:7.0
简要描述
ConfigAPI allows to configure Solr's JMX server via an HTTP POST request.By pointing it to a malicious RMI server, an attacker could take advantage of Solr's unsafe deserialization to trigger remote code execution on the Solr side.
Solr 中由配置文件触发 Jmx流程:
org.apache.solr.handler.SolrConfigHandler#handleRequestBody
->
org.apache.solr.handler.SolrConfigHandler.Command#handlePOST
->
org.apache.solr.handler.SolrConfigHandler.Command#applySetProp
->
org.apache.solr.core.CoreContainer#reload
->
org.apache.solr.core.SolrConfig#SolrConfig
->
org.apache.solr.core.SolrCore#reload
->
org.apache.solr.core.SolrCore#SolrCore
->
org.apache.solr.core.SolrCore#initInfoRegistry
->
org.apache.solr.core.JmxMonitoredMap#JmxMonitoredMap
->
javax.management.remote.JMXConnectorServerMBean#start
其路由过程不再分析,在 https://xz.aliyun.com/t/1523 这篇文章中分析过
直接到 ReuqestHandler 中,访问路径 /config 可以进入 SolrConfigHandler#handleRequestBody 中,如下图:
由poc知道我们走的是 POST 的处理流程,跟进 handlePOST 函数
如上图看见关键词 Config Overlay ,跟进 else 代码块调用的 handleCommands 函数,如下
第一个框就是 poc 给出的 POST 数据中指定的 set-property 流程,但是我感觉第二个框也可以,不过具体情况没测试
跟入 applySetProp 函数,如下
private ConfigOverlay applySetProp(CommandOperation op, ConfigOverlay overlay) { Mapm = op.getDataMap(); if (op.hasError()) return overlay; for (Map.Entry e : m.entrySet()) { String name = e.getKey(); Object val = e.getValue(); Class typ = ConfigOverlay.checkEditable(name, false, null); [……中间省略了解析 POST 数据的流程……] overlay = overlay.setProperty(name, val); } return overlay; } }
如上,注意力转向 setProperty 的调用,跟进
这里就是将 POST 参数做处理后,全部用于生成一个新的 ConfigOverlay 对象并将其返回到 handleCommands 函数中的 overlay 变量去接受赋值
标注 handleCommands 中的 overlay 变量,看到如下调用
If 代码块是处于 zookeeper 分布式状态下的,也能触发成功,不过为了便于分析,我们跟进 else 代码块:单机孤儿模式
可以看见调用了 persistConfLocally 猜测应该是将刚刚生成的 ConfigOverlay 对象的数据做持久存储,其中 ConfigOverlay.RESOURCE_NAME 的值为:
public static final String RESOURCE_NAME = "configoverlay.json";
数据应该是存入了 configoverlay.json 中
第二步就调用了 reload 函数,重启服务器
其实这里我是反向跟踪的,因为 reload 流程中会有大量的 initial 操作,根本分不清啥时候会加载我们指定的配置,从 ConfigOverlay.RESOURCE_NAME 入手
这里有个获取流数据操作,八九不离十就是这里,但是为了调用流程直观,我们还是从 reload 流程中讲述
注意到了这一句,重新加载的话,所有配置文件也会重新加载一次,既然之前写入了 configoverlay.json 文件中,那也算作是配置文件,跟进 getConfig 函数
很显眼 createSolrConfig 函数流程肯定是重加载配置,一直跟踪到 SolrConfig#SolrConfig 中,如下:
看见 overlay 了,跟进到 getConfigOverlay 函数如下
这里就刚好和前面搜索到的流数据操作吻合,那么继续在 SolrConfig 的构造函数中查看,找到如下
上图中看见了 poc 中指定的三个参数,并且用他们创建了 JmxConfiguration 对象赋值给 jmxConfig
这里还没完,JmxConfiguration 仅仅是存储信息的,并没有执行任何操作,跟踪 jmxConfig 参数如下
跟入红框所示位置
JmxMonitoredMap 的构造函数中就进行了 JMX 监控操作,可以触发 rmi 序列化
但是这里属于 initInfoRegistry 函数中,还不清楚这个函数在哪儿调用,反向查找在 SolrCore 的构造函数中被调用到
SolrCore 的构造函数由 CoreContainer#reload 调用的 SolrCore#reload 触发
至此服务端触发jmx流程已经完整
漏洞详情:https://issues.apache.org/jira/browse/SOLR-13301
漏洞POC:https://github.com/mpgn/CVE-2019-0192
https://issues.apache.org/jira/secure/attachment/12961503/12961503_SOLR-13301.patch
https://lucene.apache.org/solr/guide/6_6/config-api.html#ConfigAPI-CommandsforCommonProperties