Example #1
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)
Example #2
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())
Example #3
0
    def _analyze_html(self, request, response):
        '''
        Search for IP addresses in the HTML
        '''
        if not response.is_text_or_html():
            return

        # Performance improvement!
        if not (('10.' in response) or ('172.' in response) or
               ('192.168.' in response) or ('169.254.' in response)):
            return

        for regex in self._regex_list:
            for match in regex.findall(response.get_body()):
                match = match.strip()

                # Some proxy servers will return errors that include headers in the body
                # along with the client IP which we want to ignore
                if re.search("^.*X-Forwarded-For: .*%s" % match, response.get_body(), re.M):
                    continue

                # 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 and \
                not request.sent(match):
                    desc = 'The URL: "%s" returned an HTML document'\
                           ' 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, 'HTML', v)
Example #4
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)
Example #5
0
    def crawl(self, fuzzable_request):
        '''
        Plugin entry point, perform all the work.
        '''
        to_check = self._get_to_check(fuzzable_request.get_url())

        # I found some URLs, create fuzzable requests
        phishtank_matches = self._is_in_phishtank(to_check)
        for ptm in phishtank_matches:
            response = self._uri_opener.GET(ptm.url)
            for fr in self._create_fuzzable_requests(response):
                self.output_queue.put(fr)

        # Only create the vuln object once
        if phishtank_matches:
            desc = 'The URL: "%s" seems to be involved in a phishing scam.' \
                   ' Please see %s for more info.'
            desc = desc % (ptm.url, ptm.more_info_URL)
            
            v = Vuln('Phishing scam', desc, severity.MEDIUM, response.id,
                     self.get_name())
            v.set_url(ptm.url)
            
            kb.kb.append(self, 'phishtank', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())
Example #6
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()
Example #7
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)
Example #8
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')
Example #9
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)
Example #10
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)
Example #11
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()
Example #12
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)
Example #13
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)
Example #14
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)
Example #15
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
Example #16
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)
Example #17
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 []
Example #18
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 []
Example #19
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)
Example #20
0
    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")
Example #21
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.')

        return []
Example #22
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 []
Example #23
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)
Example #24
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)
Example #25
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 []
Example #26
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.')
Example #27
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)
Example #28
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())
Example #29
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
Example #30
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 = Vuln.from_mutant('TestCase', 'desc' * 30, 'High', 1,
                                'plugin_name', mutant)
        
        self.assertIsInstance(inst, Vuln)
        
        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())
Example #31
0
File: ldapi.py Project: weisst/w3af
    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):

            ldap_error_list = self._find_ldap_error(response)
            for ldap_error_string in ldap_error_list:
                if ldap_error_string not in mutant.get_original_response_body(
                ):

                    desc = 'LDAP injection was found at: %s' % mutant.found_at(
                    )

                    v = Vuln.from_mutant('LDAP injection vulnerability', desc,
                                         severity.HIGH, response.id,
                                         self.get_name(), mutant)

                    v.add_to_highlight(ldap_error_string)

                    self.kb_append_uniq(self, 'ldapi', v)
                    break
Example #32
0
File: csrf.py Project: weisst/w3af
    def audit(self, freq, orig_response):
        '''
        Tests a URL for CSRF vulnerabilities.

        :param freq: A FuzzableRequest
        '''
        if not self._is_suitable(freq):
            return

        # Referer/Origin check
        #
        # IMPORTANT NOTE: I'm aware that checking for the referer header does
        # NOT protect the application against all cases of CSRF, but it's a
        # very good first step. In order to exploit a CSRF in an application
        # that protects using this method an intruder would have to identify
        # other vulnerabilities such as XSS or open redirects.
        #
        # TODO: This algorithm has lots of room for improvement
        if self._is_origin_checked(freq, orig_response):
            om.out.debug('Origin for %s is checked' % freq.get_url())
            return

        # Does the request have CSRF token in query string or POST payload?
        if self._find_csrf_token(freq):
            om.out.debug('Token for %s exists and was checked' %
                         freq.get_url())
            return

        # Ok, we have found vulnerable to CSRF attack request
        msg = 'Cross Site Request Forgery has been found at: ' + freq.get_url()

        v = Vuln.from_fr('CSRF vulnerability', msg, severity.HIGH,
                         orig_response.id, self.get_name(), freq)

        self.kb_append_uniq(self, 'csrf', v)
