Exemplo n.º 1
0
    def _check_if_exists(self, web_shell_url):
        '''
        Check if the file exists.

        :param web_shell_url: The URL to check
        '''
        try:
            response = self._uri_opener.GET(web_shell_url, cache=True)
        except w3afException:
            om.out.debug('Failed to GET webshell:' + web_shell_url)
        else:
            if self._is_possible_backdoor(response):
                desc = 'A web backdoor was found at: "%s"; this could ' \
                       'indicate that the server has been compromised.'
                desc = desc % response.get_url()

                v = Vuln('Potential web backdoor', desc, severity.HIGH,
                         response.id, self.get_name())
                v.set_url(response.get_url())

                kb.kb.append(self, 'backdoors', v)
                om.out.vulnerability(v.get_desc(), severity=v.get_severity())

                for fr in self._create_fuzzable_requests(response):
                    self.output_queue.put(fr)
Exemplo n.º 2
0
    def end(self):
        '''
        This method is called when the plugin wont be used anymore.

        The real job of this plugin is done here, where I will try to see if
        one of the error_500 responses were not identified as a vuln by some
        of my audit plugins
        '''
        all_vulns = kb.kb.get_all_vulns()
        all_vulns_tuples = [(v.get_uri(), v.get_dc()) for v in all_vulns]

        for request, error_500_response_id in self._error_500_responses:
            if (request.get_uri(), request.get_dc()) not in all_vulns_tuples:
                # Found a err 500 that wasnt identified !!!
                desc = 'An unidentified web application error (HTTP response'\
                       ' code 500) was found at: "%s". Enable all plugins and'\
                       ' try again, if the vulnerability still is not identified'\
                       ', please verify manually and report it to the w3af'\
                       ' developers.'
                desc = desc % request.get_url()

                v = Vuln('Unhandled error in web application',
                         desc, severity.MEDIUM, error_500_response_id,
                         self.get_name())

                v.set_uri(request.get_uri())

                self.kb_append_uniq(self, 'error_500', v, 'VAR')

        self._error_500_responses.cleanup()
Exemplo n.º 3
0
    def _analyze_headers(self, request, response):
        '''
        Search for IP addresses in HTTP headers
        '''
        # Get the headers string
        headers_string = response.dump_headers()

        #   Match the regular expressions
        for regex in self._regex_list:
            for match in regex.findall(headers_string):

                # If i'm requesting 192.168.2.111 then I don't want to be
                # alerted about it
                if match not in self._ignore_if_match:
                    desc = 'The URL: "%s" returned an HTTP header with a'\
                           ' private IP address: "%s".'
                    desc = desc % (response.get_url(), match)
                    v = Vuln('Private IP disclosure vulnerability', desc,
                             severity.LOW, response.id, self.get_name())

                    v.set_url(response.get_url())

                    v['IP'] = match
                    v.add_to_highlight(match)
                    self.kb_append(self, 'header', v)
Exemplo n.º 4
0
    def _classic_worker(self, gh, search_term):
        '''
        Perform the searches and store the results in the kb.
        '''
        google_list = self._google_se.get_n_results(search_term, 9)

        for result in google_list:
            # I found a vuln in the site!
            response = self._uri_opener.GET(result.URL, cache=True)
            if not is_404(response):
                desc = 'ghdb plugin found a vulnerability at URL: "%s".' \
                      ' According to GHDB the vulnerability description'\
                      ' is "%s".'
                desc = desc % (response.get_url(), gh.desc)
                
                v = Vuln('Google hack database match', desc,
                         severity.MEDIUM, 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=severity.LOW)

                # Create the fuzzable requests
                for fr in self._create_fuzzable_requests(response):
                    self.output_queue.put(fr)
