Exemple #1
0
    def _test_DNS(self, original_response, dns_wildcard_url):
        '''
        Check if http://www.domain.tld/ == http://domain.tld/
        '''
        headers = Headers([('Host', dns_wildcard_url.get_domain())])
        try:
            modified_response = self._uri_opener.GET(
                original_response.get_url(), cache=True, headers=headers)
        except w3afException:
            return
        else:
            if relative_distance_lt(modified_response.get_body(),
                                    original_response.get_body(), 0.35):
                desc = 'The target site has NO DNS wildcard, and the contents' \
                       ' of "%s" differ from the contents of "%s".'
                desc = desc % (dns_wildcard_url, original_response.get_url())

                i = Info('No DNS wildcard', desc, modified_response.id,
                         self.get_name())
                i.set_url(dns_wildcard_url)

                kb.kb.append(self, 'dns_wildcard', i)
                om.out.information(i.get_desc())
            else:
                desc = 'The target site has a DNS wildcard configuration, the'\
                      ' contents of "%s" are equal to the ones of "%s".'
                desc = desc % (dns_wildcard_url, original_response.get_url())

                i = Info('DNS wildcard', desc, modified_response.id,
                         self.get_name())
                i.set_url(original_response.get_url())

                kb.kb.append(self, 'dns_wildcard', i)
                om.out.information(i.get_desc())
Exemple #2
0
    def grep(self, request, response):
        '''
        Plugin entry point. Parse the object tags.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        url = response.get_url()
        dom = response.get_dom()

        if response.is_text_or_html() and dom is not None \
        and url not in self._already_analyzed:

            self._already_analyzed.add(url)

            elem_list = self._tag_xpath(dom)
            for element in elem_list:

                tag_name = element.tag
                
                desc = 'The URL: "%s" has an "%s" tag. We recommend you download'\
                      ' the client side code and analyze it manually.'
                desc = desc % (response.get_uri(), tag_name)

                i = Info('Browser plugin content', desc, response.id,
                         self.get_name())
                i.set_url(url)
                i.add_to_highlight(tag_name)

                self.kb_append_uniq(self, tag_name, i, 'URL')
Exemple #3
0
    def discover(self, fuzzable_request):
        '''
        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        '''
        root_domain = fuzzable_request.get_url().get_root_domain()

        pks_se = pks(self._uri_opener)
        results = pks_se.search(root_domain)
        pks_url = 'http://pgp.mit.edu:11371/'

        for result in results:
            mail = result.username + '@' + root_domain
            
            desc = 'The mail account: "%s" was found at: "%s".'
            desc = desc % (mail, pks_url)

            i = Info('Email account', desc, result.id, self.get_name())
            i.set_url(URL(pks_url))
            i['mail'] = mail
            i['user'] = result.username
            i['name'] = result.name
            i['url_list'] = set([URL(pks_url), ])
            
            kb.kb.append('emails', 'emails', i)
            #   Don't save duplicated information in the KB. It's useless.
            #kb.kb.append( self, 'emails', i )
            om.out.information(i.get_desc())
Exemple #4
0
    def grep(self, request, response):
        """
        Plugin entry point.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None, all results are saved in the kb.
        """
        url = response.get_url()
        if response.is_text_or_html() and url not in self._already_inspected:

            # Don't repeat URLs
            self._already_inspected.add(url)

            dom = response.get_dom()
            # In some strange cases, we fail to normalize the document
            if dom is None:
                return

            script_elements = self._script_xpath(dom)
            for element in script_elements:
                # returns the text between <script> and </script>
                script_content = element.text

                if script_content is not None:

                    res = self._ajax_regex_re.search(script_content)
                    if res:
                        desc = 'The URL: "%s" has AJAX code.' % url
                        i = Info("AJAX code", desc, response.id, self.get_name())
                        i.set_url(url)
                        i.add_to_highlight(res.group(0))

                        self.kb_append_uniq(self, "ajax", i, "URL")
Exemple #5
0
    def grep(self, request, response):
        '''
        Plugin entry point, find feeds.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        dom = response.get_dom()
        uri = response.get_uri()

        # In some strange cases, we fail to normalize the document
        if uri not in self._already_inspected and dom is not None:

            self._already_inspected.add(uri)

            # Find all feed tags
            element_list = self._tag_xpath(dom)

            for element in element_list:

                feed_tag = element.tag
                feed_type = self._feed_types[feed_tag.lower()]
                version = element.attrib.get('version', 'unknown')

                fmt = 'The URL "%s" is a %s version %s feed.'
                desc = fmt % (uri, feed_type, version)
                i = Info('Content feed resource', desc, response.id,
                         self.get_name())
                i.set_uri(uri)
                i.add_to_highlight(feed_type)
                
                self.kb_append_uniq(self, 'feeds', i, 'URL')
Exemple #6
0
    def discover(self, fuzzable_request):
        '''
        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        '''
        root_domain = fuzzable_request.get_url().get_root_domain()

        pks_se = pks(self._uri_opener)
        results = pks_se.search(root_domain)
        pks_url = 'http://pgp.mit.edu:11371/'

        for result in results:
            mail = result.username + '@' + root_domain

            desc = 'The mail account: "%s" was found at: "%s".'
            desc = desc % (mail, pks_url)

            i = Info('Email account', desc, result.id, self.get_name())
            i.set_url(URL(pks_url))
            i['mail'] = mail
            i['user'] = result.username
            i['name'] = result.name
            i['url_list'] = [
                URL(pks_url),
            ]

            kb.kb.append('emails', 'emails', i)
            #   Don't save duplicated information in the KB. It's useless.
            #kb.kb.append( self, 'emails', i )
            om.out.information(i.get_desc())
    def _fingerprint_data(self, domain_path, wp_unique_url, response):
        '''
        Find wordpress version from data
        '''
        for wp_fingerprint in self._get_wp_fingerprints():
            
            # The URL in the XML is relative AND it has two different variables
            # that we need to replace:
            #        $wp-content$    -> wp-content/
            #        $wp-plugins$    -> wp-content/plugins/
            path = wp_fingerprint.filepath
            path = path.replace('$wp-content$', 'wp-content/')
            path = path.replace('$wp-plugins$', 'wp-content/plugins/')
            test_url = domain_path.url_join(path)
            
            response = self._uri_opener.GET(test_url, cache=True)

            response_hash = hashlib.md5(response.get_body()).hexdigest()

            if response_hash == wp_fingerprint.hash:
                version = wp_fingerprint.version

                # Save it to the kb!
                desc = 'WordPress version "%s" fingerprinted by matching known md5'\
                       ' hashes to HTTP responses of static resources available at'\
                       ' the remote WordPress install.'
                desc = desc % version
                i = Info('Fingerprinted Wordpress version', desc, response.id,
                         self.get_name())
                i.set_url(test_url)
        
                kb.kb.append(self, 'info', i)
                om.out.information(i.get_desc())
                
                break
