Skip to content

Commit

Permalink
Move definitions to "finding" classes as a way to enforce their decla…
Browse files Browse the repository at this point in the history
…ration
  • Loading branch information
devl00p committed Jul 14, 2024
1 parent 7e84c4c commit 6d939e3
Show file tree
Hide file tree
Showing 90 changed files with 2,516 additions and 1,648 deletions.
3 changes: 1 addition & 2 deletions tests/attack/test_mod_buster.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from unittest import mock
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock
from asyncio import Event, sleep

import httpx
import respx
import pytest

from wapitiCore.model import PayloadInfo
from wapitiCore.net import Request
from wapitiCore.net.crawler import AsyncCrawler
from wapitiCore.net.classes import CrawlerConfiguration
Expand Down
1 change: 0 additions & 1 deletion tests/attack/test_mod_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import pytest
import respx
import httpx
import asyncio

from wapitiCore.attack.attack import Parameter, ParameterSituation
from wapitiCore.net.classes import CrawlerConfiguration
Expand Down
1 change: 0 additions & 1 deletion tests/attack/test_mod_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from unittest.mock import AsyncMock

import httpx
import respx
import pytest

from wapitiCore.net.classes import CrawlerConfiguration
Expand Down
12 changes: 6 additions & 6 deletions tests/attack/test_mod_htp.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ async def test_finish_no_technologies():
request = Request("http://perdu.com/")
request.path_id = 1

with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_vuln_info", autospec=True) as mock_add_vuln_info, \
with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_info", autospec=True) as mock_add_info, \
patch.object(ModuleHtp, "_db", new_callable=PropertyMock) as mock_db:
crawler_configuration = CrawlerConfiguration(Request("http://perdu.com/"))
async with AsyncCrawler.with_configuration(crawler_configuration) as crawler:
Expand All @@ -171,7 +171,7 @@ async def test_finish_no_technologies():
await module_htp.finish()

mock_db.assert_called()
mock_add_vuln_info.assert_not_called()
mock_add_info.assert_not_called()


@pytest.mark.asyncio
Expand Down Expand Up @@ -201,7 +201,7 @@ async def async_magic():
pass

MagicMock.__await__ = lambda x: async_magic().__await__()
with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_vuln_info", autospec=True) as mock_add_vuln_info, \
with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_info", autospec=True) as mock_add_info, \
patch.object(ModuleHtp, "_db", new_callable=PropertyMock) as mock_db, \
patch.object(ModuleHtp, "_get_versions", return_value=versions):
crawler_configuration = CrawlerConfiguration(Request("http://perdu.com/"))
Expand All @@ -214,7 +214,7 @@ async def async_magic():

await module_htp.finish()

