Exemple #1
0
 def _send_requests( self, fuzzableRequest ):
     '''
     Actually send the requests that might be blocked.
     @parameter fuzzableRequest: The fuzzableRequest to modify in order to see if it's blocked
     '''
     rnd_param = createRandAlNum(7)
     rnd_value = createRandAlNum(7)
     original_url_str = fuzzableRequest.getURL() + '?' + rnd_param + '=' + rnd_value
     original_url = url_object(original_url_str)
     
     try:
         original_response_body = self._urlOpener.GET( original_url , useCache=True ).getBody()
     except Exception:
         msg = 'Active filter detection plugin failed to receive a '
         msg += 'response for the first request. Can not perform analysis.'
         raise w3afException( msg )
     else:
         original_response_body = original_response_body.replace( rnd_param, '' )
         original_response_body = original_response_body.replace( rnd_value, '' )
         
         for offending_string in self._get_offending_strings():
             offending_URL = fuzzableRequest.getURL() + '?' + rnd_param + '=' + offending_string
             
             # Perform requests in different threads
             targs = (offending_string, offending_URL, original_response_body, rnd_param)
             self._tm.startFunction( target=self._send_and_analyze, args=targs, ownerObj=self )
         
         # Wait for threads to finish
         self._tm.join( self )
         
         # Analyze the results
         return self._filtered, self._not_filtered
def getRemoteTempFile( execMethod ):
    '''
    @return: The name of a file in the remote file system that the user that I'm executing commands with
    can write, read and execute. The normal responses for this are files in /tmp/ or %TEMP% depending
    on the remote OS.
    '''
    os = osDetectionExec( execMethod )
    if  os == 'windows':
        _filename = apply( execMethod, ('echo %TEMP%',) ).strip() + '\\'
        _filename += createRandAlNum(6)
        
        # verify existance
        dirRes = apply( execMethod, ('dir '+_filename,) ).strip().lower()
        if 'not found' in dirRes:
            # Shit, the file exists, run again and see what we can do
            return getRemoteTempFile( execMethod )
        else:
            return _filename
        return _filename
        
        
    elif os == 'linux':
        _filename = '/tmp/' + createRandAlNum( 6 )
        
        # verify existance
        lsRes = apply( execMethod, ('ls '+_filename,) ).strip()
        if _filename == lsRes:
            # Shit, the file exists, run again and see what we can do
            return getRemoteTempFile( execMethod )
        else:
            return _filename
    else:
        raise w3afException('Failed to create filename for a temporary file in the remote host.')
Exemple #3
0
    def _get_allowed_chars(self, mutant):
        """
        These are the special characters that are tested:
            ['<', '>', '"', "'", '(', ')']
        
        Notice that this doesn't work if the filter also filters
        by length. The idea of this method is to reduce the amount of
        tests to be performed, if each char is tested separately the
        wanted performance enhancement will be lost.
        
        @return: A list with the special characters that are allowed
            by the XSS filter
        """
        # Create a random number and assign it to the mutant
        # modified parameter
        rndNum = str(createRandAlNum(2))
        oldValue = mutant.getModValue()

        joined_list = rndNum.join(self._special_characters)
        list_delimiter = str(createRandAlNum(2))
        joined_list = list_delimiter + joined_list + list_delimiter
        mutant.setModValue(joined_list)

        # send
        response = self._uri_opener.send_mutant(mutant)

        # restore the mutant values
        mutant.setModValue(oldValue)

        # Analyze the response
        allowed = []
        body = response.getBody()

        # Create the regular expression
        joined_char_regex = rndNum.join([".{0,7}?"] * len(self._special_characters))
        joined_re = list_delimiter + joined_char_regex + list_delimiter

        for match in re.findall(joined_re, body):
            match_without_lim = match[len(list_delimiter) : -len(list_delimiter)]
            split_list = match_without_lim.split(rndNum)
            for char in split_list:
                if char in self._special_characters:
                    allowed.append(char)

        allowed = list(set(allowed))
        allowed.sort()
        self._special_characters.sort()
        disallowed = list(set(self._special_characters) - set(allowed))

        if allowed == self._special_characters:
            om.out.debug("All XSS special characters are allowed: %s" % "".join(allowed))
        else:
            om.out.debug("Allowed XSS special characters: %s" % "".join(allowed))
            om.out.debug("Encoded/Removed XSS special characters: %s" % "".join(disallowed))

        return allowed