Exemple #8
0
    def _parse_document(self, response):
        '''
        Parses the HTML and adds the mail addresses to the kb.
        '''
        try:
            document_parser = parser_cache.dpc.get_document_parser_for(
                response)
        except w3afException:
            # Failed to find a suitable parser for the document
            pass
        else:
            # Search for email addresses
            for mail in document_parser.get_emails(self._domain_root):
                if mail not in self._accounts:
                    self._accounts.append(mail)

                    desc = 'The mail account: "%s" was found at: "%s".'
                    desc = desc % (mail, response.get_uri())

                    i = Info('Email account', desc, response.id,
                             self.get_name())
                    i.set_url(response.get_uri())
                    i['mail'] = mail
                    i['user'] = mail.split('@')[0]
                    i['url_list'] = set([
                        response.get_uri(),
                    ])

                    self.kb_append('emails', 'emails', i)
                    self.kb_append(self, 'emails', i)
Exemple #9
0
    def _do_request(self, url, mutant):
        '''
        Perform a simple GET to see if the result is an error or not, and then
        run the actual fuzzing.
        '''
        response = self._uri_opener.GET(
            mutant, cache=True, headers=self._headers)

        if not (is_404(response) or
        response.get_code() in (403, 401) or
        self._return_without_eval(mutant)):
            
            for fr in self._create_fuzzable_requests(response):
                self.output_queue.put(fr)
            
            #
            #   Save it to the kb (if new)!
            #
            if response.get_url() not in self._seen and response.get_url().get_file_name():
                desc = 'A potentially interesting file was found at: "%s".'
                desc = desc % response.get_url()

                i = Info('Potentially interesting file', desc, response.id,
                         self.get_name())
                i.set_url(response.get_url())
                
                kb.kb.append(self, 'files', i)
                om.out.information(i.get_desc())

                # Report only once
                self._seen.add(response.get_url())
Exemple #10
0
    def grep(self, request, response):
        '''
        Plugin entry point, find the blank bodies and report them.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if response.get_body() == '' and request.get_method() in self.METHODS\
        and response.get_code() not in self.HTTP_CODES\
        and 'location' not in response.get_lower_case_headers()\
        and response.get_url() not in self._already_reported:

            #   report these informations only once
            self._already_reported.add(response.get_url())

            desc = 'The URL: "%s" returned an empty body, this could indicate'\
                  ' an application error.'
            desc = desc % response.get_url()

            i = Info('Blank http response body', desc,
                     response.id, self.get_name())
            i.set_url(response.get_url())
            
            self.kb_append(self, 'blank_body', i)
    def _force_disclosures(self, domain_path, potentially_vulnerable_paths):
        '''
        :param domain_path: The path to wordpress' root directory
        :param potentially_vulnerable_paths: A list with the paths I'll URL-join
                                             with @domain_path, GET and parse.
        '''
        for pvuln_path in potentially_vulnerable_paths:

            pvuln_url = domain_path.url_join(pvuln_path)
            response = self._uri_opener.GET(pvuln_url, cache=True)

            if is_404(response):
                continue

            response_body = response.get_body()
            if 'Fatal error: ' in response_body:
                desc = 'Analyze the HTTP response body to find the full path'\
                       ' where wordpress was installed.'
                i = Info('WordPress path disclosure', desc, response.id,
                         self.get_name())
                i.set_url(pvuln_url)
                
                kb.kb.append(self, 'info', i)
                om.out.information(i.get_desc())
                break
    def _force_disclosures(self, domain_path, potentially_vulnerable_paths):
        '''
        :param domain_path: The path to wordpress' root directory
        :param potentially_vulnerable_paths: A list with the paths I'll URL-join
                                             with @domain_path, GET and parse.
        '''
        for pvuln_path in potentially_vulnerable_paths:

            pvuln_url = domain_path.url_join(pvuln_path)
            response = self._uri_opener.GET(pvuln_url, cache=True)

            if is_404(response):
                continue

            response_body = response.get_body()
            if 'Fatal error: ' in response_body:
                desc = 'Analyze the HTTP response body to find the full path'\
                       ' where wordpress was installed.'
                i = Info('WordPress path disclosure', desc, response.id,
                         self.get_name())
                i.set_url(pvuln_url)

                kb.kb.append(self, 'info', i)
                om.out.information(i.get_desc())
                break
Exemple #13
0
    def grep(self, request, response):
        '''
        Plugin entry point, find the blank bodies and report them.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if response.get_body() == '' and request.get_method() in self.METHODS\
        and response.get_code() not in self.HTTP_CODES\
        and 'location' not in response.get_lower_case_headers()\
        and response.get_url() not in self._already_reported:

            #   report these informations only once
            self._already_reported.add(response.get_url())

            desc = 'The URL: "%s" returned an empty body, this could indicate'\
                  ' an application error.'
            desc = desc % response.get_url()

            i = Info('Blank http response body', desc, response.id,
                     self.get_name())
            i.set_url(response.get_url())

            self.kb_append(self, 'blank_body', i)
