Skip to content

Commit

Permalink
Merge pull request #44 from Ostorlab/exploit/CVE-2018-15133
Browse files Browse the repository at this point in the history
Add detection for: CVE-2018-15133
  • Loading branch information
BlueSquare1 authored Jan 22, 2024
2 parents bdf096e + 0cb9417 commit 19289d9
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
109 changes: 109 additions & 0 deletions agent/exploits/cve_2018_15133.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""Agent Asteroid implementation for CVE-2018-15133"""
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
from requests import exceptions as requests_exceptions

from agent import definitions
from agent import exploits_registry

VULNERABILITY_TITLE = "PHP Laravel Framework 5.5.40 / 5.6.x < 5.6.30 - token Unserialize Remote Command Execution"
VULNERABILITY_REFERENCE = "CVE-2018-15133"
VULNERABILITY_DESCRIPTION = (
"In Laravel Framework through 5.5.40 and 5.6.x through 5.6.29, remote code execution "
"might occur as a result of an unserialize call on a potentially untrusted X-XSRF-TOKEN "
"value. This involves the decrypt method in Illuminate/Encryption/Encrypter.php and "
"PendingBroadcast in gadgetchains/Laravel/RCE/3/chain.php in phpggc. The attacker must "
"know the application key, which normally would never occur, but could happen if the "
"attacker previously had privileged access or successfully accomplished a previous "
"attack."
)

DEFAULT_TIMEOUT = 30
TARGET_ENDPOINT = "/_ignition/execute-solution"
VERSION_PATTERN = re.compile(r"\"framework_version\":\"(.*?)\"")
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
class CVE201815133Exploit(definitions.Exploit):
"""
CVE-2018-15133: PHP Laravel Framework 5.5.40 / 5.6.x < 5.6.30 RCE
"""

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)
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(
urlparse.urljoin(target.origin, TARGET_ENDPOINT),
timeout=DEFAULT_TIMEOUT,
)
except requests_exceptions.RequestException:
return []

target_version_match = VERSION_PATTERN.search(resp.text)
if target_version_match is None:
return []

target_version = version.parse(target_version_match.group(1))
if (
target_version == STRICT_VULNERABLE_VERSION
or LOWER_VULNERABLE_VERSION <= target_version <= UPPER_VULNERABLE_VERSION
):
vulnerability = self._create_vulnerability(target)
return [vulnerability]

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}",
"exploit-db.com": "https://www.exploit-db.com/exploits/47129",
},
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.origin} 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
52 changes: 52 additions & 0 deletions tests/exploits/cve_2018_15133_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Unit tests for Agent Asteroid: CVE-2018-15133"""
import re

import requests_mock as req_mock

from agent import definitions
from agent.exploits import cve_2018_15133


def testCVE201815133_whenVulnerable_reportFinding(
requests_mock: req_mock.mocker.Mocker,
) -> None:
"""CVE-2018-15133 unit test: case when target is vulnerable."""
exploit_instance = cve_2018_15133.CVE201815133Exploit()
requests_mock.get(
re.compile("http://localhost:8089"),
text='{"report":{"notifier":"Laravel Client","language":"PHP","framework_version":"5.6.28"}',
status_code=405,
)
target = definitions.Target("http", "localhost", 8089)

exploit_instance.accept(target)
vulnerabilities = exploit_instance.check(target)

vulnerability = vulnerabilities[0]
assert vulnerability.entry.title == (
"PHP Laravel Framework 5.5.40 / 5.6.x < 5.6.30 - token Unserialize Remote "
"Command Execution"
)
assert vulnerability.technical_detail == (
"http://localhost:8089 is vulnerable to CVE-2018-15133, PHP Laravel "
"Framework 5.5.40 / 5.6.x < 5.6.30 - token Unserialize Remote Command "
"Execution"
)


def testCVE201815133_whenSafe_reportNothing(
requests_mock: req_mock.mocker.Mocker,
) -> None:
"""CVE-2018-15133 unit test: case when target is safe."""
exploit_instance = cve_2018_15133.CVE201815133Exploit()
requests_mock.get(
re.compile("http://localhost"),
text='{"report":{"notifier":"Laravel Client","language":"PHP","framework_version":"5.6.30"}',
status_code=405,
)
target = definitions.Target("http", "localhost", 8089)

exploit_instance.accept(target)
vulnerabilities = exploit_instance.check(target)

assert len(vulnerabilities) == 0

0 comments on commit 19289d9

Please sign in to comment.