Exemplo n.º 5
0
    def _check_methods(self, url):
        '''
        Perform some requests in order to check if we are able to retrieve
        some data with methods that may be wrongly enabled.
        '''
        allowed_methods = []
        for method in ['GET', 'POST', 'ABCD', 'HEAD']:
            method_functor = getattr(self._uri_opener, method)
            try:
                response = apply(method_functor, (url, ), {})
                code = response.get_code()
            except:
                pass
            else:
                if code not in self.BAD_METHODS:
                    allowed_methods.append((method, response.id))

        if len(allowed_methods) > 0:

            response_ids = [i for m, i in allowed_methods]
            methods = ', '.join([m for m, i in allowed_methods]) + '.'
            desc = 'The resource: "%s" requires authentication but the access'\
                   ' is misconfigured and can be bypassed using these'\
                   ' methods: %s.'
            desc = desc % (url, methods)

            v = Vuln('Misconfigured access control', desc, severity.MEDIUM,
                     response_ids, self.get_name())

            v.set_url(url)
            v['methods'] = allowed_methods

            self.kb_append(self, 'auth', v)
Exemplo n.º 6
0
    def _PROPFIND(self, domain_path):
        '''
        Test PROPFIND method
        '''
        content = "<?xml version='1.0'?>\r\n"
        content += "<a:propfind xmlns:a='DAV:'>\r\n"
        content += "<a:prop>\r\n"
        content += "<a:displayname:/>\r\n"
        content += "</a:prop>\r\n"
        content += "</a:propfind>\r\n"

        hdrs = Headers([('Depth', '1')])
        res = self._uri_opener.PROPFIND(domain_path,
                                        data=content,
                                        headers=hdrs)

        if "D:href" in res and res.get_code() in xrange(200, 300):
            msg = 'Directory listing with HTTP PROPFIND method was found at' \
                  ' directory: "%s".' % domain_path

            v = Vuln('Insecure DAV configuration', msg, severity.MEDIUM,
                     res.id, self.get_name())

            v.set_url(res.get_url())
            v.set_method('PROPFIND')

            self.kb_append(self, 'dav', v)