Exemple #4
0
 def _mutate( self, data ):
     '''
     Add a random parameter.
     
     @param data: A dict-like object.
     @return: The same object with one new key-value.
     '''
     key = createRandAlNum()
     value = createRandAlNum()
     data[key] = value
     return data
 def _mutate( self, data ):
     '''
     Add a random parameter.
     
     @return: a string.
     '''
     var = createRandAlNum()
     value = createRandAlNum()
     to_append_str = var + "=" + value
     if data.count('?'):
         data += '&' + to_append_str
     else:
         data += '?' + to_append_str
     return data
def handle_crash(type, value, tb, plugins=''):
    '''Function to handle any exception that is not addressed explicitly.'''
    if issubclass(type, KeyboardInterrupt ):
        helpers.endThreads()
        import core.controllers.outputManager as om
        om.out.console(_('Thanks for using w3af.'))
        om.out.console(_('Bye!'))
        sys.exit(0)
        return
        
    exception = traceback.format_exception(type, value, tb)
    exception = "".join(exception)
    print exception

    # save the info to a file
    filename = tempfile.gettempdir() + os.path.sep + "w3af_crash-" + createRandAlNum(5) + ".txt"
    arch = file(filename, "w")
    arch.write(_('Submit this bug here: https://sourceforge.net/apps/trac/w3af/newticket \n'))
    arch.write(VERSIONS)
    arch.write(exception)
    arch.close()
    
    # Create the dialog that allows the user to send the bug to sourceforge
    bug_report_win = bug_report.bug_report_window(
                            _('Bug detected!'), exception, filename, plugins)
    
    # Blocks waiting for user interaction
    bug_report_win.show()
 def _gen_url_to_include( self, file_content, extension ):
     '''
     Generate the URL to include, based on the configuration it will return a 
     URL poiting to a XSS bug, or a URL poiting to our local webserver.
     '''
     if self._use_XSS_vuln and self._xss_vuln:
         url = urlParser.uri2url(self._xss_vuln.getURL())
         data_container = self._xss_vuln.getDc()
         data_container = data_container.copy()
         data_container[self._xss_vuln.getVar()] = file_content
         url_to_include = url + '?' + str(data_container)
         return url_to_include
     else:
         # Write the php to the webroot
         filename = createRandAlNum()
         try:
             file_handler = open(os.path.join(get_home_dir(), 'webroot', filename) , 'w')
             file_handler.write(file_content)
             file_handler.close()
         except:
             raise w3afException('Could not create file in webroot.')
         else:
             url_to_include = 'http://' + self._listen_address +':'
             url_to_include += str(self._listen_port) +'/' + filename
             return url_to_include
Exemple #8
0
    def _generic_vhosts( self, fuzzableRequest ):
        '''
        Test some generic virtual hosts, only do this once.
        '''
        res = []
        base_url = fuzzableRequest.getURL().baseUrl()
        
        common_vhost_list = self._get_common_virtualhosts(base_url)
        
        # Get some responses to compare later
        original_response = self._urlOpener.GET(base_url, useCache=True)
        orig_resp_body = original_response.getBody()
        non_existant = 'iDoNotExistPleaseGoAwayNowOrDie' + createRandAlNum(4)
        self._non_existant_response = self._urlOpener.GET(base_url, useCache=False, \
                                                        headers={'Host': non_existant })
        nonexist_resp_body = self._non_existant_response.getBody()
        
        for common_vhost in common_vhost_list:
            try:
                vhost_response = self._urlOpener.GET( base_url, useCache=False, \
                                                headers={'Host': common_vhost } )
            except w3afException:
                pass
            else:
                vhost_resp_body = vhost_response.getBody()

                # If they are *really* different (not just different by some chars)
                if relative_distance_lt(vhost_resp_body, orig_resp_body, 0.35) and \
                    relative_distance_lt(vhost_resp_body, nonexist_resp_body, 0.35):
                    res.append((common_vhost, vhost_response.id))
        
        return res
