async def test_xss_uppercase_no_script():
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65081/uppercase_no_script.php?name=obiwan")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "name"
    used_payload = persister.add_payload.call_args_list[0][1]["request"].get_params[0][1].lower()
    assert used_payload.startswith("<svg onload=&")
    await crawler.close()
Example #2
0
async def test_redirect_detection():
    persister = AsyncMock()
    request = Request(
        "http://127.0.0.1:65080/open_redirect.php?yolo=nawak&url=toto")
    crawler = AsyncCrawler(Request("http://127.0.0.1:65080/"))
    options = {"timeout": 10, "level": 2}

    module = ModuleRedirect(crawler, persister, options, Event())
    await module.attack(request)

    assert persister.add_payload.call_args_list[0][1]["module"] == "redirect"
    assert persister.add_payload.call_args_list[0][1]["category"] == _(
        "Open Redirect")
    assert persister.add_payload.call_args_list[0][1][
        "request"].get_params == [['yolo', 'nawak'],
                                  ['url', 'https://openbugbounty.org/']]
    await crawler.close()
Example #3
0
async def test_false_positive():
    respx.get("http://perdu.com/").mock(return_value=httpx.Response(200, text="You have an error in your SQL syntax"))

    persister = AsyncMock()

    request = Request("http://perdu.com/?foo=bar")
    request.path_id = 1

    crawler = AsyncCrawler(Request("http://perdu.com/"), timeout=1)
    options = {"timeout": 10, "level": 1}

    module = ModuleSql(crawler, persister, options, Event())
    module.do_post = True
    await module.attack(request)

    assert not persister.add_payload.call_count
    await crawler.close()
Example #4
0
async def test_direct_param():
    # check for false positives too
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65084/xxe/direct/param.php?foo=bar&vuln=yolo")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65084/")
    options = {"timeout": 10, "level": 1}
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert len(persister.vulnerabilities)
    assert persister.vulnerabilities[0][0] == "vuln"
    await crawler.close()
Example #5
0
async def test_out_of_band_body():
    respx.route(host="127.0.0.1").pass_through()

    persister = FakePersister()
    request = Request("http://127.0.0.1:65084/xxe/outofband/body.php",
                      method="POST",
                      post_params=[["placeholder", "yolo"]])
    request.path_id = 42
    persister.requests.append(request)
    crawler = AsyncCrawler("http://127.0.0.1:65084/")
    options = {
        "timeout": 10,
        "level": 1,
        "external_endpoint": "http://wapiti3.ovh/",
        "internal_endpoint": "http://wapiti3.ovh/"
    }
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options, Event())

    respx.get(
        "http://wapiti3.ovh/get_xxe.php?session_id=" + module._session_id
    ).mock(return_value=httpx.Response(
        200,
        json={
            "42": {
                "72617720626f6479": [{
                    "date": "2019-08-17T16:52:41+00:00",
                    "url":
                    "https://wapiti3.ovh/xxe_data/yolo/3/72617720626f6479/31337-0-192.168.2.1.txt",
                    "ip": "192.168.2.1",
                    "size": 999,
                    "payload": "linux2"
                }]
            }
        }))

    module.do_post = False
    await module.attack(request)

    assert not persister.vulnerabilities
    await module.finish()
    assert persister.vulnerabilities
    assert persister.vulnerabilities[0][0] == "raw body"
    assert "linux2" in persister.vulnerabilities[0][1]
    await crawler.close()
Example #6
0
async def test_drop_cookies():
    respx.get("http://perdu.com/").mock(return_value=httpx.Response(
        200, text="Hello there!", headers={"Set-Cookie": "foo=bar; Path=/"}))

    def print_headers_callback(request):
        return httpx.Response(200, json=dict(request.headers))

    respx.get("http://perdu.com/cookies").mock(
        side_effect=print_headers_callback)

    crawler = AsyncCrawler(Request("http://perdu.com/"))
    crawler.drop_cookies = True
    response = await crawler.async_get(Request("http://perdu.com/"))
    assert "foo=bar" in response.headers["set-cookie"]
    response = await crawler.async_get(Request("http://perdu.com/cookies"))
    assert "foo=bar" not in response.content
    await crawler.close()