Exemple #14
0
    def grep(self, request, response):
        '''
        Plugin entry point, verify if the HTML has a form with file uploads.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        url = response.get_url()

        if response.is_text_or_html() and not url in self._already_inspected:

            self._already_inspected.add(url)
            dom = response.get_dom()

            # In some strange cases, we fail to normalize the document
            if dom is not None:

                # Loop through file inputs tags
                for input_file in self._file_input_xpath(dom):
                    msg = 'The URL: "%s" has form with file upload capabilities.'
                    msg = msg % url
                    
                    i = Info('File upload form', msg, response.id,
                             self.get_name())
                    i.set_url(url)
                    to_highlight = etree.tostring(input_file)
                    i.add_to_highlight(to_highlight)
                    
                    self.kb_append_uniq(self, 'file_upload', i, 'URL')
Exemple #15
0
    def grep(self, request, response):
        '''
        Plugin entry point.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None, all results are saved in the kb.
        '''
        url = response.get_url()
        if response.is_text_or_html() and url not in self._already_inspected:

            # Don't repeat URLs
            self._already_inspected.add(url)

            if self.symfony_detected(response):
                dom = response.get_dom()
                if dom is not None and not self.csrf_detected(dom):
                    desc = 'The URL: "%s" seems to be generated by the'\
                           ' Symfony framework and contains a form that'\
                           ' perhaps has CSRF protection disabled.'
                    desc = desc % url
                    i = Info('Symfony Framework with CSRF protection disabled',
                             desc, response.id, self.get_name())
                    i.set_url(url)
                    self.kb_append_uniq(self, 'symfony', i, 'URL')
Exemple #16
0
    def _content_location_not_300(self, request, response):
        """
        Check if the response has a content-location header and the response code
        is not in the 300 range.

        :return: None, all results are saved in the kb.
        """
        if (
            "content-location" in response.get_lower_case_headers()
            and response.get_code() > 300
            and response.get_code() < 310
        ):
            desc = (
                'The URL: "%s" sent the HTTP header: "content-location"'
                ' with value: "%s" in an HTTP response with code %s which'
                " is a violation to the RFC."
            )
            desc = desc % (
                response.get_url(),
                response.get_lower_case_headers()["content-location"],
                response.get_code(),
            )
            i = Info("Content-Location HTTP header anomaly", desc, response.id, self.get_name())
            i.set_url(response.get_url())
            i.add_to_highlight("content-location")

            kb.kb.append(self, "anomaly", i)
Exemple #17
0
    def _do_request(self, mutated_url, user):
        '''
        Perform the request and compare.

        :return: The HTTP response id if the mutated_url is a web user directory,
                 None otherwise.
        '''
        response = self._uri_opener.GET(mutated_url, cache=True,
                                        headers=self._headers)
        
        path = mutated_url.get_path()
        response_body = response.get_body().replace(path, '')

        if relative_distance_lt(response_body, self._non_existent, 0.7):

            # Avoid duplicates
            if user not in [u['user'] for u in kb.kb.get('user_dir', 'users')]:
                desc = 'A user directory was found at: %s'
                desc = desc % response.get_url()
                
                i = Info('Web user home directory', desc, response.id,
                         self.get_name())
                i.set_url(response.get_url())
                i['user'] = user

                kb.kb.append(self, 'users', i)

                for fr in self._create_fuzzable_requests(response):
                    self.output_queue.put(fr)

            return response.id

        return None
    def _fingerprint_meta(self, domain_path, wp_unique_url, response):
        '''
        Check if the wp version is in index header
        '''
        # Main scan URL passed from w3af + wp index page
        wp_index_url = domain_path.url_join('index.php')
        response = self._uri_opener.GET(wp_index_url, cache=True)

        # Find the string in the response html
        find = '<meta name="generator" content="[Ww]ord[Pp]ress (\d\.\d\.?\d?)" />'
        m = re.search(find, response.get_body())

        # If string found, group version
        if m:
            version = m.group(1)

            # Save it to the kb!
            desc = 'WordPress version "%s" found in the index header.'
            desc = desc % version

            i = Info('Fingerprinted Wordpress version', desc, response.id,
                     self.get_name())
            i.set_url(wp_index_url)
            
            kb.kb.append(self, 'info', i)
            om.out.information(i.get_desc())
Exemple #19
0
    def _parse_document(self, response):
        '''
        Parses the HTML and adds the mail addresses to the kb.
        '''
        try:
            document_parser = parser_cache.dpc.get_document_parser_for(response)
        except w3afException:
            # Failed to find a suitable parser for the document
            pass
        else:
            # Search for email addresses
            for mail in document_parser.get_emails(self._domain_root):
                if mail not in self._accounts:
                    self._accounts.append(mail)

                    desc = 'The mail account: "%s" was found at: "%s".'
                    desc = desc % (mail, response.get_uri())

                    i = Info('Email account', desc, response.id,
                             self.get_name())
                    i.set_url(response.get_uri())
                    i['mail'] = mail
                    i['user'] = mail.split('@')[0]
                    i['url_list'] = [response.get_uri(), ]

                    self.kb_append('emails', 'emails', i)
                    self.kb_append(self, 'emails', i)
Exemple #20
0
    def analyze_document_links(self, request, response):
        '''
        Find session IDs in the URI and store them in the KB.
        '''
        try:
            doc_parser = parser_cache.dpc.get_document_parser_for(response)
        except:
            pass
        else:
            parsed_refs, _ = doc_parser.get_references()
            
            for link_uri in parsed_refs:
                if self._has_sessid(link_uri) and \
                response.get_url() not in self._already_reported:
                    #   report these informations only once
                    self._already_reported.add(response.get_url())

                    desc = 'The HTML content at "%s" contains a link (%s)'\
                           ' which holds a session id. The ID could be leaked'\
                           ' to third party domains through the referrer'\
                           ' header.'
                    desc = desc % (response.get_url(), link_uri)
                    
                    #   append the info object to the KB.
                    i = Info('Session ID in URL', desc, response.id,
                             self.get_name())
                    i.set_uri(response.get_uri())
                    
                    self.kb_append(self, 'url_session', i)
                    break
Exemple #21
0
    def _match_cookie_fingerprint(self, request, response, cookie_obj):
        '''
        Now we analyze the cookie and try to guess the remote web server or
        programming framework based on the cookie that was sent.

        :return: True if the cookie was fingerprinted
        '''
        cookie_obj_str = cookie_obj.output(header='')

        for cookie_str_db, system_name in self.COOKIE_FINGERPRINT:
            if cookie_str_db in cookie_obj_str:
                if system_name not in self._already_reported_server:
                    desc = 'A cookie matching the cookie fingerprint DB'\
                           ' has been found when requesting "%s".'\
                           ' The remote platform is: "%s".'
                    desc = desc % (response.get_url(), system_name)

                    i = Info('Identified cookie', desc,
                             response.id, self.get_name())

                    i.set_url(response.get_url())
                    i['httpd'] = system_name
                                        
                    self._set_cookie_to_rep(i, cobj=cookie_obj)

                    kb.kb.append(self, 'security', i)
                    self._already_reported_server.append(system_name)
                    return True

        return False
Exemple #22
0
    def _analyze_results(self, filtered, not_filtered):
        '''
        Analyze the test results and save the conclusion to the kb.
        '''
        if len(filtered) >= len(self._get_offending_strings()) / 5.0:
            desc = 'The remote network has an active filter. IMPORTANT: The'\
                   ' result of all the other plugins will be unaccurate, web'\
                   ' applications could be vulnerable but "protected" by the'\
                   ' active filter.'
                   
            i = Info('Active filter detected', desc, 1, self.get_name())
            i['filtered'] = filtered
            
            kb.kb.append(self, 'afd', i)
            om.out.information(i.get_desc())

            om.out.information('The following URLs were filtered:')
            for i in filtered:
                om.out.information('- ' + i)

            if not_filtered:
                om.out.information(
                    'The following URLs passed undetected by the filter:')
                for i in not_filtered:
                    om.out.information('- ' + i)
Exemple #23
0
    def analyze_document_links(self, request, response):
        '''
        Find session IDs in the URI and store them in the KB.
        '''
        try:
            doc_parser = parser_cache.dpc.get_document_parser_for(response)
        except:
            pass
        else:
            parsed_refs, _ = doc_parser.get_references()

            for link_uri in parsed_refs:
                if self._has_sessid(link_uri) and \
                response.get_url() not in self._already_reported:
                    #   report these informations only once
                    self._already_reported.add(response.get_url())

                    desc = 'The HTML content at "%s" contains a link (%s)'\
                           ' which holds a session id. The ID could be leaked'\
                           ' to third party domains through the referrer'\
                           ' header.'
                    desc = desc % (response.get_url(), link_uri)

                    #   append the info object to the KB.
                    i = Info('Session ID in URL', desc, response.id,
                             self.get_name())
                    i.set_uri(response.get_uri())

                    self.kb_append(self, 'url_session', i)
                    break
Exemple #24
0
    def grep(self, request, response):
        '''
        Plugin entry point.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None, all results are saved in the kb.
        '''
        url = response.get_url()
        if response.is_text_or_html() and url not in self._already_inspected:

            # Don't repeat URLs
            self._already_inspected.add(url)

            if self.symfony_detected(response):
                dom = response.get_dom()
                if dom is not None and not self.csrf_detected(dom):
                    desc = 'The URL: "%s" seems to be generated by the'\
                           ' Symfony framework and contains a form that'\
                           ' perhaps has CSRF protection disabled.'
                    desc = desc % url
                    i = Info('Symfony Framework with CSRF protection disabled',
                             desc, response.id, self.get_name())
                    i.set_url(url)
                    self.kb_append_uniq(self, 'symfony', i, 'URL')
Exemple #25
0
    def _analyze_results(self, filtered, not_filtered):
        '''
        Analyze the test results and save the conclusion to the kb.
        '''
        if len(filtered) >= len(self._get_offending_strings()) / 5.0:
            desc = 'The remote network has an active filter. IMPORTANT: The'\
                   ' result of all the other plugins will be unaccurate, web'\
                   ' applications could be vulnerable but "protected" by the'\
                   ' active filter.'

            i = Info('Active filter detected', desc, 1, self.get_name())
            i['filtered'] = filtered

            kb.kb.append(self, 'afd', i)
            om.out.information(i.get_desc())

            om.out.information('The following URLs were filtered:')
            for i in filtered:
                om.out.information('- ' + i)

            if not_filtered:
                om.out.information(
                    'The following URLs passed undetected by the filter:')
                for i in not_filtered:
                    om.out.information('- ' + i)
Exemple #26
0
    def _fingerprint_data(self, domain_path, wp_unique_url, response):
        '''
        Find wordpress version from data
        '''
        for wp_fingerprint in self._get_wp_fingerprints():

            # The URL in the XML is relative AND it has two different variables
            # that we need to replace:
            #        $wp-content$    -> wp-content/
            #        $wp-plugins$    -> wp-content/plugins/
            path = wp_fingerprint.filepath
            path = path.replace('$wp-content$', 'wp-content/')
            path = path.replace('$wp-plugins$', 'wp-content/plugins/')
            test_url = domain_path.url_join(path)

            response = self._uri_opener.GET(test_url, cache=True)

            response_hash = hashlib.md5(response.get_body()).hexdigest()

            if response_hash == wp_fingerprint.hash:
                version = wp_fingerprint.version

                # Save it to the kb!
                desc = 'WordPress version "%s" fingerprinted by matching known md5'\
                       ' hashes to HTTP responses of static resources available at'\
                       ' the remote WordPress install.'
                desc = desc % version
                i = Info('Fingerprinted Wordpress version', desc, response.id,
                         self.get_name())
                i.set_url(test_url)

                kb.kb.append(self, 'info', i)
                om.out.information(i.get_desc())

                break
Exemple #27
0
    def _fingerprint_meta(self, domain_path, wp_unique_url, response):
        '''
        Check if the wp version is in index header
        '''
        # Main scan URL passed from w3af + wp index page
        wp_index_url = domain_path.url_join('index.php')
        response = self._uri_opener.GET(wp_index_url, cache=True)

        # Find the string in the response html
        find = '<meta name="generator" content="[Ww]ord[Pp]ress (\d\.\d\.?\d?)" />'
        m = re.search(find, response.get_body())

        # If string found, group version
        if m:
            version = m.group(1)

            # Save it to the kb!
            desc = 'WordPress version "%s" found in the index header.'
            desc = desc % version

            i = Info('Fingerprinted Wordpress version', desc, response.id,
                     self.get_name())
            i.set_url(wp_index_url)

            kb.kb.append(self, 'info', i)
            om.out.information(i.get_desc())
Exemple #28
0
    def _analyze_author(self, response, frontpage_author):
        '''
        Analyze the author URL.

        :param response: The http response object for the _vti_inf file.
        :param frontpage_author: A regex match object.
        :return: None. All the info is saved to the kb.
        '''
        author_location = response.get_url().get_domain_path().url_join(
            frontpage_author.group(1))

        # Check for anomalies in the location of author.exe
        if frontpage_author.group(1) != '_vti_bin/_vti_aut/author.exe':
            name = 'Customized frontpage configuration'

            desc = 'The FPAuthorScriptUrl is at: "%s" instead of the default'\
                   ' location: "/_vti_bin/_vti_adm/author.exe". This is very'\
                   ' uncommon.'
            desc = desc % author_location
        else:
            name = 'FrontPage FPAuthorScriptUrl'

            desc = 'The FPAuthorScriptUrl is at: "%s".'
            desc = desc % author_location

        i = Info(name, desc, response.id, self.get_name())
        i.set_url(author_location)
        i['FPAuthorScriptUrl'] = author_location
        
        kb.kb.append(self, 'frontpage_version', i)
        om.out.information(i.get_desc())
Exemple #29
0
    def _extract_version_from_egg(self, query_results):
        """
        Analyzes the eggs and tries to deduce a PHP version number
        ( which is then saved to the kb ).
        """
        if not query_results:
            return None
        else:
            cmp_list = []
            for query_result in query_results:
                body = query_result.http_response.get_body()
                if isinstance(body, unicode):
                    body = body.encode("utf-8")
                hash_str = hashlib.md5(body).hexdigest()

                cmp_list.append((hash_str, query_result.egg_desc))

            cmp_set = set(cmp_list)

            found = False
            matching_versions = []
            for version in self.EGG_DB:
                version_hashes = set(self.EGG_DB[version])

                if len(cmp_set) == len(cmp_set.intersection(version_hashes)):
                    matching_versions.append(version)
                    found = True

            if matching_versions:
                desc = "The PHP framework version running on the remote" " server was identified as:\n- %s"
                versions = "\n- ".join(matching_versions)
                desc = desc % versions

                response_ids = [r.http_response.get_id() for r in query_results]

                i = Info("Fingerprinted PHP version", desc, response_ids, self.get_name())
                i["version"] = matching_versions

                kb.kb.append(self, "version", i)
                om.out.information(i.get_desc())

            if not found:
                version = "unknown"
                powered_by_headers = kb.kb.raw_read("server_header", "powered_by_string")
                try:
                    for v in powered_by_headers:
                        if "php" in v.lower():
                            version = v.split("/")[1]
                except:
                    pass

                msg = (
                    "The PHP version could not be identified using PHP eggs,"
                    ", please send this signature and the PHP version to the"
                    " w3af project develop mailing list. Signature:"
                    " EGG_DB['%s'] = %s\n"
                )
                msg = msg % (version, str(list(cmp_set)))
                om.out.information(msg)
Exemple #30
0
    def _extract_version_from_egg(self, query_results):
        '''
        Analyzes the eggs and tries to deduce a PHP version number
        ( which is then saved to the kb ).
        '''
        if not query_results:
            return None
        else:
            cmp_list = []
            for query_result in query_results:
                body = query_result.http_response.get_body()
                if isinstance(body, unicode): body = body.encode('utf-8')
                hash_str = hashlib.md5(body).hexdigest()
                
                cmp_list.append((hash_str, query_result.egg_desc))
                
            cmp_set = set(cmp_list)

            found = False
            matching_versions = []
            for version in self.EGG_DB:
                version_hashes = set(self.EGG_DB[version])

                if len(cmp_set) == len(cmp_set.intersection(version_hashes)):
                    matching_versions.append(version)
                    found = True

            if matching_versions:
                desc = 'The PHP framework version running on the remote'\
                       ' server was identified as:\n- %s'
                versions = '\n- '.join(matching_versions)
                desc = desc % versions
                
                response_ids = [r.http_response.get_id() for r in query_results]
                
                i = Info('Fingerprinted PHP version', desc, response_ids,
                         self.get_name())
                i['version'] = matching_versions
                
                kb.kb.append(self, 'version', i)
                om.out.information(i.get_desc())

            if not found:
                version = 'unknown'
                powered_by_headers = kb.kb.raw_read('server_header',
                                                    'powered_by_string')
                try:
                    for v in powered_by_headers:
                        if 'php' in v.lower():
                            version = v.split('/')[1]
                except:
                    pass
                
                msg = 'The PHP version could not be identified using PHP eggs,'\
                      ', please send this signature and the PHP version to the'\
                      ' w3af project develop mailing list. Signature:'\
                      ' EGG_DB[\'%s\'] = %s\n'
                msg = msg % (version, str(list(cmp_set)))
                om.out.information(msg)
Exemple #31
0
    def grep(self, request, response):
        '''
        Plugin entry point, search for meta tags.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        uri = response.get_uri()

        if not response.is_text_or_html() or uri in self._already_inspected\
        or is_404(response):
            return

        self._already_inspected.add(uri)

        try:
            dp = parser_cache.dpc.get_document_parser_for(response)
        except w3afException:
            return

        meta_tag_list = dp.get_meta_tags()

        for tag in meta_tag_list:
            tag_name = self._find_name(tag)
            for key, val in tag.items():

                for word in self.INTERESTING_WORDS:

                    # Check if we have something interesting
                    # and WHERE that thing actually is
                    where = content = None
                    if (word in key):
                        where = 'name'
                        content = key
                    elif (word in val):
                        where = 'value'
                        content = val

                    # Now... if we found something, report it =)
                    if where is not None:
                        # The atribute is interesting!
                        fmt = 'The URI: "%s" sent a <meta> tag with attribute'\
                              ' %s set to "%s" which looks interesting.'
                        desc = fmt % (response.get_uri(), where, content)
                        
                        if self.INTERESTING_WORDS.get(tag_name, None):
                            usage = self.INTERESTING_WORDS[tag_name]
                            desc += ' The tag is used for %s.' % usage
                        
                        i = Info('Interesting META tag', desc, response.id,
                                 self.get_name())
                        i.set_uri(response.get_uri())
                        i.add_to_highlight(where, content)

                        self.kb_append_uniq(self, 'meta_tags', i, 'URL')