Exemple #9
0
    def _single_404_check(self, http_response, html_body):
        '''
        Performs a very simple check to verify if this response is a 404 or not.
        
        It takes the original URL and modifies it by pre-pending a "not-" to the
        filename, then performs a request to that URL and compares the original
        response with the modified one. If they are equal then the original
        request is a 404.
        
        @param http_response: The original HTTP response
        @param html_body: The original HTML body after passing it by a cleaner
        
        @return: True if the original response was a 404 !
        '''
        response_url = http_response.getURL()
        filename = response_url.getFileName()
        if not filename:
            relative_url = '../%s/' % createRandAlNum( 8 )
            url_404 = response_url.urlJoin( relative_url )
        else:
            relative_url = 'not-%s' % filename 
            url_404 = response_url.urlJoin( relative_url )

        response_404 = self._send_404( url_404, store=False )
        clean_response_404_body = get_clean_body(response_404)
        
        return relative_distance_ge(clean_response_404_body, html_body, IS_EQUAL_RATIO)
Exemple #10
0
 def test_find(self):
     find_id = random.randint(1, 499)
     url = url_object('http://w3af.org/a/b/foobar.php?foo=123')
     tag_value = createRandAlNum(10)
     for i in xrange(0, 500):
         fr = FuzzReq(url, dc={'a': ['1']})
         code = 200
         if i == find_id:
             code = 302
         res = httpResponse(code, '<html>',{'Content-Type':'text/html'}, url, url)
         h1 = HistoryItem()
         h1.request = fr
         res.setId(i)
         h1.response = res
         if i == find_id:
             h1.toggleMark()
             h1.updateTag(tag_value)
         h1.save()
     h2 = HistoryItem()
     self.assertEqual(len(h2.find([('tag', "%"+tag_value+"%", 'like')])), 1)
     self.assertEqual(len(h2.find([('code', 302, '=')])), 1)
     self.assertEqual(len(h2.find([('mark', 1, '=')])), 1)
     self.assertEqual(len(h2.find([('has_qs', 1, '=')])), 500)
     self.assertEqual(len(h2.find([('has_qs', 1, '=')], resultLimit=10)), 10)
     results = h2.find([('has_qs', 1, '=')], resultLimit=1, orderData=[('id','desc')])
     self.assertEqual(results[0].id, 499)
     search_data = []
     search_data.append(('id', find_id + 1, "<"))
     search_data.append(('id', find_id - 1, ">"))
     self.assertEqual(len(h2.find(search_data)), 1)
Exemple #11
0
    def _is_echoed( self, mutant ):
        '''
        Verify if the parameter we are fuzzing is really being echoed back in the
        HTML response or not. If it isn't echoed there is no chance we are going to
        find a reflected XSS here.
        
        Also please note that I send a random alphanumeric value, and not a numeric
        value, because even if the number is echoed back (and only numbers are echoed
        back by the application) that won't be of any use in the XSS detection.
        
        @parameter mutant: The request to send.
        @return: True if variable is echoed
        '''
        # Create a random number and assign it to the mutant modified
        # parameter
        rndNum = str( createRandAlNum( 5 ) )
        oldValue = mutant.getModValue() 
        mutant.setModValue(rndNum)

        # send
        response = self._sendMutant( mutant, analyze=False )
        
        # restore the mutant values
        mutant.setModValue(oldValue)
        
        # Analyze and return response
        if rndNum in response:
            om.out.debug('The variable ' + mutant.getVar() + ' is being echoed back.' )
            return True
        else:
            om.out.debug('The variable ' + mutant.getVar() + ' is NOT being echoed back.' )
            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
Exemple #13
0
def create_crash_file(exception):
    filename = "w3af_crash-" + createRandAlNum(5) + ".txt"
    filename = os.path.join( gettempdir() , filename ) 
    crash_dump = file(filename, "w")
    crash_dump.write(_('Submit this bug here: https://sourceforge.net/apps/trac/w3af/newticket \n'))
    crash_dump.write(VERSIONS)
    crash_dump.write(exception)
    crash_dump.close()
    return filename