Exemplo n.º 7
0
    def grep(self, request, response):
        '''
        Plugin entry point, search for directory indexing.
        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if not response.is_text_or_html():
            return
        
        if response.get_url().get_domain_path() in self._already_visited:
            return

        self._already_visited.add(response.get_url().get_domain_path())
        
        html_string = response.get_body()
        for dir_indexing_match in self._multi_in.query(html_string):
            
            desc = 'The URL: "%s" has a directory indexing vulnerability.'
            desc = desc % response.get_url()
            
            v = Vuln('Directory indexing', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            self.kb_append_uniq(self, 'directory', v, 'URL')
            break
Exemplo n.º 8
0
    def end(self):
        # If all URLs implement protection, don't report anything.
        if not self._vuln_count:
            return

        # If none of the URLs implement protection, simply report
        # ONE vulnerability that says that.
        if self._total_count == self._vuln_count:
            desc = 'The whole target web application has no protection (Pragma'\
                   ' and Cache-Control headers) against sensitive content'\
                   ' caching.'

        # If most of the URLs implement the protection but some
        # don't, report ONE vulnerability saying: "Most are protected, but x, y are not.
        if self._total_count > self._vuln_count:
            desc = 'Some URLs have no protection (Pragma and Cache-Control'\
                  ' headers) against sensitive content caching. Among them:\n'
            desc += ' '.join([str(url) + '\n' for url in self._vulns])

        response_ids = [_id for _id in self._ids]

        v = Vuln('Missing cache control for HTTPS content', desc, severity.LOW,
                 response_ids, self.get_name())

        self.kb_append_uniq(self, 'cache_control', v, 'URL')

        self._vulns.cleanup()
        self._ids.cleanup()
Exemplo n.º 9
0
    def _universal_allow(self, forged_req, url, origin, response, allow_origin,
                         allow_credentials, allow_methods):
        '''
        Check if the allow_origin is set to *.

        :return: A list of vulnerability objects with the identified vulns
                 (if any).
        '''
        if allow_origin == '*':
            msg = 'The remote Web application, specifically "%s", returned' \
                  ' an %s header with the value set to "*" which is insecure'\
                  ' and leaves the application open to Cross-domain attacks.'
            msg = msg % (forged_req.get_url(), ACCESS_CONTROL_ALLOW_ORIGIN)

            v = Vuln('Access-Control-Allow-Origin set to "*"', msg,
                     severity.LOW, response.get_id(), self.get_name())

            v.set_url(forged_req.get_url())

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

            return self._filter_report('_universal_allow_counter',
                                       'universal allow-origin',
                                       severity.MEDIUM, [
                                           v,
                                       ])

        return []
Exemplo n.º 10
0
    def _SEARCH(self, domain_path):
        '''
        Test SEARCH method.
        '''
        content = "<?xml version='1.0'?>\r\n"
        content += "<g:searchrequest xmlns:g='DAV:'>\r\n"
        content += "<g:sql>\r\n"
        content += "Select 'DAV:displayname' from scope()\r\n"
        content += "</g:sql>\r\n"
        content += "</g:searchrequest>\r\n"

        res = self._uri_opener.SEARCH(domain_path, data=content)

        content_matches = '<a:response>' in res or '<a:status>' in res or \
            'xmlns:a="DAV:"' in res

        if content_matches and res.get_code() in xrange(200, 300):
            msg = 'Directory listing with HTTP SEARCH method was found at' \
                  'directory: "%s".' % domain_path

            v = Vuln('Insecure DAV configuration', msg, severity.MEDIUM,
                     res.id, self.get_name())

            v.set_url(res.get_url())
            v.set_method('SEARCH')

            self.kb_append(self, 'dav', v)
Exemplo n.º 11
0
    def _not_secure_over_https(self, request, response, cookie_obj,
                               cookie_header_value):
        '''
        Checks if a cookie that does NOT have a secure flag is sent over https.

        :param request: The http request object
        :param response: The http response object
        :param cookie_obj: The cookie object to analyze
        :param cookie_header_value: The cookie, as sent in the HTTP response
        :return: None
        '''
        # BUGBUG: See other reference in this file for http://bugs.python.org/issue1028088

        if response.get_url().get_protocol().lower() == 'https' and \
        not self.SECURE_RE.search(cookie_header_value):

            desc = 'A cookie without the secure flag was sent in an HTTPS' \
                   ' response at "%s". The secure flag prevents the browser' \
                   ' from sending a "secure" cookie over an insecure HTTP' \
                   ' channel, thus preventing potential session hijacking' \
                   ' attacks.'
            desc = desc % response.get_url()
            
            v = Vuln('Secure flag missing in HTTPS cookie', desc,
                     severity.HIGH, response.id, self.get_name())

            v.set_url(response.get_url())
            self._set_cookie_to_rep(v, cobj=cookie_obj)
            
            kb.kb.append(self, 'security', v)
Exemplo n.º 12
0
Arquivo: ssn.py Projeto: weisst/w3af
    def grep(self, request, response):
        '''
        Plugin entry point, find the SSN numbers.

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

        if response.is_text_or_html() and response.get_code() == 200 \
        and response.get_clear_text_body() is not None \
        and uri not in self._already_inspected:

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

            found_ssn, validated_ssn = self._find_SSN(
                response.get_clear_text_body())
            if validated_ssn:
                desc = 'The URL: "%s" possibly discloses a US Social Security'\
                       ' Number: "%s".'
                desc = desc % (uri, validated_ssn)
                v = Vuln('US Social Security Number disclosure', desc,
                         severity.LOW, response.id, self.get_name())
                v.set_uri(uri)

                v.add_to_highlight(found_ssn)
                self.kb_append_uniq(self, 'ssn', v, 'URL')