Exemple #32
0
    def grep(self, request, response):
        '''
        Plugin entry point, search for the user defined regex.
        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if self._all_in_one is None:
            return

        if not response.is_text_or_html():
            return

        # TODO: Verify this this is really a performance improvement
        html_string = response.get_body()
        if not self._all_in_one.search(html_string):
            return

        #One of them is in there, now we need to find out which one
        for index, regex_tuple in enumerate(self._regexlist_compiled):

            regex, info_inst = regex_tuple
            match_object = regex.search(html_string)

            if match_object:
                with self._plugin_lock:
                    #Don't change the next line to "if info_inst:",
                    #because the info_inst is an empty dict {}
                    #which evaluates to false
                    #but an info object is not the same as None
                    if not info_inst is None:
                        ids = info_inst.get_id()
                        ids.append(response.id)
                        info_inst.set_id(ids)
                    else:
                        str_match = match_object.group(0)
                        if len(str_match) > 20:
                            str_match = str_match[:20] + '...'

                        desc = 'User defined regular expression "%s" matched a' \
                               ' response. The matched string is: "%s".'
                        desc = desc % (regex.pattern, str_match)

                        info_inst = Info(
                            'User defined regular expression match', desc,
                            response.id, self.get_name())
                        info_inst.set_url(response.get_url())

                        om.out.information(desc)

                        self.kb_append_uniq(self, 'user_defined_regex',
                                            info_inst, 'URL')

                    # Save the info_inst
                    self._regexlist_compiled[index] = (regex, info_inst)
Exemple #33
0
    def _html_in_comment(self, comment, request, response):
        '''
        Find HTML code in HTML comments
        '''
        html_in_comment = self.HTML_RE.search(comment)

        if html_in_comment and \
        (comment, response.get_url()) not in self._already_reported_interesting:
            # There is HTML code in the comment.
            comment = comment.replace('\n', '')
            comment = comment.replace('\r', '')
            desc = 'A comment with the string "%s" was found in: "%s".'\
                   ' This could be interesting.'
            desc = desc % (comment, response.get_url())

            i = Info('HTML comment contains HTML code', desc, response.id,
                     self.get_name())
            i.set_dc(request.get_dc())
            i.set_uri(response.get_uri())
            i.add_to_highlight(html_in_comment.group(0))

            kb.kb.append(self, 'html_comment_hides_html', i)
            om.out.information(i.get_desc())
            self._already_reported_interesting.add(
                (comment, response.get_url()))
Exemple #34
0
 def _ssl_info_to_kb(self, url, domain):
     cert, cert_der, cipher = self._get_cert(url, domain)
     
     # Print the SSL information to the log
     desc = 'This is the information about the SSL certificate used for'\
            ' %s site:\n%s' % (domain,
                               self._dump_ssl_info(cert, cert_der, cipher))
     om.out.information(desc)
     i = Info('SSL Certificate dump', desc, 1, self.get_name())
     i.set_url(url)
     
     self.kb_append(self, 'certificate', i)
Exemple #35
0
    def _report_no_realm(self, response):
        # Report this strange case
        desc = 'The resource: "%s" requires authentication (HTTP Code'\
               ' 401) but the www-authenticate header is not present.'\
               ' This requires human verification.'
        desc = desc % response.get_url() 
        i = Info('Authentication without www-authenticate header', desc,
                 response.id, self.get_name())
        i.set_url(response.get_url())

        kb.kb.append(self, 'non_rfc_auth', i)
        om.out.information(i.get_desc())
Exemple #36
0
    def _PUT(self, domain_path):
        '''
        Tests PUT method.
        '''
        # upload
        url = domain_path.url_join(rand_alpha(5))
        rnd_content = rand_alnum(6)
        put_response = self._uri_opener.PUT(url, data=rnd_content)

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

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

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

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

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

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

            i.set_url(url)
            i.set_method('PUT')
            
            self.kb_append(self, 'dav', i)
Exemple #37
0
    def grep(self, request, response):
        '''
        Plugin entry point, search for the user defined regex.
        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if self._all_in_one is None:
            return

        if not response.is_text_or_html():
            return

        # TODO: Verify this this is really a performance improvement
        html_string = response.get_body()
        if not self._all_in_one.search(html_string):
            return

        #One of them is in there, now we need to find out which one
        for index, regex_tuple in enumerate(self._regexlist_compiled):

            regex, info_inst = regex_tuple
            match_object = regex.search(html_string)

            if match_object:
                with self._plugin_lock:
                    #Don't change the next line to "if info_inst:",
                    #because the info_inst is an empty dict {}
                    #which evaluates to false
                    #but an info object is not the same as None
                    if not info_inst is None:
                        ids = info_inst.get_id()
                        ids.append(response.id)
                        info_inst.set_id(ids)
                    else:
                        str_match = match_object.group(0)
                        if len(str_match) > 20:
                            str_match = str_match[:20] + '...'
                            
                        desc = 'User defined regular expression "%s" matched a' \
                               ' response. The matched string is: "%s".'
                        desc = desc % (regex.pattern, str_match)
                        
                        info_inst = Info('User defined regular expression match',
                                         desc, response.id, self.get_name())
                        info_inst.set_url(response.get_url())
                        
                        om.out.information(desc)

                        self.kb_append_uniq(self, 'user_defined_regex', info_inst, 'URL')

                    # Save the info_inst
                    self._regexlist_compiled[index] = (regex, info_inst)
    def _report_finding(self, response):
        '''
        Save the finding to the kb.

        :param response: The response that triggered the detection
        '''
        desc = 'The remote web server seems to have a reverse proxy installed.'

        i = Info('Reverse proxy identified', desc, response.id, self.get_name())
        i.set_url(response.get_url())

        kb.kb.append(self, 'detect_reverse_proxy', i)
        om.out.information(i.get_desc())