Exemple #14
0
 def _get_allowed_chars(self, mutant):
     '''
     These are the special characters that are tested:
         ['<', '>', '"', "'", '(', ')']
     
     I'm aware that this doesn't work if the filter also filters by length.
     The idea of this method is to reduce the amount of tests to be performed, if I start
     testing each char separately, I loose that performance enhancement that I want to
     get.
     
     @return: A list with the special characters that are allowed by the XSS filter
     '''
     # Create a random number and assign it to the mutant modified parameter
     rndNum = str( createRandAlNum( 4 ) )
     oldValue = mutant.getModValue() 
     
     joined_list = rndNum.join(self._special_characters)
     list_delimiter = str( createRandAlNum( 5 ) )
     joined_list = list_delimiter + joined_list + list_delimiter
     mutant.setModValue(joined_list)
     
     # send
     response = self._sendMutant( mutant, analyze=False )
     
     # restore the mutant values
     mutant.setModValue(oldValue)
     
     # Analyze the response
     allowed = []
     if response.getBody().count(list_delimiter) == 2:
         start = response.getBody().find(list_delimiter) 
         end = response.getBody().find(list_delimiter, start+1)
         the_list = response.getBody()[start+len(list_delimiter):end]
         split_list = the_list.split(rndNum)
         for i, char in enumerate(split_list):
             if char == self._special_characters[i]:
                 allowed.append(char)
     else:
         raise w3afException('The delimiter was not echoed back!')
     
     if allowed == self._special_characters:
         om.out.debug('All special characters are allowed.')
     
     return allowed
 def _create_file(self):
     '''
     Create random name file php with random php content. To be used in the
     remote file inclusion test.
     '''
     # First, generate the php file to be included.
     rand1 = createRandAlNum(9)
     rand2 = createRandAlNum(9)
     filename = createRandAlNum()
     php_code = '<? \n echo "%s";\n echo "%s";\n ?>' % (rand1, rand2)
     
     # Write the php to the webroot
     file_handler = open(os.path.join(get_home_dir(), 'webroot', filename), 'w')
     file_handler.write(php_code)
     file_handler.close()
     
     # Define the required parameters
     self._rfi_url = 'http://' + self._listen_address +':' + str(self._listen_port)
     self._rfi_url += '/' + filename
     self._rfi_result = rand1 + rand2
Exemple #16
0
    def _PUT(self, domain_path):
        """
        Tests PUT method.
        """
        # upload
        url = domain_path.urlJoin(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)
Exemple #17
0
 def _return_without_eval( self, uri ):
     '''
     This method tries to lower the false positives. 
     '''     
     if not uri.hasQueryString():
         return False
     
     uri.setFileName( uri.getFileName() + createRandAlNum( 7 ) )
         
     try:
         response = self._urlOpener.GET( uri, useCache=True, headers=self._headers )
     except KeyboardInterrupt,e:
         raise e
Exemple #18
0
    def _get_xss_tests(self):
        """
        Does a select to the DB for a list of XSS strings that will be
        tested agains the site.
        
        @return: A list of tuples with all XSS strings to test and
            the browsers in which they work. 
        Example: [('<>RANDOMIZE', ['Internet Explorer'])]
        """
        # Used to identify everything that is sent to the web app
        rnd_value = createRandAlNum(4)

        return [(x[0].replace("RANDOMIZE", rnd_value), x[1]) for x in xss.XSS_TESTS]
