예제 #1
0
class TestWAVSEPIdenticalPOST(WAVSEPTest):

    base_path = ('/wavsep/active/SQL-Injection/'
                 'SInjection-Detection-Evaluation-POST-200Identical/')

    target_url = get_wavsep_http(base_path)

    def test_found_sqli_wavsep_identical_post(self):
        expected_path_param = {
            (u'Case01-InjectionInView-Numeric-Blind-200ValidResponseWithDefaultOnException.jsp',
             u'transactionId'),
            (u'Case02-InjectionInView-String-Blind-200ValidResponseWithDefaultOnException.jsp',
             u'username'),
            (u'Case03-InjectionInView-Date-Blind-200ValidResponseWithDefaultOnException.jsp',
             u'transactionDate'),
            (u'Case04-InjectionInUpdate-Numeric-TimeDelayExploit-200Identical.jsp',
             u'transactionId'),
            (u'Case05-InjectionInUpdate-String-TimeDelayExploit-200Identical.jsp',
             u'description'),
            (u'Case06-InjectionInUpdate-Date-TimeDelayExploit-200Identical.jsp',
             u'transactionDate'),
            (u'Case07-InjectionInUpdate-NumericWithoutQuotes-TimeDelayExploit-200Identical.jsp',
             u'transactionId'),
            (u'Case08-InjectionInUpdate-DateWithoutQuotes-TimeDelayExploit-200Identical.jsp',
             u'transactionDate'),
        }

        # None is OK to miss -> 100% coverage
        ok_to_miss = set()
        skip_startwith = {'index.jsp'}
        kb_addresses = {('sqli', 'sqli'), ('blind_sqli', 'blind_sqli')}

        self._scan_assert(self.config, expected_path_param, ok_to_miss,
                          kb_addresses, skip_startwith)
예제 #2
0
    def test_xss_false_positive_1516(self):
        target_url = get_wavsep_http('/wavsep/active/Reflected-XSS/'
                                     'RXSS-Detection-Evaluation-GET/'
                                     'Case24-Js2ScriptTag.jsp?userinput=1234')
        self._scan(target_url, CONFIG)

        vulns = self.kb.get('blind_sqli', 'blind_sqli')
        self.assertEquals(0, len(vulns))
예제 #3
0
    def test_xss_false_positive_1516(self):
        target_url = get_wavsep_http('/wavsep/active/Reflected-XSS/'
                                     'RXSS-Detection-Evaluation-GET/'
                                     'Case24-Js2ScriptTag.jsp?userinput=1234')
        self._scan(target_url, CONFIG)

        vulns = self.kb.get('blind_sqli', 'blind_sqli')
        self.assertEquals(0, len(vulns))