Exemple #39
0
 def analyze_disco(self, request, response):
     for disco_string in self._disco_strings:
         if disco_string in response:
             desc = 'The URL: "%s" is a DISCO file that contains references'\
                    ' to WSDL URLs.'
             desc = desc % response.get_url()
             i = Info('DISCO resource', desc, response.id,
                      self.get_name())
             i.set_url(response.get_url())
             i.add_to_highlight(disco_string)
             
             self.kb_append_uniq(self, 'disco', i, 'URL')
             break
Exemple #40
0
    def _analyze_crossdomain_clientaccesspolicy(self, url, response, file_name):
        try:
            dom = xml.dom.minidom.parseString(response.get_body())
        except Exception:
            # Report this, it may be interesting for the final user
            # not a vulnerability per-se... but... it's information after all
            if 'allow-access-from' in response.get_body() or \
            'cross-domain-policy' in response.get_body() or \
            'cross-domain-access' in response.get_body():

                desc = 'The "%s" file at: "%s" is not a valid XML.'
                desc = desc % (file_name, response.get_url())
            
                i = Info('Invalid RIA settings file', desc, response.id,
                         self.get_name())
                i.set_url(response.get_url())
                
                kb.kb.append(self, 'info', i)
                om.out.information(i.get_desc())
        else:
            if(file_name == 'crossdomain.xml'):
                url_list = dom.getElementsByTagName("allow-access-from")
                attribute = 'domain'
            if(file_name == 'clientaccesspolicy.xml'):
                url_list = dom.getElementsByTagName("domain")
                attribute = 'uri'

            for url in url_list:
                url = url.getAttribute(attribute)

                desc = 'The "%s" file at "%s" allows flash/silverlight'\
                       ' access from any site.'
                desc = desc % (file_name, response.get_url())

                if url == '*':
                    v = Vuln('Insecure RIA settings', desc, severity.LOW,
                             response.id, self.get_name())
                    v.set_url(response.get_url())
                    v.set_method('GET')

                    kb.kb.append(self, 'vuln', v)
                    om.out.vulnerability(v.get_desc(),
                                         severity=v.get_severity())
                else:
                    i = Info('Cross-domain allow ACL', desc,
                             response.id, self.get_name())
                    i.set_url(response.get_url())
                    i.set_method('GET')

                    kb.kb.append(self, 'info', i)
                    om.out.information(i.get_desc())
 def _kb_info_user(self, url, response_id, username):
     '''
     Put user in Kb
     :return: None, everything is saved in kb
     '''
     desc = 'WordPress user "%s" found during username enumeration.'
     desc = desc % username
     
     i = Info('Identified WordPress user', desc, response_id,
              self.get_name())
     i.set_url(url)
     
     kb.kb.append(self, 'users', i)
     om.out.information(i.get_desc())