Exemple #19
0
    def modifyRequest(self, request ):
        '''
        Mangles the request
        
        @parameter request: HTTPRequest instance that is going to be modified by the evasion plugin
        @return: The modified request

        >>> from core.data.parsers.urlParser import url_object
        >>> import re
        >>> rp = rndPath()
        
        >>> u = url_object('http://www.w3af.com/')
        >>> r = HTTPRequest( u )
        >>> url_string = rp.modifyRequest( r ).url_object.url_string
        >>> re.match('http://www.w3af.com/\w*/../', url_string) is not None
        True

        >>> u = url_object('http://www.w3af.com/abc/')
        >>> r = HTTPRequest( u )
        >>> url_string = rp.modifyRequest( r ).url_object.url_string
        >>> re.match('http://www.w3af.com/\w*/../abc/', url_string) is not None
        True

        >>> u = url_object('http://www.w3af.com/abc/def.htm')
        >>> r = HTTPRequest( u )
        >>> url_string = rp.modifyRequest( r ).url_object.url_string
        >>> re.match('http://www.w3af.com/\w*/../abc/def.htm', url_string) is not None
        True

        >>> u = url_object('http://www.w3af.com/abc/def.htm?id=1')
        >>> r = HTTPRequest( u )
        >>> url_string = rp.modifyRequest( r ).url_object.url_string
        >>> re.match('http://www.w3af.com/\w*/../abc/def.htm\?id=1', url_string) is not None
        True
        
        '''
        # We mangle the URL
        path = request.url_object.getPath()
        if re.match('^/', path):
            random_alnum = createRandAlNum()
            path = '/' + random_alnum + '/..' + path

        # Finally, we set all the mutants to the request in order to return it
        new_url = request.url_object.copy()
        new_url.setPath( path )
        
        # Finally, we set all the mutants to the request in order to return it
        new_req = HTTPRequest( new_url , request.data, request.headers, 
                               request.get_origin_req_host() )
        
        return new_req
Exemple #20
0
 def _idFailedLoginPage(self, freq):
     '''
     Generate TWO different response bodies that are the result of failed
     logins.
     
     The first result is for logins with filled user and password fields;
     the second one is for a filled user and a blank passwd.
     '''
     data_container = freq.getDc()
     data_container = self._true_extra_fields(data_container)
     
     # The first tuple is an invalid username and a password
     # The second tuple is an invalid username with a blank password
     tests = [(createRandAlNum(8), createRandAlNum(8)), 
              (createRandAlNum(8), '')]
     
     # The result is going to be stored here
     self._login_failed_result_list = []
     
     for user, passwd in tests:
         # Setup the data_container
         # Remember that we can have password only forms!
         if self._user_field_name is not None:
             data_container[self._user_field_name][0] = user
         data_container[self._passwd_field_name][0] = passwd
         freq.setDc(data_container)
         
         response = self._sendMutant(freq, analyze=False, grepResult=False)
         
         body = response.getBody()
         body = body.replace(user, '')
         body = body.replace(passwd, '')
         
         # Save it
         self._login_failed_result_list.append(body)
     
     # Now I perform a self test, before starting with the actual bruteforcing
     # The first tuple is an invalid username and a password
     # The second tuple is an invalid username with a blank password
     tests = [(createRandAlNum(8), createRandAlNum(8)),
              (createRandAlNum(8), '')]
     
     for user, passwd in tests:
         # Now I do a self test of the result I just created.
         #   Remember that we can have password only forms!
         if self._user_field_name is not None:
             data_container[self._user_field_name][0] = user
         data_container[self._passwd_field_name][0] = passwd
         freq.setDc(data_container)
         response = self._sendMutant(freq, analyze=False, grepResult=False)
         
         body = response.getBody()
         body = body.replace(user, '')
         body = body.replace(passwd, '')
         
         if not self._matchesFailedLogin(body):
             raise w3afException('Failed to generate a response that '
                                 'matches the failed login page.')
Exemple #21
0
    def _get_files( self ):
        '''
        If the extension is in the templates dir, open it and return the handler.
        If the extension isn't in the templates dir, create a file with random 
        content, open it and return the handler.
        
        @return: A list of open files.
        '''
        result = []

        # All of this work is done in the "/tmp" directory:

        for ext in self._extensions:
            
            template_filename = 'template.' + ext
            if template_filename in os.listdir( self._template_dir ):
                
                #
                # Copy to "/tmp"
                #
                # Open target
                temp_dir = get_temp_dir()
                low_level_fd, file_name = tempfile.mkstemp(prefix='w3af_', suffix='.' + ext, dir=temp_dir)
                file_handler = os.fdopen(low_level_fd, "w+b")
                # Read source
                template_content = file( os.path.join(self._template_dir, template_filename)).read()
                # Write content to target
                file_handler.write(template_content)
                file_handler.close()
                
                # Open the target again:
                try:
                    file_handler = file( file_name, 'r')
                except:
                    raise w3afException('Failed to open temp file: "' + file_name  + '".')
                else:
                    path, file_name = os.path.split(file_name)
                    result.append( (file_handler, file_name) )
                    
            else:
                # I dont have a template for this file extension!
                temp_dir = get_temp_dir()
                low_level_fd, file_name = tempfile.mkstemp(prefix='w3af_', suffix='.' + ext, dir=temp_dir)
                file_handler = os.fdopen(low_level_fd, "w+b")
                file_handler.write( createRandAlNum(32) )
                file_handler.close()
                path, file_name = os.path.split(file_name)
                result.append( (file(file_name), file_name) )
        
        return result