Example #33
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()
Example #34
0
 def _report_vuln(self, mutant, response, mod_value):
     '''
     Create a Vuln object and store it in the KB.
     
     :return: None
     '''
     csp_protects = site_protected_against_xss_by_csp(response)
     vuln_severity = severity.LOW if csp_protects else severity.MEDIUM
     
     desc = 'A Cross Site Scripting vulnerability was found at: %s'
     desc = desc % mutant.found_at()
     
     if csp_protects:
         desc += 'The risk associated with this vulnerability was lowered'\
                 ' because the site correctly implements CSP. The'\
                 ' vulnerability is still a risk for the application since'\
                 ' only the latest versions of some browsers implement CSP'\
                 ' checking.'
     
     v = Vuln.from_mutant('Cross site scripting vulnerability', desc,
                          vuln_severity, response.id, self.get_name(),
                          mutant)
     v.add_to_highlight(mod_value) 
     
     self.kb_append_uniq(self, 'xss', v)
Example #35
0
    def _analyze_ips(self, ip_address_list, fuzzable_request):
        '''
        Search all IP addresses in Bing and determine if they have more than
        one domain hosted on it. Store findings in KB.
        '''
        bing_wrapper = bing(self._uri_opener)

        # This is the best way to search, one by one!
        for ip_address in ip_address_list:
            results = bing_wrapper.get_n_results('ip:' + ip_address,
                                                 self._result_limit)

            results = [r.URL.base_url() for r in results]
            results = list(set(results))

            # not vuln by default
            is_vulnerable = False

            if len(results) > 1:
                # We may have something...
                is_vulnerable = True

                if len(results) == 2:
                    # Maybe we have this case:
                    # [Mon 09 Jun 2008 01:08:26 PM ART] - http://216.244.147.14/
                    # [Mon 09 Jun 2008 01:08:26 PM ART] - http://www.business.com/
                    # Where www.business.com resolves to 216.244.147.14; so we don't really
                    # have more than one domain in the same server.
                    try:
                        res0 = socket.gethostbyname(results[0].get_domain())
                        res1 = socket.gethostbyname(results[1].get_domain())
                    except:
                        pass
                    else:
                        if res0 == res1:
                            is_vulnerable = False

            if is_vulnerable:
                desc = 'The web application under test seems to be in a shared' \
                       ' hosting. This list of domains, and the domain of the ' \
                       ' web application under test, all point to the same IP' \
                       ' address (%s):\n' % ip_address

                domain_list = kb.kb.raw_read(self, 'domains')

                for url in results:
                    domain = url.get_domain()
                    desc += '- %s\n' % domain

                    domain_list.append(domain)

                kb.kb.raw_write(self, 'domains', domain_list)

                v = Vuln.from_fr('Shared hosting', desc, severity.MEDIUM, 1,
                                 self.get_name(), fuzzable_request)

                v['also_in_hosting'] = results

                om.out.vulnerability(desc, severity=severity.MEDIUM)
                kb.kb.append(self, 'shared_hosting', v)
Example #36
0
    def is_injectable(self, mutant):
        '''
        Check if this mutant is delay injectable or not.

        @mutant: The mutant object that I have to inject to
        :return: A vulnerability object or None if nothing is found
        '''
        for delay_obj in self._get_delays():

            ed = ExactDelayController(mutant, delay_obj, self._uri_opener)
            success, responses = ed.delay_is_controlled()

            if success:
                # Now I can be sure that I found a vuln, we control the response
                # time with the delay
                desc = 'Blind SQL injection using time delays was found at: %s'
                desc = desc % mutant.found_at()

                response_ids = [r.id for r in responses]

                v = Vuln.from_mutant('Blind SQL injection vulnerability', desc,
                                     severity.HIGH, response_ids, 'blind_sqli',
                                     mutant)

                om.out.debug(v.get_desc())

                return v

        return None
Example #37
0
    def _with_time_delay(self, freq):
        '''
        Tests an URL for OS Commanding vulnerabilities using time delays.

        :param freq: A FuzzableRequest
        '''
        fake_mutants = create_mutants(freq, [
            '',
        ])

        for mutant in fake_mutants:

            if self._has_bug(mutant):
                continue

            for delay_obj in self._get_wait_commands():

                ed = ExactDelayController(mutant, delay_obj, self._uri_opener)
                success, responses = ed.delay_is_controlled()

                if success:
                    desc = 'OS Commanding was found at: %s' % mutant.found_at()

                    v = Vuln.from_mutant('OS commanding vulnerability', desc,
                                         severity.HIGH,
                                         [r.id for r in responses],
                                         self.get_name(), mutant)

                    v['os'] = delay_obj.get_OS()
                    v['separator'] = delay_obj.get_separator()

                    self.kb_append_uniq(self, 'os_commanding', v)
                    break