Exemple #42
0
    def _analyze_gears_manifest(self, url, response, file_name):
        if '"entries":' in response:
            # Save it to the kb!
            desc = 'A gears manifest file was found at: "%s".'\
                   ' Each file should be manually reviewed for sensitive'\
                   ' information that may get cached on the client.'
            desc = desc % url

            i = Info('Gears manifest resource', desc, response.id,
                     self.get_name())
            i.set_url(url)

            kb.kb.append(self, url, i)
            om.out.information(i.get_desc())
    def _kb_info_user(self, url, response_id, username):
        '''
        Put user in Kb
        :return: None, everything is saved in kb
        '''
        desc = 'WordPress user "%s" found during username enumeration.'
        desc = desc % username

        i = Info('Identified WordPress user', desc, response_id,
                 self.get_name())
        i.set_url(url)

        kb.kb.append(self, 'users', i)
        om.out.information(i.get_desc())
Exemple #44
0
    def _html_in_comment(self, comment, request, response):
        '''
        Find HTML code in HTML comments
        '''
        html_in_comment = self.HTML_RE.search(comment)
        
        if html_in_comment and \
        (comment, response.get_url()) not in self._already_reported_interesting:
            # There is HTML code in the comment.
            comment = comment.replace('\n', '')
            comment = comment.replace('\r', '')
            desc = 'A comment with the string "%s" was found in: "%s".'\
                   ' This could be interesting.'
            desc = desc % (comment, response.get_url())

            i = Info('HTML comment contains HTML code', desc,
                     response.id, self.get_name())
            i.set_dc(request.get_dc())
            i.set_uri(response.get_uri())
            i.add_to_highlight(html_in_comment.group(0))
            
            kb.kb.append(self, 'html_comment_hides_html', i)
            om.out.information(i.get_desc())
            self._already_reported_interesting.add(
                (comment, response.get_url()))