Exemplo n.º 13
0
        def write_vuln_to_kb(vulnty, url, funcs):
            vulndata = php_sca.KB_DATA[vulnty]
            for f in funcs:
                vuln_sev = vulndata['severity']
                desc = name = vulndata['name']

                v = Vuln(name, desc, vuln_sev, 1, 'PHP Static Code Analyzer')
                v.set_uri(url)
                v.set_var(f.vulnsources[0])

                args = list(vulndata['kb_key']) + [v]

                # TODO: Extract the method from the PHP code
                #     $_GET == GET
                #     $_POST == POST
                #     $_REQUEST == GET
                v.set_method('GET')

                # TODO: Extract all the other variables that are
                # present in the PHP file using the SCA
                v.set_dc(DataContainer())

                #
                # TODO: This needs to be checked! OS Commanding specific
                #       attributes.
                v['os'] = 'unix'
                v['separator'] = ''

                kb.kb.append(*args)
Exemplo n.º 14
0
    def _send_and_check(self, repo_url, repo_get_files, repo, domain_path):
        '''
        Check if a repository index exists in the domain_path.

        :return: None, everything is saved to the self.out_queue.
        '''
        http_response = self.http_get_and_parse(repo_url)

        if not is_404(http_response):

            filenames = repo_get_files(http_response.get_body())

            parsed_url_set = set()

            for filename in self._clean_filenames(filenames):
                test_url = domain_path.url_join(filename)
                if test_url not in self._analyzed_filenames:
                    parsed_url_set.add(test_url)
                    self._analyzed_filenames.add(filename)

            self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

            if parsed_url_set:
                desc = 'A %s was found at: "%s"; this could indicate that'\
                       ' a %s is accessible. You might be able to download'\
                       ' the Web application source code.'
                desc = desc % (repo, http_response.get_url(), repo)

                v = Vuln('Source code repository', desc, severity.MEDIUM,
                         http_response.id, self.get_name())
                v.set_url(http_response.get_url())

                kb.kb.append(self, repo, v)
                om.out.vulnerability(v.get_desc(), severity=v.get_severity())
Exemplo n.º 15
0
    def end(self):
        # If all URLs implement protection, don't report anything.
        if not self._vuln_count:
            return

        response_ids = [_id for _id in self._ids]

        # If none of the URLs implement protection, simply report
        # ONE vulnerability that says that.
        if self._total_count == self._vuln_count:
            desc = 'The whole target has no protection (X-Frame-Options'\
                  ' header) against Click-Jacking attacks'
        # If most of the URLs implement the protection but some
        # don't, report ONE vulnerability saying: "Most are protected,
        # but x, y are not.
        if self._total_count > self._vuln_count:
            desc = 'Some URLs have no protection (X-Frame-Options header) '\
                  'against Click-Jacking attacks. Among them:\n '\
                  ' '.join([str(url) + '\n' for url in self._vulns])

        v = Vuln('Click-Jacking vulnerability', desc, severity.MEDIUM,
                 response_ids, self.get_name())

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

        self._vulns.cleanup()
        self._ids.cleanup()