Exemple #22
0
 def _return_without_eval( self, parameters, uri ):
     if urlParser.getDomainPath( uri ) == uri:
         return False
     
     (server, query , expected_response, method , desc) = parameters
     function_reference = getattr( self._urlOpener , method )
     
     url = urlParser.uri2url( uri )
     url += createRandAlNum( 7 )
     if urlParser.getQueryString( query ):
         url = url + '?' + str( urlParser.getQueryString( query ) )
         
     try:
         response = function_reference( url )
     except KeyboardInterrupt,e:
         raise e
Exemple #23
0
 def _return_without_eval(self, uri):
     '''
     This method tries to lower the false positives. 
     '''     
     if not uri.hasQueryString():
         return False
     
     uri.setFileName(uri.getFileName() + createRandAlNum(7))
         
     try:
         response = self._uri_opener.GET(
                                uri, cache=True, headers=self._headers)
     except w3afException, e:
         msg = 'An exception was raised while requesting "' + uri + '" , the error message is: '
         msg += str(e)
         om.out.error(msg)
Exemple #24
0
 def modifyRequest(self, request ):
     '''
     Mangles the request
     
     @parameter request: urllib2.Request instance that is going to be modified by the evasion plugin
     '''
     # We mangle the URL
     path = urlParser.getPathQs( request.get_full_url() )
     if re.match('^/', path):
         random_alnum = createRandAlNum()
         path = '/' + random_alnum + '/..' + path
     
     # Finally, we set all the mutants to the request in order to return it
     url = urlParser.getProtocol( request.get_full_url() )
     url += '://' + urlParser.getNetLocation( request.get_full_url() ) + path        
     new_req = urllib2.Request( url , request.get_data(), request.headers,
                                             request.get_origin_req_host() )
     
     return new_req
Exemple #25
0
    def test_tag(self):
        tag_id = random.randint(501, 999)
        tag_value = createRandAlNum(10)
        url = url_object('http://w3af.org/a/b/c.php')

        for i in xrange(501, 1000):
            fr = FuzzReq(url, dc={'a': ['1']})
            res = httpResponse(200, '<html>',{'Content-Type':'text/html'}, url, url)
            h1 = HistoryItem()
            h1.request = fr
            res.setId(i)
            h1.response = res
            if i == tag_id:
                h1.updateTag(tag_value)
            h1.save()

        h2 = HistoryItem()
        h2.load(tag_id)
        self.assertEqual(h2.tag, tag_value)
Exemple #26
0
 def _get_limit_response( self, m ):
     '''
     We request the limit (something that doesn't exist)
         - If http://localhost/a.php?b=1 ; then I should request b=12938795  (random number)
         - If http://localhost/a.php?b=abc ; then I should request b=hnv98yks (random alnum)
     
     @return: The limit response object
     '''
     # Copy the dc, needed to make a good vuln report
     dc = copy.deepcopy(m.getDc())
     
     if m.getOriginalValue().isdigit():
         m.setModValue( createRandNum(length=8) )
     else:
         m.setModValue( createRandAlNum(length=8) )
     limit_response = self._sendMutant(  m , analyze=False )
     
     # restore the dc
     m.setDc( dc )
     return limit_response