mock_add_vuln_info.assert_called_once_with(
mock_add_info.assert_called_once_with(
module_htp,
category="Fingerprint web server",
request=Request("http://perdu.com/"),
Expand Down Expand Up @@ -249,7 +249,7 @@ async def async_magic():
pass

MagicMock.__await__ = lambda x: async_magic().__await__()
with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_vuln_info", autospec=True) as mock_add_vuln_info, \
with patch("wapitiCore.attack.mod_htp.ModuleHtp.add_info", autospec=True) as mock_add_info, \
patch.object(ModuleHtp, "_db", new_callable=PropertyMock) as mock_db, \
patch.object(ModuleHtp, "_get_versions", return_value=versions):
crawler_configuration = CrawlerConfiguration(Request("http://perdu.com/"))
Expand All @@ -262,7 +262,7 @@ async def async_magic():

await module_htp.finish()

mock_add_vuln_info.assert_called_once_with(
mock_add_info.assert_called_once_with(
module_htp,
category="Fingerprint web server",
request=Request("http://perdu.com/"),
Expand Down
5 changes: 2 additions & 3 deletions tests/attack/test_mod_log4shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from tests import get_mock_open
from wapitiCore.attack.attack import VULN
from wapitiCore.attack.mod_log4shell import ModuleLog4Shell
from wapitiCore.definitions.log4shell import NAME
from wapitiCore.definitions.log4shell import Log4ShellFinding
from wapitiCore.language.vulnerability import CRITICAL_LEVEL
from wapitiCore.net.crawler import AsyncCrawler
from wapitiCore.net.classes import CrawlerConfiguration
Expand Down Expand Up @@ -156,12 +156,11 @@ async def mock_verify_dns(_header_uuid: str):
request_id=-1,
payload_type=VULN,
module="log4shell",
category=NAME,
finding_class=Log4ShellFinding,
level=CRITICAL_LEVEL,
request=request,
parameter="Header: payload",
info=f"URL {modified_request.url} seems vulnerable to Log4Shell attack by using the header Header",
wstg=["WSTG-INPV-11"],
response=page,
)

Expand Down
7 changes: 4 additions & 3 deletions tests/attack/test_mod_network_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from wapitiCore.attack.network_devices.mod_harbor import ModuleHarbor



@pytest.mark.asyncio
@respx.mock
async def test_no_net_device():
Expand Down Expand Up @@ -46,6 +45,7 @@ async def test_no_net_device():

assert not persister.add_payload.call_count


@pytest.mark.asyncio
@respx.mock
async def test_ubika_without_version():
Expand Down Expand Up @@ -473,7 +473,8 @@ async def test_raise_on_request_error():
)
)

respx.get(url__regex=r"http://perdu.com/.*").mock(side_effect=RequestError("RequestError occurred: [Errno -2] Name or service not known"))
respx.get(url__regex=r"http://perdu.com/.*").mock(
side_effect=RequestError("RequestError occurred: [Errno -2] Name or service not known"))

persister = AsyncMock()

Expand Down Expand Up @@ -540,6 +541,7 @@ async def test_detect_harbor_with_version():
)
assert persister.add_payload.call_args_list[0][1]["module"] == "network_device"


@pytest.mark.asyncio
@respx.mock
async def test_detect_harbor_without_version():
Expand Down Expand Up @@ -591,7 +593,6 @@ async def test_detect_harbor_without_version():
@pytest.mark.asyncio
@respx.mock
async def test_detect_harbor_with_json_error():

respx.get("http://perdu.com/").mock(
return_value=httpx.Response(
200,
Expand Down
2 changes: 1 addition & 1 deletion tests/attack/test_mod_nikto.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from asyncio import Event, sleep
from asyncio import Event
from itertools import chain
from unittest.mock import AsyncMock

Expand Down
2 changes: 1 addition & 1 deletion tests/attack/test_mod_spring4shell.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import os
from asyncio import Event
from unittest.mock import patch, AsyncMock
from unittest.mock import AsyncMock
from httpx import Response as HttpxResponse

import pytest
Expand Down
2 changes: 1 addition & 1 deletion tests/attack/test_mod_sql.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from urllib.parse import urlparse, parse_qs
from tempfile import NamedTemporaryFile
import sqlite3
from asyncio import Event, sleep
from asyncio import Event
from unittest.mock import AsyncMock

import httpx
Expand Down
23 changes: 14 additions & 9 deletions tests/attack/test_mod_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
from wapitiCore.net import Request
from wapitiCore.language.vulnerability import CRITICAL_LEVEL, HIGH_LEVEL, INFO_LEVEL, MEDIUM_LEVEL
from wapitiCore.net.crawler import AsyncCrawler
from wapitiCore.attack.mod_ssl import ModuleSsl, NAME, extract_altnames, match_address, check_ocsp_must_staple, \
check_ev_certificate, process_vulnerabilities, process_bad_protocols
from wapitiCore.attack.mod_ssl import (
ModuleSsl, SslInformationFinding, SslVulnerabilityFinding, extract_altnames,
match_address, check_ocsp_must_staple, check_ev_certificate, process_vulnerabilities, process_bad_protocols
)


def https_server(cert_directory: str):
Expand Down Expand Up @@ -65,15 +67,18 @@ async def test_ssl_scanner():
# Depending on installed python/openssl version different vulnerabilities may be present but the following
# vulnerabilities and information should be there everytime

x = persister.add_payload.calls()
print(x)

persister.add_payload.assert_any_call(
request_id=-1,
payload_type="additional",
module="ssl",
category=NAME,
category=SslInformationFinding.name,
level=INFO_LEVEL,
request=request,
parameter='',
wstg=["WSTG-CRYP-01"],
wstg=['WSTG-CRYP-01'],
info="Certificate subject: yolo.com",
response=None
)
Expand All @@ -82,7 +87,7 @@ async def test_ssl_scanner():
request_id=-1,
payload_type="vulnerability",
module="ssl",
category=NAME,
category=SslVulnerabilityFinding.name,
level=CRITICAL_LEVEL,
request=request,
parameter='',
Expand All @@ -95,11 +100,11 @@ async def test_ssl_scanner():
request_id=-1,
payload_type="vulnerability",
module="ssl",
category=NAME,
category=SslVulnerabilityFinding.name,
level=HIGH_LEVEL,
request=request,
parameter='',
wstg=["WSTG-CRYP-01"],
wstg=['WSTG-CRYP-01'],
info="Strict Transport Security (HSTS) is not set",
response=None
)
Expand All @@ -108,11 +113,11 @@ async def test_ssl_scanner():
request_id=-1,
payload_type="vulnerability",
module="ssl",
category=NAME,
category=SslVulnerabilityFinding.name,
level=MEDIUM_LEVEL,
request=request,
parameter='',
wstg=["WSTG-CRYP-01"],
wstg=['WSTG-CRYP-01'],
info="Self-signed certificate detected: The certificate is not signed by a trusted Certificate Authority",
response=None
)
Expand Down
48 changes: 31 additions & 17 deletions wapitiCore/attack/attack.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@
from enum import Enum, Flag, auto
import random
from binascii import hexlify
from functools import partialmethod
from typing import Optional, Iterator, Tuple, List, Callable, Union, Iterable
from typing import Optional, Iterator, Tuple, List, Callable, Union, Iterable, Type
from asyncio import Event
import json

