Skip to content

Commit

Permalink
Merge pull request #45 from Ostorlab/cve_2023_49897
Browse files Browse the repository at this point in the history
Implement CVE-2023-49897
  • Loading branch information
3asm authored Jan 22, 2024
2 parents 19289d9 + 40d2729 commit e2c3d66
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 0 deletions.
119 changes: 119 additions & 0 deletions agent/exploits/cve_2023_49897.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Agent Asteroid implementation for CVE-2023-49897."""
import re

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

VULNERABILITY_TITLE = 'FXC wireless LAN routers "AE1021PE" and "AE1021" vulnerable to OS command injection.'
VULNERABILITY_REFERENCE = "CVE-2023-49897"
VULNERABILITY_DESCRIPTION = (
"An OS command injection vulnerability exists in AE1021PE firmware version 2.0.9 and earlier "
"and AE1021 firmware version 2.0.9 and earlier. If this vulnerability is exploited, "
"an arbitrary OS command may be executed by an attacker who can log in to the product."
)

DEFAULT_TIMEOUT = 90
MAX_REDIRECTS = 2
UPPER_VULNERABLE_VERSION = version.parse("2.0.9")
HEADERS = {
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": "cookieno=489646; username=admin; password=admin",
}


@exploits_registry.register
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",
"password_input": "admin",
"lang": "ja_JP",
"hashstr": "202310281340",
"username": "admin",
"password": "admin",
}
try:
self.session.post(
target.origin + "/cgi-bin/login.apply",
headers=HEADERS,
data=data,
timeout=DEFAULT_TIMEOUT,
)
response = self.session.get(
target.origin + "/main_top.htm",
headers=HEADERS,
timeout=DEFAULT_TIMEOUT,
)
return response.status_code == 200 and "logout" in response.text
except requests_exceptions.RequestException:
return False

def check(self, target: definitions.Target) -> list[definitions.Vulnerability]:
try:
response = self.session.get(
target.origin + "/cgi-bin/runtime?system_status",
headers=HEADERS,
timeout=DEFAULT_TIMEOUT,
)
if response.status_code == 200:
pattern = re.compile(r"firmware_version=(\d+\.\d+\.\d+)")
match = pattern.search(response.text)
if match is not None:
firmware_version = version.parse(match.group(1))
if firmware_version <= UPPER_VULNERABLE_VERSION:
vulnerability = self._create_vulnerability(target)
return [vulnerability]
except requests.exceptions.RequestException:
return []
return []

def _create_vulnerability(
self, target: definitions.Target
) -> definitions.Vulnerability:
entry = kb.Entry(
title=VULNERABILITY_TITLE,
risk_rating="CRITICAL",
short_description=VULNERABILITY_DESCRIPTION,
description=VULNERABILITY_DESCRIPTION,
references={
"nvd.nist.gov": f"https://nvd.nist.gov/vuln/detail/{VULNERABILITY_REFERENCE}",
"akamai.com": "https://www.akamai.com/blog/security-research/zero-day-vulnerability"
"-spreading-mirai-patched",
"cisa.gov": "https://www.cisa.gov/news-events/ics-advisories/icsa-23-355-01",
},
recommendation=(
"- Make sure to install the latest security patches from software vendor. \n"
"- Update to the latest software version"
),
security_issue=True,
privacy_issue=False,
has_public_exploit=True,
targeted_by_malware=True,
targeted_by_ransomware=True,
targeted_by_nation_state=True,
)
technical_detail = (
f"{target.scheme}://{target.host}:{target.port} is vulnerable to {VULNERABILITY_REFERENCE}, "
f"{VULNERABILITY_TITLE}"
)
vulnerability = definitions.Vulnerability(
entry=entry,
technical_detail=technical_detail,
risk_rating=agent_report_vulnerability_mixin.RiskRating.CRITICAL,
)
return vulnerability
89 changes: 89 additions & 0 deletions tests/exploits/cve_2023_49897_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Unit tests for Agent Asteriod: CVE-2023_49897."""

import requests_mock as req_mock

from agent import definitions
from agent.exploits import cve_2023_49897

STATUS = """
system.general.uptime=6day:10h:31m:47s
model_number=XC
oemcode=XC
fw_information=WAP7011A-FLF-XC-2.0.10 (Beta, Build 10128.183-s7887)
version_runtime=s7887)
system.general.hardware.version=R01
bootcode_info=U-Boot_1.1.4_1.2
version_bootcode=1.2
firmware_version=2.0.9
system.general.model_name=AE1021PE
system.general.mode=bridge
wifi.general.CountryCode=JP
system.memory.total=62052
system.memory.free=23968
system.cpu.free=10
"""


def testCVE2023_49897_whenVulnerable_reportFinding(
requests_mock: req_mock.mocker.Mocker,
) -> None:
"""Ensure that the exploit reports findings when the application is vulnerable."""
exploit_instance = cve_2023_49897.CVE202349897Exploit()
url = "http://8.8.8.8:9200"
requests_mock.get(
url + "/main_top.htm",
status_code=200,
text="logout",
)
requests_mock.post(
url + "/cgi-bin/login.apply",
status_code=200,
)
requests_mock.get(
url + "/cgi-bin/runtime?system_status",
status_code=200,
text=STATUS,
)
target = definitions.Target("http", "8.8.8.8", 9200)

vulnerabilities = exploit_instance.check(target)

assert exploit_instance.accept(target) is True
vulnerability = vulnerabilities[0]
assert (
vulnerability.entry.title
== 'FXC wireless LAN routers "AE1021PE" and "AE1021" vulnerable to OS command injection.'
)
assert (
vulnerability.technical_detail
== "http://8.8.8.8:9200 is vulnerable to CVE-2023-49897, "
'FXC wireless LAN routers "AE1021PE" and "AE1021" vulnerable to OS command injection.'
)


def testCVE202349897_whenNotVulnerable_noFindingsReported(
requests_mock: req_mock.mocker.Mocker,
) -> None:
"""Ensure that there is no findings when the application is not vulnerable."""
exploit_instance = cve_2023_49897.CVE202349897Exploit()
url = "http://8.8.8.8:9200"
requests_mock.get(
url + "/main_top.htm",
status_code=302,
text="access",
)
requests_mock.post(
url + "/cgi-bin/login.apply",
status_code=302,
)
requests_mock.get(
url + "/cgi-bin/runtime?system_status",
status_code=401,
text="",
)
target = definitions.Target("http", "8.8.8.8", 9200)

vulnerabilities = exploit_instance.check(target)

assert exploit_instance.accept(target) is False
assert len(vulnerabilities) == 0

0 comments on commit e2c3d66

Please sign in to comment.