Exemplo n.º 16
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.
        '''
        uri = response.get_uri()
        if response.is_text_or_html() and uri not in self._already_inspected:

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

            for regex in self._regex_list:
                for m in regex.findall(response.get_body()):
                    user = m[0]

                    desc = 'The URL: "%s" contains a SVN versioning signature'\
                           ' with the username "%s".'
                    desc = desc % (uri, user)

                    v = Vuln('SVN user disclosure vulnerability', desc,
                             severity.LOW, response.id, self.get_name())

                    v.set_uri(uri)
                    v['user'] = user
                    v.add_to_highlight(user)

                    self.kb_append_uniq(self, 'users', v, 'URL')
Exemplo n.º 17
0
    def _ssl_cookie_via_http(self, request, response):
        '''
        Analyze if a cookie value, sent in a HTTPS request, is now used for
        identifying the user in an insecure page. Example:
            Login is done over SSL
            The rest of the page is HTTP
        '''
        if request.get_url().get_protocol().lower() == 'https':
            return
        
        for cookie in kb.kb.get('analyze_cookies', 'cookies'):
            if cookie.get_url().get_protocol().lower() == 'https' and \
            request.get_url().get_domain() == cookie.get_url().get_domain():
                
                # The cookie was sent using SSL, I'll check if the current
                # request, is using these values in the POSTDATA / QS / COOKIE
                for key in cookie['cookie-object'].keys():
                    
                    value = cookie['cookie-object'][key].value
                    
                    # This if is to create less false positives
                    if len(value) > 6 and value in request.dump():

                        desc = 'Cookie values that were set over HTTPS, are' \
                               ' then sent over an insecure channel in a' \
                               ' request to "%s".'
                        desc = desc % request.get_url()
                    
                        v = Vuln('Secure cookies over insecure channel', desc,
                                 severity.HIGH, response.id, self.get_name())

                        v.set_url(response.get_url())

                        self._set_cookie_to_rep(v, cobj=cookie['cookie-object'])
                        kb.kb.append(self, 'security', v)
Exemplo n.º 18
0
    def _http_only(self, request, response, cookie_obj,
                   cookie_header_value, fingerprinted):
        '''
        Verify if the cookie has the httpOnly parameter set

        Reference:
            http://www.owasp.org/index.php/HTTPOnly
            http://en.wikipedia.org/wiki/HTTP_cookie

        :param request: The http request object
        :param response: The http response object
        :param cookie_obj: The cookie object to analyze
        :param cookie_header_value: The cookie, as sent in the HTTP response
        :param fingerprinted: True if the cookie was fingerprinted
        :return: None
        '''
        if not self.HTTPONLY_RE.search(cookie_header_value):
            
            vuln_severity = severity.MEDIUM if fingerprinted else severity.LOW
            desc = 'A cookie without the HttpOnly flag was sent when requesting' \
                   ' "%s". The HttpOnly flag prevents potential intruders from' \
                   ' accessing the cookie value through Cross-Site Scripting' \
                   ' attacks.'
            desc = desc % response.get_url()
            
            v = Vuln('Cookie without HttpOnly', desc,
                     vuln_severity, response.id, self.get_name())
            v.set_url(response.get_url())
            
            self._set_cookie_to_rep(v, cobj=cookie_obj)

            kb.kb.append(self, 'security', v)
Exemplo n.º 19
0
    def grep(self, request, response):
        '''
        Plugin entry point, search for the code disclosures.

        Unit tests are available at plugins/grep/tests.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        '''
        if response.is_text_or_html() and \
        response.get_url() not in self._already_added:

            match, lang = is_source_file(response.get_body())

            if match:
                # Check also for 404
                if not is_404(response):
                    desc = 'The URL: "%s" has a %s code disclosure vulnerability.'
                    desc = desc % (response.get_url(), lang)

                    v = Vuln('Code disclosure vulnerability', desc,
                             severity.LOW, response.id, self.get_name())

                    v.set_url(response.get_url())
                    v.add_to_highlight(match.group())

                    self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
                    self._already_added.add(response.get_url())

                else:
                    self._first_404 = False

                    desc = 'The URL: "%s" has a %s code disclosure'\
                           ' vulnerability in the customized 404 script.'
                    desc = desc % (v.get_url(), lang)

                    v = Vuln('Code disclosure vulnerability in 404 page', desc,
                             severity.LOW, response.id, self.get_name())

                    v.set_url(response.get_url())
                    v.add_to_highlight(match.group())
                    self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
Exemplo n.º 20
0
    def _origin_echo(self, forged_req, url, origin, response, allow_origin,
                     allow_credentials_str, allow_methods):
        '''
        First check if the @allow_origin is set to the value we sent
        (@origin) and if the allow_credentials is set to True. If this test
        is successful (most important vulnerability) then do not check for
        the @allow_origin is set to the value we sent.

        :return: A list of vulnerability objects with the identified vulns
                 (if any).
        '''
        if allow_origin is not None:
            allow_origin = allow_origin.lower()

            allow_credentials = False
            if allow_credentials_str is not None:
                allow_credentials = 'true' in allow_credentials_str.lower()

            if origin in allow_origin:

                if allow_credentials:
                    sev = severity.HIGH
                    name = 'Insecure Access-Control-Allow-Origin with credentials'
                    msg = 'The remote Web application, specifically "%s", returned' \
                          ' an %s header with the value set to the value sent in the'\
                          ' request\'s Origin header and a %s header with the value'\
                          ' set to "true", which is insecure and leaves the'\
                          ' application open to Cross-domain attacks which can' \
                          ' affect logged-in users.'
                    msg = msg % (forged_req.get_url(),
                                 ACCESS_CONTROL_ALLOW_ORIGIN,
                                 ACCESS_CONTROL_ALLOW_CREDENTIALS)

                else:
                    sev = severity.LOW
                    name = 'Insecure Access-Control-Allow-Origin'
                    msg = 'The remote Web application, specifically "%s", returned' \
                          ' an %s header with the value set to the value sent in the'\
                          ' request\'s Origin header, which is insecure and leaves'\
                          ' the application open to Cross-domain attacks.'
                    msg = msg % (forged_req.get_url(),
                                 ACCESS_CONTROL_ALLOW_ORIGIN)

                v = Vuln(name, msg, sev, response.get_id(), self.get_name())
                v.set_url(forged_req.get_url())

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

                return self._filter_report('_origin_echo_counter',
                                           'origin echoed in allow-origin',
                                           severity.HIGH, [
                                               v,
                                           ])

        return []
Exemplo n.º 21
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)
Exemplo n.º 22
0
    def create_base_vuln(self):
        '''
        :return: A vulnerability with some preconfigured settings
        '''
        desc = 'This vulnerability was added to the knowledge-base by the'\
               ' user and represents a "%s" vulnerability.'
        desc = desc % self.get_vulnerability_name()

        v = Vuln('Manually added vulnerability', desc, severity.HIGH,
                 self.get_vuln_id(), 'manual')

        return v
Exemplo n.º 23
0
    def _parse_xssed_result(self, response):
        '''
        Parse the result from the xssed site and create the corresponding info
        objects.

        :return: Fuzzable requests pointing to the XSS (if any)
        '''
        html_body = response.get_body()

        if "<b>XSS:</b>" in html_body:
            #
            #   Work!
            #
            regex_many_vulns = re.findall(
                "<a href='(/mirror/\d*/)' target='_blank'>", html_body)
            for mirror_relative_link in regex_many_vulns:

                mirror_url = self._xssed_url.url_join(mirror_relative_link)
                xss_report_response = self._uri_opener.GET(mirror_url)
                matches = re.findall("URL:.+", xss_report_response.get_body())

                dxss = self._decode_xssed_url

                if self._fixed in xss_report_response.get_body():
                    vuln_severity = severity.LOW
                    desc = 'This script contained a XSS vulnerability: "%s".'
                    desc = desc % dxss(dxss(matches[0]))
                else:
                    vuln_severity = severity.HIGH
                    desc = 'According to xssed.com, this script contains a'\
                           ' XSS vulnerability: "%s".'
                    desc = desc % dxss(dxss(matches[0]))
                    
                v = Vuln('Potential XSS vulnerability', desc,
                         vuln_severity, response.id, self.get_name())

                v.set_url(mirror_url)

                kb.kb.append(self, 'xss', v)
                om.out.information(v.get_desc())

                #
                #   Add the fuzzable request, this is useful if I have the
                #   XSS plugin enabled because it will re-test this and
                #   possibly confirm the vulnerability
                #
                fuzzable_request_list = self._create_fuzzable_requests(
                    xss_report_response)
                return fuzzable_request_list
        else:
            #   Nothing to see here...
            om.out.debug('xssed_dot_com did not find any previously reported'
                         ' XSS vulnerabilities.')
Exemplo n.º 24
0
    def end(self):
        '''
        Perform global analysis for all vulnerabilities found.
        '''
        #Check if vulns has been found
        if self._total_count == 0:
            return

        #Parse vulns collection
        vuln_already_reported = []
        total_url_processed_count = len(self._urls)
        for vuln_store_item in self._vulns:
            for csp_directive_name, csp_vulns_list in vuln_store_item.csp_vulns.iteritems(
            ):
                for csp_vuln in csp_vulns_list:
                    #Check if the current vuln is common (shared) to several url processed
                    #and has been already reported
                    if csp_vuln.desc in vuln_already_reported:
                        continue
                    #Search for current vuln occurences in order to know if
                    #the vuln is common (shared) to several url processed
                    occurences = self._find_occurences(csp_vuln.desc)
                    v = None
                    if len(occurences) > 1:
                        #Shared vuln case
                        v = Vuln('CSP vulnerability',
                                 csp_vuln.desc, csp_vuln.severity, occurences,
                                 self.get_name())
                        vuln_already_reported.append(csp_vuln.desc)
                    else:
                        #Isolated vuln case
                        v = Vuln('CSP vulnerability', csp_vuln.desc,
                                 csp_vuln.severity, vuln_store_item.resp_id,
                                 self.get_name())
                    #Report vuln
                    self.kb_append(self, 'csp', v)

        #Cleanup
        self._urls.cleanup()
        self._vulns.cleanup()
Exemplo n.º 25
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())
Exemplo n.º 26
0
    def _brute_worker(self, url, combination):
        '''
        Try a user/password combination with HTTP basic authentication against
        a specific URL.

        :param url: A string representation of an URL
        :param combination: A tuple that contains (user,pass)
        '''
        # Remember that this worker is called from a thread which lives in a
        # threadpool. If the worker finds something, it has to let the rest know
        # and the way we do that is by setting self._found.
        #
        # If one thread sees that we already bruteforced the access, the rest will
        # simply no-op
        if not self._found or not self._stop_on_first:
            user, passwd = combination

            raw_values = "%s:%s" % (user, passwd)
            auth = 'Basic %s' % base64.b64encode(raw_values).strip()
            headers = Headers([('Authorization', auth)])

            try:
                response = self._uri_opener.GET(url,
                                                cache=False,
                                                grep=False,
                                                headers=headers)
            except w3afException, w3:
                msg = 'Exception while bruteforcing basic authentication,'\
                      ' error message: "%s".'
                om.out.debug(msg % w3)
            else:
                # GET was OK
                if response.get_code() != 401:
                    self._found = True

                    desc = 'Found authentication credentials to: "%s".'\
                           ' A valid user and password combination is: %s/%s .'
                    desc = desc % (url, user, passwd)
                    v = Vuln('Guessable credentials', desc, severity.HIGH,
                             response.id, self.get_name())
                    v.set_url(url)

                    v['user'] = user
                    v['pass'] = passwd
                    v['response'] = response

                    kb.kb.append(self, 'auth', v)
                    om.out.vulnerability(v.get_desc(),
                                         severity=v.get_severity())
Exemplo n.º 27
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())
Exemplo n.º 28
0
    def _secure_over_http(self, request, response, cookie_obj, cookie_header_value):
        '''
        Checks if a cookie marked as secure is sent over http.

        Reference:
            http://en.wikipedia.org/wiki/HTTP_cookie

        :param request: The http request object
        :param response: The http response object
        :param cookie_obj: The cookie object to analyze
        :param cookie_header_value: The cookie, as sent in the HTTP response
        :return: None
        '''
        # BUGBUG: http://bugs.python.org/issue1028088
        #
        # I workaround this issue by using the raw string from the HTTP
        # response instead of the parsed:
        #
        #        cookie_obj_str = cookie_obj.output(header='')
        #
        # Bug can be reproduced like this:
        # >>> import Cookie
        # >>> cookie_object = Cookie.SimpleCookie()
        # >>> cookie_object.load('a=b; secure; httponly')
        # >>> cookie_object.output(header='')
        # ' a=b'
        #
        # Note the missing secure/httponly in the output return

        # And now, the code:
        if self.SECURE_RE.search(cookie_header_value) and \
        response.get_url().get_protocol().lower() == 'http':
            
            desc = 'A cookie marked with the secure flag was sent over' \
                   ' an insecure channel (HTTP) when requesting the URL:'\
                   ' "%s", this usually means that the Web application was'\
                   ' designed to run over SSL and was deployed without'\
                   ' security or that the developer does not understand the'\
                   ' "secure" flag.'
            desc = desc % response.get_url()
            
            v = Vuln('Secure cookie over HTTP', desc,
                     severity.HIGH, response.id, self.get_name())

            v.set_url(response.get_url())

            self._set_cookie_to_rep(v, cobj=cookie_obj)

            kb.kb.append(self, 'security', v)
Exemplo n.º 29
0
    def _filter_report(self, counter, section, vuln_severity,
                       analysis_response):
        '''
        :param counter: A string representing the name of the attr to increment
                        when a vulnerability is found by the decorated method.

        :param section: A string with the section name to use in the
                        description when there are too many vulnerabilities of
                        this type.

        :param vuln_severity: One of the constants in the severity module.

        :param analysis_response: The vulnerability (if any) found by the
                                  analysis method.
        '''
        if len(analysis_response):

            counter_val = getattr(self, counter)

            if counter_val <= self.MAX_REPEATED_REPORTS:
                counter_val += 1
                setattr(self, counter, counter_val)
                return analysis_response
            else:
                if section not in self._reported_global:
                    self._reported_global.add(section)

                    response_id = analysis_response[0].get_id()
                    msg = 'More than %s URLs in the Web application under analysis' \
                          ' returned a CORS response that triggered the %s' \
                          ' detection. Given that this seems to be an issue' \
                          ' that affects all of the site URLs, the scanner will' \
                          ' not report any other specific vulnerabilities of this'\
                          ' type.'
                    msg = msg % (self.MAX_REPEATED_REPORTS, section)

                    v = Vuln('Multiple CORS misconfigurations', msg,
                             vuln_severity, response_id, self.get_name())

                    self.kb_append(self, 'cors_origin', v)
                    return [
                        v,
                    ]

        return []
Exemplo n.º 30
0
    def _universal_origin_allow_creds(self, forged_req, url, origin, response,
                                      allow_origin, allow_credentials_str,
                                      allow_methods):
        '''
        Quote: "The above example would fail if the header was wildcarded as:
        Access-Control-Allow-Origin: *.  Since the Access-Control-Allow-Origin
        explicitly mentions http://foo.example, the credential-cognizant content
        is returned to the invoking web content.  Note that in line 23, a
        further cookie is set."

        https://developer.mozilla.org/en-US/docs/HTTP_access_control

        This method detects this bad implementation, which this is not a vuln
        it might be interesting for the developers and/or security admins.

        :return: Any implementation errors (as vuln objects) that might be found.
        '''
        allow_credentials = False
        if allow_credentials_str is not None:
            allow_credentials = 'true' in allow_credentials_str.lower()

        if allow_credentials and allow_origin == '*':

            msg = 'The remote Web application, specifically "%s", returned' \
                  ' an %s header with the value set to "*"  and an %s header'\
                  ' with the value set to "true" which according to Mozilla\'s'\
                  ' documentation is invalid. This implementation error might'\
                  ' affect the application behavior.'
            msg = msg % (forged_req.get_url(), ACCESS_CONTROL_ALLOW_ORIGIN,
                         ACCESS_CONTROL_ALLOW_CREDENTIALS)

            v = Vuln('Incorrect withCredentials implementation', msg,
                     severity.INFORMATION, response.get_id(), self.get_name())
            v.set_url(forged_req.get_url())

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

            return self._filter_report(
                '_universal_origin_allow_creds_counter',
                'withCredentials CORS implementation error',
                severity.INFORMATION, [
                    v,
                ])

        return []