Example #38
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()
Example #39
0
    def _analyze_result(self, mutant, response):
        '''
        Analyze results of the _send_mutant method.
        '''
        if self._has_bug(mutant):
            return
        
        dom = response.get_dom()

        if response.is_text_or_html() and dom is not None:

            elem_list = self._tag_xpath(dom)

            for element in elem_list:

                if 'src' not in element.attrib:
                    return []

                src_attr = element.attrib['src']

                for url in self._test_urls:
                    if src_attr.startswith(url):
                        # Vuln vuln!
                        desc = 'A phishing vector was found at: %s'
                        desc = desc % mutant.found_at()
                        
                        v = Vuln.from_mutant('Phishing vector', desc,
                                             severity.LOW, response.id,
                                             self.get_name(), mutant)
                        
                        v.add_to_highlight(src_attr)
                        self.kb_append_uniq(self, 'phishing_vector', v)
Example #40
0
    def _confirm_file_upload(self, path, mutant, http_response):
        """
        Confirms if the file was uploaded to path

        :param path: The URL where we suspect that a file was uploaded to.
        :param mutant: The mutant that originated the file on the remote end
        :param http_response: The HTTP response asociated with sending mutant
        """
        get_response = self._uri_opener.GET(path, cache=False)

        if not is_404(get_response) and self._has_no_bug(mutant):
            # This is necessary, if I don't do this, the session
            # saver will break cause REAL file objects can't
            # be picked
            mutant.set_mod_value("<file_object>")

            desc = "A file upload to a directory inside the webroot" " was found at: %s" % mutant.found_at()

            v = Vuln.from_mutant(
                "Insecure file upload",
                desc,
                severity.HIGH,
                [http_response.id, get_response.id],
                self.get_name(),
                mutant,
            )

            v["file_dest"] = get_response.get_url()
            v["file_vars"] = mutant.get_file_vars()

            self.kb_append_uniq(self, "file_upload", v)
            return
Example #41
0
    def audit(self, freq, orig_response):
        '''
        Tests an URL for ReDoS vulnerabilities using time delays.

        :param freq: A FuzzableRequest
        '''
        if self.ignore_this_request(freq):
            return

        fake_mutants = create_mutants(freq, ['', ])

        for mutant in fake_mutants:
            for delay_obj in self.get_delays():
                
                adc = AproxDelayController(mutant, delay_obj, self._uri_opener,
                                           delay_setting=EXPONENTIALLY)
                success, responses = adc.delay_is_controlled()
    
                if success:
                    # Now I can be sure that I found a vuln, we control the
                    # response time with the delay
                    desc = 'ReDoS was found at: %s' % mutant.found_at()
                    
                    response_ids = [r.id for r in responses]
                    
                    v = Vuln.from_mutant('ReDoS vulnerability', desc,
                                         severity.MEDIUM, response_ids,
                                         self.get_name(), mutant)
                    
                    self.kb_append_uniq(self, 'redos', v)
                    break
Example #42
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
Example #43
0
    def _analyze_echo(self, mutant, response):
        '''
        Analyze results of the _send_mutant method that was sent in the _with_echo method.
        '''
        #
        #   I will only report the vulnerability once.
        #
        if self._has_no_bug(mutant):

            for file_pattern_match in self._multi_in.query(
                    response.get_body()):

                if file_pattern_match not in mutant.get_original_response_body(
                ):
                    # Search for the correct command and separator
                    sentOs, sentSeparator = self._get_os_separator(mutant)

                    desc = 'OS Commanding was found at: %s' % mutant.found_at()
                    # Create the vuln obj
                    v = Vuln.from_mutant('OS commanding vulnerability', desc,
                                         severity.HIGH, response.id,
                                         self.get_name(), mutant)

                    v['os'] = sentOs
                    v['separator'] = sentSeparator
                    v.add_to_highlight(file_pattern_match)

                    self.kb_append_uniq(self, 'os_commanding', v)
                    break
Example #44
0
    def _confirm_file_upload(self, path, mutant, http_response):
        '''
        Confirms if the file was uploaded to path

        :param path: The URL where we suspect that a file was uploaded to.
        :param mutant: The mutant that originated the file on the remote end
        :param http_response: The HTTP response asociated with sending mutant
        '''
        get_response = self._uri_opener.GET(path, cache=False)

        if not is_404(get_response) and self._has_no_bug(mutant):
            # This is necessary, if I don't do this, the session
            # saver will break cause REAL file objects can't
            # be picked
            mutant.set_mod_value('<file_object>')

            desc = 'A file upload to a directory inside the webroot' \
                   ' was found at: %s' % mutant.found_at()

            v = Vuln.from_mutant('Insecure file upload', desc, severity.HIGH,
                                 [http_response.id, get_response.id],
                                 self.get_name(), mutant)

            v['file_dest'] = get_response.get_url()
            v['file_vars'] = mutant.get_file_vars()

            self.kb_append_uniq(self, 'file_upload', v)
            return