Example #7
0
def test_missing_value():
    req2 = Request("http://perdu.com/directory/?high=tone", )
    # Filename of the target URL should be injected but it is missing here, we should not raise a mutation
    mutator = Mutator(payloads=[("[FILE_NAME]::$DATA", Flags())])
    count = 0
    for __ in mutator.mutate(req2):
        count += 1
    assert count == 0
Example #8
0
async def test_out_of_band_param():
    respx.route(host="127.0.0.1").pass_through()

    persister = AsyncMock()
    request = Request(
        "http://127.0.0.1:65084/xxe/outofband/param.php?foo=bar&vuln=yolo")
    request.path_id = 7
    persister.get_path_by_id.return_value = request
    crawler = AsyncCrawler("http://127.0.0.1:65084/")
    options = {
        "timeout": 10,
        "level": 1,
        "external_endpoint": "http://wapiti3.ovh/",
        "internal_endpoint": "http://wapiti3.ovh/"
    }
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options, Event())

    respx.get(
        "http://wapiti3.ovh/get_xxe.php?session_id=" + module._session_id
    ).mock(return_value=httpx.Response(
        200,
        json={
            "7": {
                "76756c6e": [{
                    "date": "2019-08-17T16:52:41+00:00",
                    "url":
                    "https://wapiti3.ovh/xxe_data/yolo/7/76756c6e/31337-0-192.168.2.1.txt",
                    "ip": "192.168.2.1",
                    "size": 999,
                    "payload": "linux2"
                }]
            }
        }))

    module.do_post = False
    await module.attack(request)

    assert not persister.add_payload.call_count
    await module.finish()
    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "vuln"
    assert "linux2" in dict(persister.add_payload.call_args_list[0][1]
                            ["request"].get_params)["vuln"]
    await crawler.close()
Example #9
0
async def test_direct_param():
    # check for false positives too
    persister = AsyncMock()
    request = Request(
        "http://127.0.0.1:65084/xxe/direct/param.php?foo=bar&vuln=yolo")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65084/")
    options = {"timeout": 10, "level": 1}
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "vuln"
    await crawler.close()
Example #10
0
def test_direct_body():
    persister = FakePersister()
    request = Request("http://127.0.0.1:65084/xxe/direct/body.php",
                      method="POST",
                      post_params=[["placeholder", "yolo"]])
    request.path_id = 42
    crawler = Crawler("http://127.0.0.1:65084/")
    options = {"timeout": 10, "level": 1}
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options)

    module.attack(request)

    assert len(persister.vulnerabilities)
    assert persister.vulnerabilities[0][0] == "raw body"
    assert "/etc/passwd" in persister.vulnerabilities[0][1]
Example #11
0
def test_timeout():
    url = "http://127.0.0.1:65080/timeout.php"

    crawler = Crawler(url, timeout=1)
    request = Request(url)

    with pytest.raises(ReadTimeout):
        crawler.send(request)
async def test_attr_quote_escape():
    # We should succeed at closing the attribute value and the opening tag
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65081/attr_quote_escape.php?class=custom")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "class"
    assert persister.add_payload.call_args_list[0][1]["request"].get_params[0][1].lower().startswith("'></pre>")
    await crawler.close()
async def test_script_filter_bypass():
    # We should succeed at bypass the <script filter
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65081/script_tag_filter.php?name=kenobi")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "name"
    assert persister.add_payload.call_args_list[0][1]["request"].get_params[0][1].lower().startswith("<svg")
    await crawler.close()
async def test_frame_src_no_escape():
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65081/frame_src_no_escape.php?url=https://wapiti.sourceforge.io/")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "url"
    used_payload = persister.add_payload.call_args_list[0][1]["request"].get_params[0][1].lower()
    assert used_payload.startswith("javascript:alert(/w")
    await crawler.close()
Example #15
0
def test_bad_separator_used():
    persister = FakePersister()
    request = Request("http://127.0.0.1:65081/confuse_separator.php?number=42")
    request.path_id = 42
    persister.requests.append(request)
    crawler = Crawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options)
    module.do_post = False
    for __ in module.attack():
        pass

    assert persister.vulnerabilities
    used_payload = persister.vulnerabilities[0][1].lower()
    assert used_payload.startswith("\">")