Exemple #45
0
    def _analyze_methods(self, url, allowed_methods, id_list):
        # Check for DAV
        if set(allowed_methods).intersection(self.DAV_METHODS):
            # dav is enabled!
            # Save the results in the KB so that other plugins can use this
            # information
            desc = 'The URL "%s" has the following allowed methods. These'\
                  ' include DAV methods and should be disabled: %s'
            desc = desc % (url, ', '.join(allowed_methods))

            i = Info('DAV methods enabled', desc, id_list, self.get_name())
            i.set_url(url)
            i['methods'] = allowed_methods

            kb.kb.append(self, 'dav-methods', i)
        else:
            # Save the results in the KB so that other plugins can use this
            # information. Do not remove these information, other plugins
            # REALLY use it !
            desc = 'The URL "%s" has the following enabled HTTP methods: %s'
            desc = desc % (url, ', '.join(allowed_methods))

            i = Info('Allowed HTTP methods', desc, id_list, self.get_name())
            i.set_url(url)
            i['methods'] = allowed_methods

            kb.kb.append(self, 'methods', i)
Exemple #46
0
    def _identify_with_bruteforce(self, url):
        id_list = []
        allowed_methods = []
        #
        #   Before doing anything else, I'll send a request with a
        #   non-existant method if that request succeds, then all will...
        #
        non_exist_response = self._uri_opener.ARGENTINA(url)
        get_response = self._uri_opener.GET(url)

        if non_exist_response.get_code() not in self.BAD_CODES\
        and get_response.get_body() == non_exist_response.get_body():

            desc = 'The remote Web server has a custom configuration, in'\
                  ' which any not implemented methods that are invoked are'\
                  ' defaulted to GET instead of returning a "Not Implemented"'\
                  ' response.'
            response_ids = [non_exist_response.get_id(), get_response.get_id()]
            i = Info('Non existent methods default to GET', desc, response_ids,
                     self.get_name())
            i.set_url(url)

            kb.kb.append(self, 'custom-configuration', i)
            #
            #   It makes no sense to continue working, all methods will
            #   appear as enabled because of this custom configuration.
            #
            return [], [non_exist_response.id, get_response.id]

        # 'DELETE' is not tested! I don't want to remove anything...
        # 'PUT' is not tested! I don't want to overwrite anything...
        methods_to_test = self._supported_methods.copy()

        # remove dangerous methods.
        methods_to_test.remove('DELETE')
        methods_to_test.remove('PUT')

        for method in methods_to_test:
            method_functor = getattr(self._uri_opener, method)
            try:
                response = apply(method_functor, (url, ), {})
            except:
                pass
            else:
                code = response.get_code()
                if code not in self.BAD_CODES:
                    allowed_methods.append(method)
                    id_list.append(response.id)

        return allowed_methods, id_list
Exemple #47
0
    def _report_finding(self, response):
        '''
        Save the finding to the kb.

        :param response: The response that triggered the detection
        '''
        desc = 'The remote web server seems to have a reverse proxy installed.'

        i = Info('Reverse proxy identified', desc, response.id,
                 self.get_name())
        i.set_url(response.get_url())

        kb.kb.append(self, 'detect_reverse_proxy', i)
        om.out.information(i.get_desc())
Exemple #48
0
    def _identify_with_bruteforce(self, url):
        id_list = []
        allowed_methods = []
        #
        #   Before doing anything else, I'll send a request with a
        #   non-existant method if that request succeds, then all will...
        #
        non_exist_response = self._uri_opener.ARGENTINA(url)
        get_response = self._uri_opener.GET(url)

        if non_exist_response.get_code() not in self.BAD_CODES\
        and get_response.get_body() == non_exist_response.get_body():

            desc = 'The remote Web server has a custom configuration, in'\
                  ' which any not implemented methods that are invoked are'\
                  ' defaulted to GET instead of returning a "Not Implemented"'\
                  ' response.'
            response_ids = [non_exist_response.get_id(), get_response.get_id()]
            i = Info('Non existent methods default to GET', desc, response_ids,
                     self.get_name())
            i.set_url(url)
            
            kb.kb.append(self, 'custom-configuration', i)
            #
            #   It makes no sense to continue working, all methods will
            #   appear as enabled because of this custom configuration.
            #
            return [], [non_exist_response.id, get_response.id]

        # 'DELETE' is not tested! I don't want to remove anything...
        # 'PUT' is not tested! I don't want to overwrite anything...
        methods_to_test = self._supported_methods.copy()

        # remove dangerous methods.
        methods_to_test.remove('DELETE')
        methods_to_test.remove('PUT')

        for method in methods_to_test:
            method_functor = getattr(self._uri_opener, method)
            try:
                response = apply(method_functor, (url,), {})
            except:
                pass
            else:
                code = response.get_code()                
                if code not in self.BAD_CODES:
                    allowed_methods.append(method)
                    id_list.append(response.id)
        
        return allowed_methods, id_list
Exemple #49
0
    def crawl(self, fuzzable_request):
        '''
        Get the robots.txt file and parse it.

        :param fuzzable_request: A fuzzable_request instance that contains
                                (among other things) the URL to test.
        '''
        dirs = []

        base_url = fuzzable_request.get_url().base_url()
        robots_url = base_url.url_join('robots.txt')
        http_response = self._uri_opener.GET(robots_url, cache=True)

        if not is_404(http_response):
            # Save it to the kb!
            desc = 'A robots.txt file was found at: "%s", this file might'\
                   ' expose private URLs and requires a manual review. The'\
                   ' scanner will add all URLs listed in this files to the'\
                   ' analysis queue.'
            desc =  desc % robots_url
            
            i = Info('robots.txt file', desc,
                     http_response.id, self.get_name())
            i.set_url(robots_url)
            
            kb.kb.append(self, 'robots.txt', i)
            om.out.information(i.get_desc())

            # Work with it...
            dirs.append(robots_url)
            for line in http_response.get_body().split('\n'):

                line = line.strip()

                if len(line) > 0 and line[0] != '#' and \
                    (line.upper().find('ALLOW') == 0 or
                     line.upper().find('DISALLOW') == 0):

                    url = line[line.find(':') + 1:]
                    url = url.strip()
                    try:
                        url = base_url.url_join(url)
                    except:
                        # Simply ignore the invalid URL
                        pass
                    else:
                        dirs.append(url)

        self.worker_pool.map(self.http_get_and_parse, dirs)