Example #45
0
File: redos.py Project: weisst/w3af
    def audit(self, freq, orig_response):
        '''
        Tests an URL for ReDoS vulnerabilities using time delays.

        :param freq: A FuzzableRequest
        '''
        if self.ignore_this_request(freq):
            return

        fake_mutants = create_mutants(freq, [
            '',
        ])

        for mutant in fake_mutants:
            for delay_obj in self.get_delays():

                adc = AproxDelayController(mutant,
                                           delay_obj,
                                           self._uri_opener,
                                           delay_setting=EXPONENTIALLY)
                success, responses = adc.delay_is_controlled()

                if success:
                    # Now I can be sure that I found a vuln, we control the
                    # response time with the delay
                    desc = 'ReDoS was found at: %s' % mutant.found_at()

                    response_ids = [r.id for r in responses]

                    v = Vuln.from_mutant('ReDoS vulnerability', desc,
                                         severity.MEDIUM, response_ids,
                                         self.get_name(), mutant)

                    self.kb_append_uniq(self, 'redos', v)
                    break
Example #46
0
    def _with_time_delay(self, freq):
        '''
        Tests an URL for OS Commanding vulnerabilities using time delays.

        :param freq: A FuzzableRequest
        '''
        fake_mutants = create_mutants(freq, ['', ])

        for mutant in fake_mutants:

            if self._has_bug(mutant):
                continue

            for delay_obj in self._get_wait_commands():

                ed = ExactDelayController(mutant, delay_obj, self._uri_opener)
                success, responses = ed.delay_is_controlled()

                if success:
                    desc = 'OS Commanding was found at: %s' % mutant.found_at()
                                        
                    v = Vuln.from_mutant('OS commanding vulnerability', desc,
                                         severity.HIGH, [r.id for r in responses],
                                         self.get_name(), mutant)

                    v['os'] = delay_obj.get_OS()
                    v['separator'] = delay_obj.get_separator()

                    self.kb_append_uniq(self, 'os_commanding', v)
                    break
Example #47
0
    def _analyze_echo(self, mutant, response):
        '''
        Analyze results of the _send_mutant method that was sent in the _with_echo method.
        '''
        #
        #   I will only report the vulnerability once.
        #
        if self._has_no_bug(mutant):

            for file_pattern_match in self._multi_in.query(response.get_body()):

                if file_pattern_match not in mutant.get_original_response_body():
                    # Search for the correct command and separator
                    sentOs, sentSeparator = self._get_os_separator(mutant)

                    desc = 'OS Commanding was found at: %s' % mutant.found_at()
                    # Create the vuln obj
                    v = Vuln.from_mutant('OS commanding vulnerability', desc,
                                         severity.HIGH, response.id,
                                         self.get_name(), mutant)

                    v['os'] = sentOs
                    v['separator'] = sentSeparator
                    v.add_to_highlight(file_pattern_match)
                    
                    self.kb_append_uniq(self, 'os_commanding', v)
                    break
Example #48
0
    def _analyze_ips(self, ip_address_list, fuzzable_request):
        '''
        Search all IP addresses in Bing and determine if they have more than
        one domain hosted on it. Store findings in KB.
        '''
        bing_wrapper = bing(self._uri_opener)
        
        # This is the best way to search, one by one!
        for ip_address in ip_address_list:
            results = bing_wrapper.get_n_results('ip:' + ip_address,
                                               self._result_limit)

            results = [r.URL.base_url() for r in results]
            results = list(set(results))

            # not vuln by default
            is_vulnerable = False

            if len(results) > 1:
                # We may have something...
                is_vulnerable = True

                if len(results) == 2:
                    # Maybe we have this case:
                    # [Mon 09 Jun 2008 01:08:26 PM ART] - http://216.244.147.14/
                    # [Mon 09 Jun 2008 01:08:26 PM ART] - http://www.business.com/
                    # Where www.business.com resolves to 216.244.147.14; so we don't really
                    # have more than one domain in the same server.
                    try:
                        res0 = socket.gethostbyname(results[0].get_domain())
                        res1 = socket.gethostbyname(results[1].get_domain())
                    except:
                        pass
                    else:
                        if res0 == res1:
                            is_vulnerable = False

            if is_vulnerable:
                desc = 'The web application under test seems to be in a shared' \
                       ' hosting. This list of domains, and the domain of the ' \
                       ' web application under test, all point to the same IP' \
                       ' address (%s):\n' % ip_address
                
                domain_list = kb.kb.raw_read(self, 'domains')
                
                for url in results:
                    domain = url.get_domain()
                    desc += '- %s\n' % domain
                    
                    domain_list.append(domain)
                    
                kb.kb.raw_write(self, 'domains', domain_list)
                    
                v = Vuln.from_fr('Shared hosting', desc, severity.MEDIUM, 1,
                                 self.get_name(), fuzzable_request)

                v['also_in_hosting'] = results
                
                om.out.vulnerability(desc, severity=severity.MEDIUM)
                kb.kb.append(self, 'shared_hosting', v)