Example #16
0
async def test_warning_false_positive():
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65085/inclusion.php?yolo=warn&f=toto")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65085/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_file(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count == 1
    assert [
        "f", "/etc/services"
    ] in persister.add_payload.call_args_list[0][1]["request"].get_params
    await crawler.close()
Example #17
0
def test_escape_with_style():
    persister = FakePersister()
    request = Request("http://127.0.0.1:65081/escape_with_style.php?color=green")
    request.path_id = 42
    persister.requests.append(request)
    crawler = Crawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options)
    module.do_post = False
    for __ in module.attack():
        pass

    assert persister.vulnerabilities
    used_payload = persister.vulnerabilities[0][1].lower()
    assert used_payload.startswith("</style>")
Example #18
0
async def test_inclusion_detection():
    # Will also test false positive detection
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65085/inclusion.php?yolo=nawak&f=toto")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65085/")
    options = {"timeout": 10, "level": 2}

    module = ModuleFile(crawler, persister, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count == 1
    assert persister.add_payload.call_args_list[0][1]["module"] == "file"
    assert persister.add_payload.call_args_list[0][1]["category"] == _("Path Traversal")
    assert ["f", "/etc/services"] in persister.add_payload.call_args_list[0][1]["request"].get_params
    await crawler.close()
async def test_xss_inside_tag_link():
    persister = AsyncMock()
    request = Request("http://127.0.0.1:65081/link_href_strip_tags.php?url=http://perdu.com/")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.add_payload.call_count
    assert persister.add_payload.call_args_list[0][1]["parameter"] == "url"
    used_payload = persister.add_payload.call_args_list[0][1]["request"].get_params[0][1].lower()
    assert "<" not in used_payload and ">" not in used_payload and "autofocus href onfocus" in used_payload
    await crawler.close()
def test_attr_double_quote_escape():
    # We should succeed at closing the attribute value and the opening tag
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65081/attr_double_quote_escape.php?class=custom")
    request.path_id = 42
    crawler = Crawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options)
    module.do_post = False
    module.attack(request)

    assert persister.vulnerabilities
    assert persister.vulnerabilities[0][0] == "class"
    assert persister.vulnerabilities[0][1].lower().startswith("\"></pre>")
def test_title_false_positive():
    # We should fail at escaping the title tag and we should be aware of it
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65080/title_false_positive.php?title=yolo&fixed=yes")
    request.path_id = 42
    persister.requests.append(request)
    crawler = Crawler("http://127.0.0.1:65080/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options)
    module.do_post = False
    for __ in module.attack():
        pass

    assert persister.vulnerabilities == []
def test_script_filter_bypass():
    # We should succeed at bypass the <script filter
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65081/script_tag_filter.php?name=kenobi")
    request.path_id = 42
    crawler = Crawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options)
    module.do_post = False
    module.attack(request)

    assert persister.vulnerabilities
    assert persister.vulnerabilities[0][0] == "name"
    assert persister.vulnerabilities[0][1].lower().startswith("<svg")
Example #23
0
async def test_rare_tag_and_event():
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65081/filter_common_keywords.php?msg=test")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.vulnerabilities
    used_payload = persister.vulnerabilities[0][1].lower()
    assert used_payload.startswith("<custom\nchecked\nonpointerenter=")
    await crawler.close()
Example #24
0
async def test_xss_with_weak_csp():
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65081/weak_csp.php?content=Hello%20there")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.vulnerabilities
    assert _("Warning: Content-Security-Policy is present!"
             ) not in persister.vulnerabilities[0][2]
    await crawler.close()
Example #25
0
async def test_escape_with_style():
    persister = FakePersister()
    request = Request(
        "http://127.0.0.1:65081/escape_with_style.php?color=green")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.vulnerabilities
    used_payload = persister.vulnerabilities[0][1].lower()
    assert used_payload.startswith("</style>")
    await crawler.close()
Example #26
0
async def test_xss_inside_tag_input():
    persister = FakePersister()
    request = Request("http://127.0.0.1:65081/input_text_strip_tags.php?uid=5")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.vulnerabilities
    assert persister.vulnerabilities[0][0] == "uid"
    used_payload = persister.vulnerabilities[0][1].lower()
    assert "<" not in used_payload and ">" not in used_payload and "autofocus/onfocus" in used_payload
    await crawler.close()