def handle_crash(type, value, tb, **data):
    '''Function to handle any exception that is not addressed explicitly.'''
    if issubclass(type, KeyboardInterrupt ):
        helpers.endThreads()
        import core.controllers.outputManager as om
        om.out.console(_('Thanks for using w3af.'))
        om.out.console(_('Bye!'))
        sys.exit(0)
        return
        
    exception = traceback.format_exception(type, value, tb)
    exception = "".join(exception)
    print exception

    # get version info for python, gtk and pygtk
    versions = _("\nPython version:\n%s\n\n") % sys.version
    versions += _("GTK version:%s\n") % ".".join(str(x) for x in gtk.gtk_version)
    versions += _("PyGTK version:%s\n\n") % ".".join(str(x) for x in gtk.pygtk_version)

    # get the version info for w3af
    versions += '\n' + get_w3af_version()

    # save the info to a file
    filename = tempfile.gettempdir() + os.path.sep + "w3af_crash-" + createRandAlNum(5) + ".txt"
    arch = file(filename, "w")
    arch.write(_('Submit this bug here: https://sourceforge.net/apps/trac/w3af/newticket \n'))
    arch.write(versions)
    arch.write(exception)
    arch.close()
    
    # Create the dialog that allows the user to send the bug to sourceforge
    
    bug_report_win = bug_report.bug_report_window(_('Bug detected!'), 
                                                  exception, versions,
                                                  filename, **data)
    
    # Blocks waiting for user interaction
    bug_report_win.show()
    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 )
Exemple #29
0
    def _parse( self, line ):
        '''
        This method parses a line from the database file
        
        @ return: A a list of tuples where each tuple has the following data
            1. server
            2. query
            3. expected_response
            4. method
            5. desc
        '''
        splitted_line = line.split('","')
        
        server = splitted_line[0].replace('"','')
        original_query = splitted_line[1].replace('"','')
        expected_response = splitted_line[2].replace('"','')
        method = splitted_line[3].replace('"','').upper()
        desc = splitted_line[4].replace('"','')
        desc = desc.replace('\\n', '')
        desc = desc.replace('\\r', '')
        
        if original_query.count(' '):
            return []
        else:
            # Now i should replace the @CGIDIRS variable with the user settings
            # The same goes for every @* variable.
            to_send = []
            to_send.append ( (server, original_query, expected_response, method , desc) )
            
            to_mutate = []
            to_mutate.append( original_query )
            if original_query.count( '@CGIDIRS' ):
                for cgiDir in self._cgi_dirs:
                    query2 = original_query.replace('@CGIDIRS' , cgiDir )
                    to_send.append ( (server, query2, expected_response, method , desc) )
                    to_mutate.append( query2 )
                to_mutate.remove( original_query )
                to_send.remove ( (server, original_query, expected_response, method , desc) )
                
            
            to_mutate2 = []
            for query in to_mutate:
                res = re.findall( 'JUNK\((.*?)\)', query )
                if res:
                    query2 = re.sub( 'JUNK\((.*?)\)', createRandAlNum( int(res[0]) ), query )
                    to_send.append ( (server, query2, expected_response, method , desc) )
                    to_mutate2.append( query2 )
                    to_send.remove ( (server, query, expected_response, method , desc) )
                    to_mutate.remove( query )
            to_mutate.extend( to_mutate2 )
            
            to_mutate2 = []
            for query in to_mutate:
                if query.count( '@ADMINDIRS' ):
                    for adminDir in self._admin_dirs:
                        query2 = query.replace('@ADMINDIRS' , adminDir )
                        to_send.append ( (server, query2, expected_response, method , desc) )
                        to_mutate2.append( query2 )
                    to_mutate.remove( query )
                    to_send.remove ( (server, query, expected_response, method , desc) )
            to_mutate.extend( to_mutate2 )
            
            to_mutate2 = []
            for query in to_mutate:
                if query.count( '@NUKE' ):
                    for nuke in self._nuke:
                        query2 = query.replace('@NUKE' , nuke )
                        to_send.append ( (server, query2, expected_response, method , desc) )
                        to_mutate2.append( query2 )
                    to_mutate.remove( query )
                    to_send.remove ( (server, query, expected_response, method , desc) )
            to_mutate.extend( to_mutate2 )
            
            for query in to_mutate:
                if query.count( '@USERS' ):         
                    for user in self._users:
                        query2 = query.replace('@USERS' , user )
                        to_send.append ( (server, query2, expected_response, method , desc) )
                    to_mutate.remove( query )
                    to_send.remove ( (server, query, expected_response, method , desc) )

            return to_send
    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