예제 #4
0
class TestWAVSEPWithDifferentiationPOST(WAVSEPTest):

    base_path = ('/wavsep/active/SQL-Injection/'
                 'SInjection-Detection-Evaluation-POST-200Valid/')

    target_url = get_wavsep_http(base_path)

    def test_found_sqli_wavsep_differentiation_post(self):
        expected_path_param = {
            (u'Case01-InjectionInLogin-String-LoginBypass-WithDifferent200Responses.jsp',
             u'username'),
            (u'Case01-InjectionInLogin-String-LoginBypass-WithDifferent200Responses.jsp',
             u'password'),
            (u'Case02-InjectionInSearch-String-UnionExploit-WithDifferent200Responses.jsp',
             u'msg'),
            (u'Case03-InjectionInCalc-String-BooleanExploit-WithDifferent200Responses.jsp',
             u'username'),
            (u'Case04-InjectionInUpdate-String-CommandInjection-WithDifferent200Responses.jsp',
             u'msg'),
            (u'Case05-InjectionInSearchOrderBy-String-BinaryDeliberateRuntimeError-WithDifferent200Responses.jsp',
             u'orderby'),
            (u'Case06-InjectionInView-Numeric-PermissionBypass-WithDifferent200Responses.jsp',
             u'transactionId'),
            (u'Case07-InjectionInSearch-Numeric-UnionExploit-WithDifferent200Responses.jsp',
             u'msgId'),
            (u'Case08-InjectionInCalc-Numeric-BooleanExploit-WithDifferent200Responses.jsp',
             u'minBalanace'),
            (u'Case09-InjectionInUpdate-Numeric-CommandInjection-WithDifferent200Responses.jsp',
             u'msgid'),
            (u'Case10-InjectionInSearchOrderBy-Numeric-BinaryDeliberateRuntimeError-WithDifferent200Responses.jsp',
             u'orderby'),
            (u'Case11-InjectionInView-Date-PermissionBypass-WithDifferent200Responses.jsp',
             u'transactionDate'),
            (u'Case12-InjectionInSearch-Date-UnionExploit-WithDifferent200Responses.jsp',
             u'transactionDate'),
            (u'Case13-InjectionInCalc-Date-BooleanExploit-WithDifferent200Responses.jsp',
             u'transactionDate'),
            (u'Case14-InjectionInUpdate-Date-CommandInjection-WithDifferent200Responses.jsp',
             u'transactionDate'),
            (u'Case15-InjectionInSearch-DateWithoutQuotes-UnionExploit-WithDifferent200Responses.jsp',
             u'transactionDate'),
            (u'Case16-InjectionInView-NumericWithoutQuotes-PermissionBypass-WithDifferent200Responses.jsp',
             u'transactionId'),
            (u'Case17-InjectionInSearch-NumericWithoutQuotes-UnionExploit-WithDifferent200Responses.jsp',
             u'msgId'),
            (u'Case18-InjectionInCalc-NumericWithoutQuotes-BooleanExploit-WithDifferent200Responses.jsp',
             u'minBalanace'),
            (u'Case19-InjectionInUpdate-NumericWithoutQuotes-CommandInjection-WithDifferent200Responses.jsp',
             u'msgid'),
        }

        # None is OK to miss -> 100% coverage
        ok_to_miss = set()
        skip_startwith = {'index.jsp'}
        kb_addresses = {('sqli', 'sqli'), ('blind_sqli', 'blind_sqli')}

        self._scan_assert(self.config, expected_path_param, ok_to_miss,
                          kb_addresses, skip_startwith)
예제 #5
0
    def test_1557_random_number_of_results(self):
        """
        Pseudo-random number of vulnerabilities found in audit phase (xss)

        https://github.com/andresriancho/w3af/issues/1557
        """
        script = TEST_SCRIPT_1557 % (OUTPUT_PATH, get_wavsep_http())
        file(SCRIPT_PATH, "w").write(script)

        python_executable = sys.executable

        VULN_STRING = "A Cross Site Scripting vulnerability was found at"
        URL_VULN_RE = re.compile('%s: "(.*?)"' % VULN_STRING)
        all_previous_vulns = []

        loops = 2 if is_running_on_ci() else 10

        for i in xrange(loops):
            print("Start run #%s" % i)
            found_vulns = set()

            p = subprocess.Popen(
                [python_executable, "w3af_console", "-n", "-s", SCRIPT_PATH],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                stdin=subprocess.PIPE,
                shell=False,
                universal_newlines=True,
            )

            stdout, stderr = p.communicate()
            i_vuln_count = stdout.count(VULN_STRING)
            print("%s vulnerabilities found" % i_vuln_count)

            self.assertNotEqual(i_vuln_count, 0, stdout)

            for line in stdout.split("\n"):
                if VULN_STRING in line:
                    found_vulns.add(URL_VULN_RE.search(line).group(1))

            for previous_found in all_previous_vulns:
                self.assertEqual(found_vulns, previous_found)

            all_previous_vulns.append(found_vulns)
예제 #6
0
    def test_1557_random_number_of_results(self):
        """
        Pseudo-random number of vulnerabilities found in audit phase (xss)

        https://github.com/andresriancho/w3af/issues/1557
        """
        script = TEST_SCRIPT_1557 % (OUTPUT_PATH, get_wavsep_http())
        file(SCRIPT_PATH, 'w').write(script)

        python_executable = sys.executable

        VULN_STRING = 'A Cross Site Scripting vulnerability was found at'
        URL_VULN_RE = re.compile('%s: "(.*?)"' % VULN_STRING)
        all_previous_vulns = []

        loops = 2 if is_running_on_ci() else 10

        for i in xrange(loops):
            print('Start run #%s' % i)
            found_vulns = set()

            p = subprocess.Popen(
                [python_executable, 'w3af_console', '-n', '-s', SCRIPT_PATH],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                stdin=subprocess.PIPE,
                shell=False,
                universal_newlines=True)

            stdout, stderr = p.communicate()
            i_vuln_count = stdout.count(VULN_STRING)
            print('%s vulnerabilities found' % i_vuln_count)

            self.assertNotEqual(i_vuln_count, 0, stdout)

            for line in stdout.split('\n'):
                if VULN_STRING in line:
                    found_vulns.add(URL_VULN_RE.search(line).group(1))

            for previous_found in all_previous_vulns:
                self.assertEqual(found_vulns, previous_found)

            all_previous_vulns.append(found_vulns)