Example #27
0
async def test_attr_escape():
    # We should succeed at closing the attribute value and the opening tag
    persister = FakePersister()
    request = Request("http://127.0.0.1:65081/attr_escape.php?state=checked")
    request.path_id = 42
    crawler = AsyncCrawler("http://127.0.0.1:65081/")
    options = {"timeout": 10, "level": 2}
    logger = Mock()

    module = mod_xss(crawler, persister, logger, options, Event())
    module.do_post = False
    await module.attack(request)

    assert persister.vulnerabilities
    assert persister.vulnerabilities[0][0] == "state"
    assert persister.vulnerabilities[0][1].lower().startswith("><script>")
    await crawler.close()
Example #28
0
def test_direct_param():
    # check for false positives too
    persister = FakePersister()
    request = Request("http://127.0.0.1:65084/xxe/direct/param.php?foo=bar&vuln=yolo")
    request.path_id = 42
    persister.requests.append(request)
    crawler = Crawler("http://127.0.0.1:65084/")
    options = {"timeout": 10, "level": 1}
    logger = Mock()

    module = mod_xxe(crawler, persister, logger, options)
    module.do_post = False
    for __ in module.attack():
        pass

    assert len(persister.vulnerabilities)
    assert persister.vulnerabilities[0][0] == "vuln"
Example #29
0
async def test_cookie_dump():
    with NamedTemporaryFile() as json_fd:
        json_cookie = JsonCookie()
        json_cookie.load(json_fd.name)
        json_cookie.delete("httpbin.org")

        url = "http://httpbin.org/welcome/"
        respx.get(url).mock(return_value=httpx.Response(
            200,
            headers=[(
                "set-cookie",
                "foo=bar; Path=/"), ("set-cookie",
                                     "dead=beef; Path=/welcome/")]))

        crawler = AsyncCrawler(Request(url))
        await crawler.async_get(Request(url))

        json_cookie.addcookies(crawler.session_cookies)

        await crawler.close()
        json_cookie.dump()

        data = json.load(open(json_fd.name))
        assert data == {
            '.httpbin.org': {
                '/': {
                    'foo': {
                        'expires': None,
                        'port': None,
                        'secure': False,
                        'value': 'bar',
                        'version': 0
                    }
                },
                '/welcome/': {
                    'dead': {
                        'expires': None,
                        'port': None,
                        'secure': False,
                        'value': 'beef',
                        'version': 0
                    }
                }
            }
        }
Example #30
0
    async def attack(self, request: Request):
        root_url = await self.persister.get_root_url()

        if request.url == root_url:
            vsphere_url = request.url + ("" if request.url.endswith("/") else
                                         "/") + self.VSPHERE_URL
            vsphere_request = Request(path=vsphere_url,
                                      method=request.method,
                                      referer=request.referer,
                                      link_depth=request.link_depth)
            await self.attack_vsphere_url(vsphere_request)
        await self.attack_vsphere_url(request)
        headers = await self.read_headers()

        batch_malicious_headers, headers_uuid_record = self._get_batch_malicious_headers(
            headers)

        for malicious_headers in batch_malicious_headers:
            modified_request = Request(request.url)

            try:
                await self.crawler.async_send(modified_request,
                                              malicious_headers,
                                              follow_redirects=True)
            except RequestError:
                self.network_errors += 1
                continue
            await self._verify_headers_vulnerability(modified_request,
                                                     malicious_headers,
                                                     headers_uuid_record)

        injected_get_and_post_requests: Iterable[Tuple[
            Request, str, uuid.UUID]] = itertools.chain(
                self._inject_payload(request, request.get_params),
                self._inject_payload(request, request.post_params))

        for malicious_request, param_name, param_uuid in injected_get_and_post_requests:
            try:
                await self.crawler.async_send(malicious_request,
                                              follow_redirects=True)
            except RequestError:
                self.network_errors += 1
                continue
            await self._verify_param_vulnerability(malicious_request,
                                                   param_uuid, param_name)