def smart_fill_file(var_name, file_name): """ This function will return a NamedStringIO, ready to use in multipart forms. The contents of the file and its extension are carefully chosen to try to go through any form filters the web application might be implementing. """ extension = guess_extension(var_name, file_name) _, file_content, file_name = get_file_from_template(extension) # I have to create the NamedStringIO with a "name", # required for MultipartContainer to properly encode this as multipart/post return NamedStringIO(file_content, name=file_name)
def smart_fill_file(var_name, file_name): """ This function will return a NamedStringIO, ready to use in multipart forms. The contents of the file and its extension are carefully chosen to try to go through any form filters the web application might be implementing. """ extension = guess_extension(var_name, file_name) _, file_content, file_name = get_file_from_template(extension) # I have to create the NamedStringIO with a "name", # required for MultipartContainer to properly encode this as multipart/post return NamedStringIO(file_content, name=file_name)
def mutant_smart_fill(freq, dc_copy, ignore_pname, ignore_index, fuzzer_config): """ :param freq: The fuzzable request (original request instance) we're fuzzing :param ignore_pname: A parameter name to ignore :param ignore_index: The index we want to ignore :return: A data container that has been filled using smart_fill, ignoring the parameters that I'm fuzzing and filling the file inputs with valid image file. """ for var_name_dc in dc_copy: for element_index_dc, element_value_dc in enumerate( dc_copy[var_name_dc]): if (var_name_dc, element_index_dc) == (ignore_pname, ignore_index): continue if dc_copy.get_type(var_name_dc) in AVOID_FILLING_FORM_TYPES: continue # Fill only if the parameter does NOT have a value set. # # The reason of having this already set would be that the form # has something like this: # # <input type="text" name="p" value="foobar"> # if dc_copy[var_name_dc][element_index_dc] == '': # # Fill it smartly # dc_copy[var_name_dc][element_index_dc] = smart_fill( var_name_dc) # Please see the comment above (search for __HERE__) for an explanation # of what we are doing here: for var_name in freq.get_file_vars(): # Try to upload a valid file extension = fuzzer_config.get('fuzz_form_files') or 'gif' success, file_content, file_name = get_file_from_template(extension) # I have to create the NamedStringIO with a "name", # required for MultipartPostHandler str_file = NamedStringIO(file_content, name=file_name) # TODO: Is this hard-coded [0] enough? dc_copy[var_name][0] = str_file return dc_copy
def mutant_smart_fill(freq, dc_copy, ignore_pname, ignore_index, fuzzer_config): """ :param freq: The fuzzable request (original request instance) we're fuzzing :param ignore_pname: A parameter name to ignore :param ignore_index: The index we want to ignore :return: A data container that has been filled using smart_fill, ignoring the parameters that I'm fuzzing and filling the file inputs with valid image file. """ for var_name_dc in dc_copy: for element_index_dc, element_value_dc in enumerate(dc_copy[var_name_dc]): if (var_name_dc, element_index_dc) == (ignore_pname, ignore_index): continue if dc_copy.get_type(var_name_dc) in AVOID_FILLING_FORM_TYPES: continue # Fill only if the parameter does NOT have a value set. # # The reason of having this already set would be that the form # has something like this: # # <input type="text" name="p" value="foobar"> # if dc_copy[var_name_dc][element_index_dc] == '': # # Fill it smartly # dc_copy[var_name_dc][element_index_dc] = smart_fill(var_name_dc) # Please see the comment above (search for __HERE__) for an explanation # of what we are doing here: for var_name in freq.get_file_vars(): # Try to upload a valid file extension = fuzzer_config.get('fuzz_form_files') or 'gif' success, file_content, file_name = get_file_from_template(extension) # I have to create the NamedStringIO with a "name", # required for MultipartPostHandler str_file = NamedStringIO(file_content, name=file_name) # TODO: Is this hard-coded [0] enough? dc_copy[var_name][0] = str_file return dc_copy
def audit(self, freq, orig_response): """ Searches for file upload vulns. :param freq: A FuzzableRequest """ if freq.get_method().upper() != "POST" or not freq.get_file_vars(): return for file_parameter in freq.get_file_vars(): for extension in self._extensions: _, file_content, file_name = get_file_from_template(extension) # Only file handlers are passed to the create_mutants functions named_stringio = NamedStringIO(file_content, file_name) mutants = create_mutants(freq, [named_stringio], fuzzable_param_list=[file_parameter]) for mutant in mutants: mutant.uploaded_file_name = file_name self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def audit(self, freq, orig_response): """ Searches for file upload vulns. :param freq: A FuzzableRequest """ if freq.get_method().upper() != 'POST' or not freq.get_file_vars(): return for file_parameter in freq.get_file_vars(): for extension in self._extensions: _, file_content, file_name = get_file_from_template(extension) # Only file handlers are passed to the create_mutants functions named_stringio = NamedStringIO(file_content, file_name) mutants = create_mutants(freq, [named_stringio], fuzzable_param_list=[file_parameter]) for mutant in mutants: mutant.uploaded_file_name = file_name self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_result)
def test_mutant_creation_post_data(self): form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username"), ("value", "")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form_params.add_field_by_attr_items([("name", "image"), ("type", "file")]) form = MultipartContainer(form_params) freq = FuzzableRequest(self.url, post_data=form) ph = 'w3af.core.data.constants.file_templates.file_templates.rand_alpha' with patch(ph) as mock_rand_alpha: mock_rand_alpha.return_value = 'upload' generated_mutants = PostDataMutant.create_mutants(freq, self.payloads, [], False, self.fuzzer_config) self.assertEqual(len(generated_mutants), 6, generated_mutants) _, gif_file_content, _ = get_file_from_template('gif') gif_named_stringio = NamedStringIO(gif_file_content, 'upload.gif') expected_forms = [] form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['def'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['abc'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) # TODO: Please note that these two multipart forms are a bug, since # they should never be created by PostDataMutant.create_mutants # (they are not setting the image as a file, just as a string) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = ['def'] form['username'] = ['John8212'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = ['abc'] form['username'] = ['John8212'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) # # TODO: /end # form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['John8212'] form['address'] = ['abc'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['John8212'] form['address'] = ['def'] expected_forms.append(form) boundary = get_boundary() noop = '1' * len(boundary) expected_data = [encode_as_multipart(f, boundary) for f in expected_forms] expected_data = set([s.replace(boundary, noop) for s in expected_data]) generated_forms = [m.get_dc() for m in generated_mutants] generated_data = [str(f).replace(f.boundary, noop) for f in generated_forms] self.assertEqual(expected_data, set(generated_data)) str_file = generated_forms[0]['image'][0] self.assertIsInstance(str_file, NamedStringIO) self.assertEqual(str_file.name[-4:], '.gif') self.assertEqual(gif_file_content, str_file) str_file = generated_forms[1]['image'][0] self.assertIsInstance(str_file, NamedStringIO) self.assertEqual(str_file.name[-4:], '.gif') self.assertEqual(gif_file_content, str_file) self.assertIn('name="image"; filename="upload.gif"', generated_data[0])
def test_get_file_from_template_false(self): success, file_content, file_name = get_file_from_template('swf') self.assertFalse(success) self.assertTrue(file_name.endswith('.swf'), file_name)
def test_get_file_from_template_true(self): success, file_content, file_name = get_file_from_template('gif') self.assertTrue(success) self.assertIn('GIF', file_content) self.assertTrue(file_name.endswith('.gif'), file_name)
def test_mutant_creation_post_data(self): form_params = FormParameters() form_params.add_field_by_attr_items([("name", "username"), ("value", "")]) form_params.add_field_by_attr_items([("name", "address"), ("value", "")]) form_params.add_field_by_attr_items([("name", "image"), ("type", "file")]) form = MultipartContainer(form_params) freq = FuzzableRequest(self.url, post_data=form) ph = 'w3af.core.data.constants.file_templates.file_templates.rand_alpha' with patch(ph) as mock_rand_alpha: mock_rand_alpha.return_value = 'upload' generated_mutants = PostDataMutant.create_mutants(freq, self.payloads, [], False, self.fuzzer_config) self.assertEqual(len(generated_mutants), 6, generated_mutants) _, gif_file_content, _ = get_file_from_template('gif') gif_named_stringio = NamedStringIO(gif_file_content, 'upload.gif') expected_forms = [] form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['def'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['abc'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) # TODO: Please note that these two multipart forms are a bug, since # they should never be created by PostDataMutant.create_mutants # (they are not setting the image as a file, just as a string) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = ['def'] form['username'] = ['John8212'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = ['abc'] form['username'] = ['John8212'] form['address'] = ['Bonsai Street 123'] expected_forms.append(form) # # TODO: /end # form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['John8212'] form['address'] = ['abc'] expected_forms.append(form) form = MultipartContainer(copy.deepcopy(form_params)) form['image'] = [gif_named_stringio] form['username'] = ['John8212'] form['address'] = ['def'] expected_forms.append(form) boundary = get_boundary() noop = '1' * len(boundary) expected_data = [encode_as_multipart(f, boundary) for f in expected_forms] expected_data = set([s.replace(boundary, noop) for s in expected_data]) generated_forms = [m.get_dc() for m in generated_mutants] generated_data = [str(f).replace(f.boundary, noop) for f in generated_forms] self.assertEqual(expected_data, set(generated_data)) str_file = generated_forms[0]['image'][0] self.assertIsInstance(str_file, NamedStringIO) self.assertEqual(str_file.name[-4:], '.gif') self.assertEqual(gif_file_content, str_file) str_file = generated_forms[1]['image'][0] self.assertIsInstance(str_file, NamedStringIO) self.assertEqual(str_file.name[-4:], '.gif') self.assertEqual(gif_file_content, str_file) self.assertIn('name="image"; filename="upload.gif"', generated_data[0])