예제 #7
0
class TestWAVSEPExperimentalPOST(WAVSEPTest):

    base_path = ('/wavsep/active/SQL-Injection/'
                 'SInjection-Detection-Evaluation-POST-200Error-Experimental/')

    target_url = get_wavsep_http(base_path)

    def test_found_sqli_wavsep_experimental_post(self):
        expected_path_param = {
            (u'Case01-InjectionInInsertValues-String-BinaryDeliberateRuntimeError-With200Errors.jsp',
             u'target'),
            (u'Case01-InjectionInInsertValues-String-BinaryDeliberateRuntimeError-With200Errors.jsp',
             u'msg')
        }

        # None is OK to miss -> 100% coverage
        ok_to_miss = set()
        skip_startwith = {'index.jsp'}
        kb_addresses = {('sqli', 'sqli'), ('blind_sqli', 'blind_sqli')}

        self._scan_assert(self.config, expected_path_param, ok_to_miss,
                          kb_addresses, skip_startwith)
예제 #8
0
class TestXSS(PluginTest):

    XSS_PATH = get_moth_http('/audit/xss/')
    XSS_302_URL = 'http://moth/w3af/audit/xss/302/'
    XSS_URL_SMOKE = get_moth_http('/audit/xss/')

    WAVSEP_BASE = '/active/RXSS-Detection-Evaluation-GET/'
    WAVSEP_PATH = get_wavsep_http(WAVSEP_BASE)
    WAVSEP_2919 = get_wavsep_http('%sCase16-Js2ScriptSupportingProperty.jsp' %
                                  WAVSEP_BASE)

    _run_configs = {
        'cfg': {
            'target': None,
            'plugins': {
                'audit':
                (PluginConfig('xss',
                              ('persistent_xss', True, PluginConfig.BOOL)), ),
                'crawl':
                (PluginConfig('web_spider',
                              ('only_forward', True, PluginConfig.BOOL)), )
            },
        },
        'smoke': {
            'target': XSS_URL_SMOKE + 'simple_xss.py?text=1',
            'plugins': {
                'audit':
                (PluginConfig('xss',
                              ('persistent_xss', True, PluginConfig.BOOL)), ),
            },
        }
    }

    def normalize_kb_data(self, xss_vulns):
        """
        Take the XSS vulns as input and translate them into a list of tuples
        which contain:
            - Vulnerable URL
            - Vulnerable parameter
            - All parameters that were sent
        """
        kb_data = [(str(m.get_url()), m.get_token_name(),
                    tuple(sorted(m.get_dc().keys())))
                   for m in (xv.get_mutant() for xv in xss_vulns)]
        return kb_data

    def normalize_expected_data(self, target_url, expected):
        """
        Take a list with the expected vulnerabilities to be found  as input
        and translate them into a list of tuples which contain:
            - Vulnerable URL
            - Vulnerable parameter
            - All parameters that were sent
        """
        expected_data = [(target_url + e[0], e[1], tuple(sorted(e[2])))
                         for e in expected]
        return expected_data

    @attr('smoke')
    def test_find_one_xss(self):
        """
        Simplest possible test to verify that we identify XSSs.
        """
        cfg = self._run_configs['smoke']
        self._scan(cfg['target'], cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        EXPECTED = [
            ('simple_xss.py', 'text', ['text']),
        ]
        expected_data = self.normalize_expected_data(self.XSS_URL_SMOKE,
                                                     EXPECTED)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )

    def test_2919_javascript_src_frame(self):
        """
        https://github.com/andresriancho/w3af/issues/2919
        https://github.com/andresriancho/w3af/issues/1557
        """
        cfg = self._run_configs['smoke']
        self._scan(self.WAVSEP_2919 + '?userinput=1', cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        EXPECTED = [
            ('Case16-Js2ScriptSupportingProperty.jsp', 'userinput',
             ['userinput']),
        ]
        expected_data = self.normalize_expected_data(self.WAVSEP_PATH,
                                                     EXPECTED)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )

    def test_no_false_positive_499(self):
        """
        Avoiding false positives in the case where the payload is echoed back
        inside an attribute and the quotes are removed.
        
        :see: https://github.com/andresriancho/w3af/pull/499
        """
        cfg = self._run_configs['smoke']
        self._scan(self.XSS_PATH + '499_check.py?text=1', cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')

        self.assertEquals(0, len(xss_vulns), xss_vulns)

    def scan_file_upload_fuzz_files(self):
        cfg = self._run_configs['cfg']
        target_path = get_php_moth_http('/audit/file_upload/echo_content/')
        self._scan(target_path, cfg['plugins'])

    def test_user_configured_find_in_file_upload_content(self):
        """
        Do not send file content mutants unless the user configures it.
        https://github.com/andresriancho/w3af/issues/3149
        """
        # Set the value to False (True is the default)
        cf.save('fuzz_form_files', False)

        try:
            self.scan_file_upload_fuzz_files()
        finally:
            # Restore the default
            cf.save('fuzz_form_files', True)

        xss_vulns = self.kb.get('xss', 'xss')
        self.assertEqual(len(xss_vulns), 0, xss_vulns)

    def test_find_in_file_upload_content(self):
        """
        Find XSS in the content of an uploaded file
        https://github.com/andresriancho/w3af/issues/3149
        """
        self.scan_file_upload_fuzz_files()
        target_path = get_php_moth_http('/audit/file_upload/echo_content/')

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        EXPECTED = [
            ('txt_uploader.php', 'txt_file', ['txt_file']),
        ]
        expected_data = self.normalize_expected_data(target_path, EXPECTED)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )

    def test_found_xss(self):
        cfg = self._run_configs['cfg']
        self._scan(self.XSS_PATH, cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        expected = [
            # Trivial
            ('simple_xss.py', 'text', ['text']),

            # Form with GET method
            # https://github.com/andresriancho/w3af/issues/3149
            ('simple_xss_GET_form.py', 'text', ['text']),

            # Form with multipart enctype
            # https://github.com/andresriancho/w3af/issues/3149
            ('xss_multipart_form.py', 'text', ['text']),

            # Simple filters
            ('script_insensitive_blacklist_xss.py', 'text', ['text']),
            ('script_blacklist_xss.py', 'text', ['text']),

            # Simple encodings
            ('lower_str_xss.py', 'text', ['text']),

            # Forms with POST
            ('simple_xss_form.py', 'text', ['text']),
            ('two_inputs_form.py', 'address', ['address', 'name']),

            # Persistent XSS
            ('persistent_xss_form.py', 'text', ['text']),

            # XSS with CSP
            ('xss_with_safe_csp.py', 'text', ['text']),
            ('xss_with_weak_csp.py', 'text', ['text']),
        ]
        expected_data = self.normalize_expected_data(self.XSS_PATH, expected)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )

        # Now we want to verify that the vulnerability with safe CSP has lower
        # severity than the one with weak CSP
        csp_vulns = [v for v in xss_vulns if 'csp.py' in v.get_url()]
        self.assertEqual(len(csp_vulns), 2)

        severities = [v.get_severity() for v in csp_vulns]
        self.assertEqual(set(severities), {severity.MEDIUM, severity.LOW},
                         csp_vulns)

    @attr('ci_fails')
    def test_found_xss_with_redirect(self):
        cfg = self._run_configs['cfg']
        self._scan(self.XSS_302_URL, cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        expected = [('302.php', 'x', ('x', )), ('302.php', 'a', ('a', )),
                    ('printer.php', 'a', (
                        'a',
                        'added',
                    )), ('printer.php', 'added', (
                        'a',
                        'added',
                    )), ('printer.php', 'added', ('added', )),
                    ('printer.php', 'x', ('x', 'added')),
                    ('printer.php', 'added', ('x', 'added'))]
        expected_data = self.normalize_expected_data(self.XSS_302_URL,
                                                     expected)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )

    def test_found_wavsep_get_xss(self):
        cfg = self._run_configs['cfg']
        self._scan(self.WAVSEP_PATH, cfg['plugins'])

        xss_vulns = self.kb.get('xss', 'xss')
        kb_data = self.normalize_kb_data(xss_vulns)

        expected = [
            ('Case01-Tag2HtmlPageScope.jsp', 'userinput', ['userinput']),
            ('Case02-Tag2TagScope.jsp', 'userinput', ['userinput']),
            ('Case03-Tag2TagStructure.jsp', 'userinput', ['userinput']),
            ('Case04-Tag2HtmlComment.jsp', 'userinput', ['userinput']),
            ('Case05-Tag2Frameset.jsp', 'userinput', ['userinput']),
            ('Case06-Event2TagScope.jsp', 'userinput', ['userinput']),
            ('Case07-Event2DoubleQuotePropertyScope.jsp', 'userinput',
             ['userinput']),
            ('Case08-Event2SingleQuotePropertyScope.jsp', 'userinput',
             ['userinput']),
            ('Case09-SrcProperty2TagStructure.jsp', 'userinput', ['userinput'
                                                                  ]),
            ('Case10-Js2DoubleQuoteJsEventScope.jsp', 'userinput',
             ['userinput']),
            ('Case11-Js2SingleQuoteJsEventScope.jsp', 'userinput',
             ['userinput']),
            ('Case12-Js2JsEventScope.jsp', 'userinput', ['userinput']),
            #('Case13-Vbs2DoubleQuoteVbsEventScope.jsp', 'userinput', ['userinput']),
            #('Case14-Vbs2SingleQuoteVbsEventScope.jsp', 'userinput', ['userinput']),
            ('Case15-Vbs2VbsEventScope.jsp', 'userinput', ['userinput']),
            ('Case16-Js2ScriptSupportingProperty.jsp', 'userinput',
             ['userinput']),
            #('Case17-Js2PropertyJsScopeDoubleQuoteDelimiter.jsp', 'userinput', ['userinput']),
            #('Case18-Js2PropertyJsScopeSingleQuoteDelimiter.jsp', 'userinput', ['userinput']),
            #('Case19-Js2PropertyJsScope.jsp', 'userinput', ['userinput']),
            #('Case20-Vbs2PropertyVbsScopeDoubleQuoteDelimiter.jsp', 'userinput', ['userinput']),
            #('Case21-Vbs2PropertyVbsScope.jsp', 'userinput', ['userinput']),
            ('Case22-Js2ScriptTagDoubleQuoteDelimiter.jsp', 'userinput',
             ['userinput']),
            ('Case23-Js2ScriptTagSingleQuoteDelimiter.jsp', 'userinput',
             ['userinput']),
            ('Case24-Js2ScriptTag.jsp', 'userinput', ['userinput']),
            ('Case25-Vbs2ScriptTagDoubleQuoteDelimiter.jsp', 'userinput',
             ['userinput']),
            ('Case26-Vbs2ScriptTag.jsp', 'userinput', ['userinput']),
            ('Case27-Js2ScriptTagOLCommentScope.jsp', 'userinput',
             ['userinput']),
            ('Case28-Js2ScriptTagMLCommentScope.jsp', 'userinput',
             ['userinput']),
            ('Case29-Vbs2ScriptTagOLCommentScope.jsp', 'userinput',
             ['userinput']),
            ('Case30-Tag2HtmlPageScopeMultipleVulnerabilities.jsp',
             'userinput', ['userinput', 'userinput2']),
            ('Case30-Tag2HtmlPageScopeMultipleVulnerabilities.jsp',
             'userinput2', ['userinput', 'userinput2']),
            ('Case31-Tag2HtmlPageScopeDuringException.jsp', 'userinput',
             ['userinput']),
            ('Case32-Tag2HtmlPageScopeValidViewstateRequired.jsp', 'userinput',
             ['userinput', '__VIEWSTATE']),
        ]

        expected_data = self.normalize_expected_data(self.WAVSEP_PATH,
                                                     expected)

        self.assertEquals(
            set(expected_data),
            set(kb_data),
        )