Example #49
0
File: rfi.py Project: weisst/w3af
    def _analyze_result(self, rfi_data, mutant, response):
        '''
        Analyze results of the _send_mutant method.
        '''
        if rfi_data.rfi_result in response:
            desc = 'A remote file inclusion vulnerability that allows remote' \
                  ' code execution was found at: %s' % mutant.found_at()

            v = Vuln.from_mutant('Remote code execution', desc, severity.HIGH,
                                 response.id, self.get_name(), mutant)

            self._vulns.append(v)

        elif rfi_data.rfi_result_part_1 in response \
        and rfi_data.rfi_result_part_2 in response:
            # This means that both parts ARE in the response body but the
            # rfi_data.rfi_result is NOT in it. In other words, the remote
            # content was embedded but not executed
            desc = 'A remote file inclusion vulnerability without code' \
                  ' execution was found at: %s' % mutant.found_at()

            v = Vuln.from_mutant('Remote file inclusion',
                                 desc, severity.MEDIUM, response.id,
                                 self.get_name(), mutant)

            self._vulns.append(v)

        else:
            #
            #   Analyze some errors that indicate that there is a RFI but
            #   with some "configuration problems"
            #
            for error in self.RFI_ERRORS:
                if error in response and not error in mutant.get_original_response_body(
                ):
                    desc = 'A potential remote file inclusion vulnerability' \
                           ' was identified by the means of application error' \
                           '  messages at: %s' % mutant.found_at()

                    v = Vuln.from_mutant('Potential remote file inclusion',
                                         desc, severity.LOW, response.id,
                                         self.get_name(), mutant)

                    v.add_to_highlight(error)
                    self._vulns.append(v)
                    break
Example #50
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)
Example #51
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 []
Example #52
0
    def _from_csv_get_vulns(self):
        file_vulns = []

        vuln_reader = csv.reader(open(self.OUTPUT_FILE, 'rb'),
                                 delimiter=',',
                                 quotechar='|',
                                 quoting=csv.QUOTE_MINIMAL)

        for name, method, uri, var, dc, _id, desc in vuln_reader:
            v = Vuln(name, desc, 'High', json.loads(_id), 'TestCase')
            v.set_method(method)
            v.set_uri(URL(uri))
            v.set_var(var)
            v.set_dc(dc)

            file_vulns.append(v)

        return file_vulns
Example #53
0
    def _parse_cookie(self, request, response, cookie_header_value):
        '''
        If the response sets more than one Cookie, this method will
        be called once for each "Set-Cookie" header.

        BUGBUG: The urllib2 library concatenates , values of repeated headers.
                See HTTPMessage.addheader() in httplib.py

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :param cookie_header_value: The cookie, as sent in the HTTP response

        :return: The cookie object or None if the parsing failed
        '''
        cookie_object = Cookie.SimpleCookie()
        
        # FIXME: Workaround for bug in Python's Cookie.py
        #
        # if type(rawdata) == type(""):
        #     self.__ParseString(rawdata)
        #
        # Should read "if isinstance(rawdata, basestring)"
        cookie_header_value = cookie_header_value.encode('utf-8')
        
        try:
            # Note to self: This line may print some chars to the console
            cookie_object.load(cookie_header_value)
        except Cookie.CookieError:
            desc = 'The remote Web application sent a cookie with an' \
                  ' incorrect format: "%s" that does NOT respect the RFC.'
            desc = desc % cookie_header_value
            
            i = Vuln('Invalid cookie', desc,
                     severity.HIGH, response.id, self.get_name())
            i.set_url(response.get_url())

            self._set_cookie_to_rep(i, cstr=cookie_header_value)

            # The cookie is invalid, this is worth mentioning ;)
            kb.kb.append(self, 'invalid-cookies', i)
            return None

        else:
            return cookie_object