from pkg_resources import resource_filename
from httpx import ReadTimeout, RequestError

from wapitiCore.definitions import FindingBase
from wapitiCore.model import PayloadInfo
from wapitiCore.net.crawler import AsyncCrawler
from wapitiCore.net.classes import CrawlerConfiguration
Expand Down Expand Up @@ -246,34 +246,49 @@ def __init__(
# Must be left empty in the code
self.deps = []

async def add_payload(self, payload_type: str, category: str, request_id: int = -1,
async def add_payload(self, finding_class: Type[FindingBase], request_id: int = -1,
level: int = 0, request: Request = None, parameter: str = "", info: str = "",
wstg: Optional[List[str]] = None,
response: Response = None):

finding = finding_class()
await self.persister.add_payload(
request_id=request_id,
payload_type=payload_type,
payload_type=finding.type,
module=self.name,
category=category,
category=finding.name,
level=level,
request=request,
parameter=parameter,
info=info,
wstg=wstg,
wstg=finding.wstg_code,
response=response
)

add_vuln = partialmethod(add_payload, payload_type=VULN)
add_vuln_critical = partialmethod(add_payload, payload_type=VULN, level=CRITICAL_LEVEL)
add_vuln_high = partialmethod(add_payload, payload_type=VULN, level=HIGH_LEVEL)
add_vuln_medium = partialmethod(add_payload, payload_type=VULN, level=MEDIUM_LEVEL)
add_vuln_low = partialmethod(add_payload, payload_type=VULN, level=LOW_LEVEL)
add_vuln_info = partialmethod(add_payload, payload_type=VULN, level=INFO_LEVEL)
# Define explicit wrapper functions for each severity level
async def add_info(self, finding_class: Type[FindingBase], request_id: int = -1,
request: Optional[Request] = None, parameter: str = "",
info: str = "", response: Optional[Response] = None):
await self.add_payload(finding_class, request_id, INFO_LEVEL, request, parameter, info, response)

async def add_low(self, finding_class: Type[FindingBase], request_id: int = -1,
request: Optional[Request] = None, parameter: str = "",
info: str = "", response: Optional[Response] = None):
await self.add_payload(finding_class, request_id, LOW_LEVEL, request, parameter, info, response)

add_anom_high = partialmethod(add_payload, payload_type=ANOM, level=HIGH_LEVEL)
add_anom_medium = partialmethod(add_payload, payload_type=ANOM, level=MEDIUM_LEVEL)
async def add_medium(self, finding_class: Type[FindingBase], request_id: int = -1,
request: Optional[Request] = None, parameter: str = "",
info: str = "", response: Optional[Response] = None):
await self.add_payload(finding_class, request_id, MEDIUM_LEVEL, request, parameter, info, response)

add_addition = partialmethod(add_payload, payload_type=ADDITION, level=INFO_LEVEL)
async def add_high(self, finding_class: Type[FindingBase], request_id: int = -1,
request: Optional[Request] = None, parameter: str = "",
info: str = "", response: Optional[Response] = None):
await self.add_payload(finding_class, request_id, HIGH_LEVEL, request, parameter, info, response)

async def add_critical(self, finding_class: Type[FindingBase], request_id: int = -1,
request: Optional[Request] = None, parameter: str = "",
info: str = "", response: Optional[Response] = None):
await self.add_payload(finding_class, request_id, CRITICAL_LEVEL, request, parameter, info, response)

def load_require(self, dependencies: list = None):
self.deps = dependencies
Expand Down Expand Up @@ -508,7 +523,6 @@ def _mutate_urlencoded_multipart(
)
yield reverse_evil_req, reverse_parameter, reverse_payload_info


params_list[i][1] = saved_value

def _mutate_query_string(
Expand Down
14 changes: 6 additions & 8 deletions wapitiCore/attack/cms/mod_drupal_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from wapitiCore.net import Request
from wapitiCore.attack.cms.cms_common import CommonCMS, MSG_TECHNO_VERSIONED
from wapitiCore.net.response import Response
from wapitiCore.definitions.fingerprint_webapp import NAME as WEB_APP_VERSIONED, WSTG_CODE as WEB_WSTG_CODE
from wapitiCore.definitions.fingerprint import NAME as TECHNO_DETECTED, WSTG_CODE
from wapitiCore.definitions.fingerprint_webapp import SoftwareVersionDisclosureFinding
from wapitiCore.definitions.fingerprint import SoftwareNameDisclosureFinding
from wapitiCore.main.log import log_blue, logging

MSG_NO_DRUPAL = "No Drupal Detected"
Expand Down Expand Up @@ -87,17 +87,15 @@ async def attack(self, request: Request, response: Optional[Response] = None):
)

if self.versions:
await self.add_vuln_info(
category=WEB_APP_VERSIONED,
await self.add_info(
finding_class=SoftwareVersionDisclosureFinding,
request=request_to_root,
info=json.dumps(drupal_detected),
wstg=WEB_WSTG_CODE
)
await self.add_addition(
category=TECHNO_DETECTED,
await self.add_info(
finding_class=SoftwareNameDisclosureFinding,
request=request_to_root,
info=json.dumps(drupal_detected),
wstg=WSTG_CODE
)
else:
log_blue(MSG_NO_DRUPAL)
14 changes: 6 additions & 8 deletions wapitiCore/attack/cms/mod_joomla_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from wapitiCore.net import Request
from wapitiCore.attack.cms.cms_common import CommonCMS, MSG_TECHNO_VERSIONED
from wapitiCore.net.response import Response
from wapitiCore.definitions.fingerprint_webapp import NAME as WEB_APP_VERSIONED, WSTG_CODE as WEB_WSTG_CODE
from wapitiCore.definitions.fingerprint import NAME as TECHNO_DETECTED, WSTG_CODE
from wapitiCore.definitions.fingerprint_webapp import SoftwareVersionDisclosureFinding
from wapitiCore.definitions.fingerprint import SoftwareNameDisclosureFinding
from wapitiCore.main.log import log_blue

MSG_NO_JOOMLA = "No Joomla Detected"
Expand Down Expand Up @@ -86,17 +86,15 @@ async def attack(self, request: Request, response: Optional[Response] = None):
)

if self.versions:
await self.add_vuln_info(
category=WEB_APP_VERSIONED,
await self.add_info(
finding_class=SoftwareVersionDisclosureFinding,
request=request_to_root,
info=json.dumps(joomla_detected),
wstg=WEB_WSTG_CODE
)
await self.add_addition(
category=TECHNO_DETECTED,
await self.add_info(
finding_class=SoftwareNameDisclosureFinding,
request=request_to_root,
info=json.dumps(joomla_detected),
wstg=WSTG_CODE
)
else:
log_blue(MSG_NO_JOOMLA)
Loading

0 comments on commit 6d939e3

Please sign in to comment.