Exemple #50
0
    def _parse_zone_h_result(self, response):
        '''
        Parse the result from the zone_h site and create the corresponding info
        objects.

        :return: None
        '''
        #
        #   I'm going to do only one big "if":
        #
        #       - The target site was hacked more than one time
        #       - The target site was hacked only one time
        #

        # This is the string I have to parse:
        # in the zone_h response, they are two like this, the first has to be ignored!
        regex = 'Total notifications: <b>(\d*)</b> of which <b>(\d*)</b> single ip and <b>(\d*)</b> mass'
        regex_result = re.findall(regex, response.get_body())

        try:
            total_attacks = int(regex_result[0][0])
        except IndexError:
            om.out.debug(
                'An error was generated during the parsing of the zone_h website.'
            )
        else:

            # Do the if...
            if total_attacks > 1:
                desc = 'The target site was defaced more than one time in the'\
                       ' past. For more information please visit the following'\
                       ' URL: "%s".' % response.get_url()

                v = Vuln('Previous defacements', desc, severity.MEDIUM,
                         response.id, self.get_name())
                v.set_url(response.get_url())

                kb.kb.append(self, 'defacements', v)
                om.out.information(v.get_desc())
            elif total_attacks == 1:
                desc = 'The target site was defaced in the past. For more'\
                       ' information please visit the following URL: "%s".'
                desc = desc % response.get_url()
                i = Info('Previous defacements', desc, response.id,
                         self.get_name())
                i.set_url(response.get_url())

                kb.kb.append(self, 'defacements', i)
                om.out.information(i.get_desc())
Exemple #51
0
    def _fingerprint_installer(self, domain_path, wp_unique_url, response):
        '''
        GET latest.zip and latest.tar.gz and compare with the hashes from the
        release.db that was previously generated from wordpress.org [0]
        and contains all release hashes.

        This gives the initial wordpress version, not the current one.

        [0] http://wordpress.org/download/release-archive/
        '''
        zip_url = domain_path.url_join('latest.zip')
        tar_gz_url = domain_path.url_join('latest.tar.gz')
        install_urls = [zip_url, tar_gz_url]

        for install_url in install_urls:
            response = self._uri_opener.GET(install_url,
                                            cache=True,
                                            respect_size_limit=False)

            # md5sum the response body
            m = hashlib.md5()
            m.update(response.get_body())
            remote_release_hash = m.hexdigest()

            release_db = self._release_db

            for line in file(release_db):
                try:
                    line = line.strip()
                    release_db_hash, release_db_name = line.split(',')
                except:
                    continue

                if release_db_hash == remote_release_hash:

                    desc = 'The sysadmin used WordPress version "%s" during the'\
                           ' installation, which was found by matching the contents'\
                           ' of "%s" with the hashes of known releases. If the'\
                           ' sysadmin did not update wordpress, the current version'\
                           ' will still be the same.'
                    desc = desc % (release_db_name, install_url)

                    i = Info('Fingerprinted Wordpress version', desc,
                             response.id, self.get_name())
                    i.set_url(install_url)

                    kb.kb.append(self, 'info', i)
                    om.out.information(i.get_desc())
    def discover(self, fuzzable_request):
        '''
        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        '''
        if self._is_proxyed_conn(fuzzable_request):
            desc = 'Your ISP seems to have a transparent proxy installed,'\
                   ' this can influence scan results in unexpected ways.'

            i = Info('Transparent proxy detected', desc, 1, self.get_name())
            i.set_url(fuzzable_request.get_url())

            kb.kb.append(self, 'detect_transparent_proxy', i)
            om.out.information(i.get_desc())
        else:
            om.out.information('Your ISP has no transparent proxy.')
Exemple #53
0
    def _header_was_injected(self, mutant, response):
        '''
        This method verifies if a header was successfully injected

        :param mutant: The mutant that was sent to generate the response
        :param response: The HTTP response where I want to find the injected header.
        :return: True / False
        '''
        # Get the lower case headers
        headers = response.get_lower_case_headers()

        # Analyze injection
        for header, value in headers.items():
            if HEADER_NAME in header and value.lower() == HEADER_VALUE:
                return True

            elif HEADER_NAME in header and value.lower() != HEADER_VALUE:
                msg = 'The vulnerable header was added to the HTTP response,'\
                      ' but the value is not what w3af expected (%s: %s).'\
                      ' Please verify manually.'
                msg = msg % (HEADER_NAME, HEADER_VALUE)
                om.out.information(msg)

                i = Info.from_mutant('Parameter modifies response headers',
                                     msg, response.id, self.get_name(), mutant)

                self.kb_append_uniq(self, 'response_splitting', i)
                return False

        return False
Exemple #54
0
    def test_from_mutant(self):
        dc = DataContainer()
        url = URL('http://moth/')
        payloads = ['abc', 'def']

        dc['a'] = [
            '1',
        ]
        dc['b'] = [
            '2',
        ]
        freq = FuzzableRequest(url, dc=dc)
        fuzzer_config = {}

        created_mutants = Mutant.create_mutants(freq, payloads, [], False,
                                                fuzzer_config)

        mutant = created_mutants[0]

        inst = Info.from_mutant('TestCase', 'desc' * 30, 1, 'plugin_name',
                                mutant)

        self.assertIsInstance(inst, Info)

        self.assertEqual(inst.get_uri(), mutant.get_uri())
        self.assertEqual(inst.get_url(), mutant.get_url())
        self.assertEqual(inst.get_method(), mutant.get_method())
        self.assertEqual(inst.get_dc(), mutant.get_dc())
        self.assertEqual(inst.get_var(), mutant.get_var())
Exemple #55
0
    def _analyze_result(self, mutant, response):
        '''
        Analyze results of the _send_mutant method.
        '''
        #
        #   I will only report the vulnerability once.
        #
        if self._has_no_bug(mutant):

            if self._header_was_injected(mutant, response):
                desc = 'Response splitting was found at: %s' % mutant.found_at(
                )

                v = Vuln.from_mutant('Response splitting vulnerability', desc,
                                     severity.MEDIUM, response.id,
                                     self.get_name(), mutant)

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

            # When trying to send a response splitting to php 5.1.2 I get :
            # Header may not contain more than a single header, new line detected
            for error in self.HEADER_ERRORS:

                if error in response:
                    desc = 'The variable "%s" at URL "%s" modifies the HTTP'\
                           ' response headers, but this error was sent while'\
                           ' testing for response splitting: "%s".'
                    desc = desc % (mutant.get_var(), mutant.get_url(), error)
                    i = Info.from_mutant('Parameter modifies response headers',
                                         desc, response.id, self.get_name(),
                                         mutant)

                    self.kb_append_uniq(self, 'response_splitting', i)

                    return
Exemple #56
0
    def __init__(self, name, desc, severity, response_ids, plugin_name):
        '''
        :param name: The vulnerability name, will be checked against the values
                     in core.data.constants.vulns.
        
        :param desc: The vulnerability description
        
        :param severity: The severity for this object
        
        :param response_ids: A list of response ids associated with this vuln
        
        :param plugin_name: The name of the plugin which identified the vuln
        '''
        Info.__init__(self, name, desc, response_ids, plugin_name)

        self.set_severity(severity)