Skip to content

Commit df5c33d

Browse files
committed
Add docstring , add test for parse_advisory_data.
Signed-off-by: Ziad <ziadhany2016@gmail.com>
1 parent b957915 commit df5c33d

6 files changed

Lines changed: 220 additions & 17 deletions

File tree

vulnerabilities/importers/fireeye.py

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
99
import os
10+
import re
1011
from typing import Iterable
1112

1213
from fetchcode.vcs.git import fetch_via_git
@@ -16,10 +17,11 @@
1617
from vulnerabilities.importer import Reference
1718
from vulnerabilities.importer import logger
1819
from vulnerabilities.utils import build_description
20+
from vulnerabilities.utils import dedupe
1921

2022

2123
class FireyeImporter(Importer):
22-
spdx_license_expression = ""
24+
spdx_license_expression = "unknown"
2325
license_url = ""
2426
url = "git+https://github.com/mandiant/Vulnerability-Disclosures"
2527

@@ -43,31 +45,37 @@ def fork_and_get_dir(url) -> dict:
4345

4446

4547
def get_files(fork_directory):
46-
for root, _, files in os.walk(fork_directory):
47-
if root in [".git"]:
48+
for root_dir in os.listdir(fork_directory):
49+
if root_dir in ("README.md", ".git"):
4850
continue
49-
for file in files:
50-
if file.endswith(".md") and not file == "README.md":
51-
with open(os.path.join(root, file), "r", encoding="ISO-8859-1") as f:
52-
print(file)
53-
yield f.read()
51+
52+
for root, _, files in os.walk(os.path.join(fork_directory, root_dir)):
53+
for file in files:
54+
if file.endswith((".md", ".MD")):
55+
with open(os.path.join(root, file), "r", encoding="ISO-8859-1") as f:
56+
print(file)
57+
yield f.read()
5458

5559

5660
def parse_advisory_data(raw_data) -> AdvisoryData:
61+
"""
62+
Parse a fireeye advisory repo and return an AdvisoryData or None.
63+
These files are in Markdown format.
64+
"""
5765
raw_data = raw_data.replace("\n\n", "\n")
5866
md_list = raw_data.split("\n")
5967
md_dict = md_list_to_dict(md_list)
6068

6169
database_id = md_list[0][1::]
6270
summary = md_dict.get(database_id[1::]) or []
6371
description = md_dict.get("## Description") or []
64-
impact = md_dict.get("## Impact")
65-
exploit_ability = md_dict.get("## Exploitability")
72+
impact = md_dict.get("## Impact") # not used but can be to get severity
73+
exploit_ability = md_dict.get("## Exploitability") # not used but can be to get severity
6674
cve_ref = md_dict.get("## CVE Reference") or []
67-
tech_details = md_dict.get("## Technical Details")
68-
resolution = md_dict.get("## Resolution")
69-
disc_credits = md_dict.get("## Discovery Credits")
70-
disc_timeline = md_dict.get("## Disclosure Timeline")
75+
tech_details = md_dict.get("## Technical Details") # not used
76+
resolution = md_dict.get("## Resolution") # not used
77+
disc_credits = md_dict.get("## Discovery Credits") # not used
78+
disc_timeline = md_dict.get("## Disclosure Timeline") # not used
7179
references = md_dict.get("## References") or []
7280

7381
return AdvisoryData(
@@ -79,15 +87,67 @@ def parse_advisory_data(raw_data) -> AdvisoryData:
7987

8088

8189
def get_references(references):
82-
return [Reference(url=ref[2::]) for ref in references if ref]
90+
"""
91+
Args:
92+
references:
93+
a list of references ( urls ) in md format
94+
Returns:
95+
a list of Reference
96+
>>> get_references(["- http://1-4a.com/cgi-bin/alienform/af.cgi"])
97+
[Reference(reference_id='', url='http://1-4a.com/cgi-bin/alienform/af.cgi', severities=[])]
98+
>>> get_references(["- [Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)"])
99+
[Reference(reference_id='', url='https://www.cve.org/CVERecord?id=CVE-2021-42712', severities=[])]
100+
"""
101+
urls = []
102+
for ref in references:
103+
if ref.startswith("- "):
104+
urls.append(matcher_url(ref[2::]))
105+
else:
106+
urls.append(matcher_url(ref))
107+
108+
return [Reference(url=url) for url in urls if url]
109+
110+
111+
def matcher_url(ref) -> str:
112+
"""
113+
Args:
114+
ref: reference url in Markdown format
115+
Returns:
116+
url of reference markup
117+
"""
118+
markup_regex = "\[([^\[]+)]\(\s*(http[s]?://.+)\s*\)"
119+
matched_markup = re.findall(markup_regex, ref)
120+
if matched_markup:
121+
return matched_markup[0][1]
122+
else:
123+
return ref
83124

84125

85126
def get_aliases(database_id, cve_ref) -> []:
127+
"""
128+
Args:
129+
database_id: string of database id like
130+
cve_ref: list of CVEs
131+
132+
Returns:
133+
a list of aliases
134+
>>> get_aliases("MNDT-2021-0012",["CVE-2021-44207"])
135+
['CVE-2021-44207', 'MNDT-2021-0012']
136+
"""
86137
cve_ref.append(database_id)
87-
return cve_ref
138+
return dedupe(cve_ref)
88139

89140

90141
def md_list_to_dict(md_list):
142+
"""
143+
Args:
144+
md_list: a md file splited by \n
145+
Returns:
146+
a dictionary of md_list
147+
>>> md_list_to_dict(["# Header","hello" , "hello again" ,"# Header2"])
148+
{'# Header': ['hello', 'hello again'], '# Header2': []}
149+
150+
"""
91151
md_dict = {}
92152
md_key = ""
93153
for md_line in md_list:
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# FEYE-2019-0002
2+
## Description
3+
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.
4+
5+
## Impact
6+
High - Arbitrary Ring 0 code execution
7+
8+
## Exploitability
9+
Medium/Low - Driver must be loaded or attacker will require admin rights. Newer versions require admin callers.
10+
11+
## CVE Reference
12+
CVE-2019-7245
13+
14+
## Technical Details
15+
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.
16+
17+
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:
18+
19+
* 0xC0000081
20+
* 0xC0000082
21+
* 0xC0000083
22+
* 0x174
23+
* 0x175
24+
* 0x176
25+
26+
For exploitation details see the INFILTRATE presentation in the references.
27+
28+
## Resolution
29+
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)
30+
31+
## Discovery Credits
32+
Ryan Warns
33+
34+
## Disclosure Timeline
35+
- 2 February 2019 - Contacted vendor
36+
- 2 February 2019 - Vendor response, confirmation of issue
37+
- 25 July 2019 - Vendor confirmed fix
38+
- 6 August 2019 - Fixed version released
39+
40+
## References
41+
[Exploitation Details](https://downloads.immunityinc.com/infiltrate2019-slidepacks/ryan-warns-timothy-harrison-device-driver-debauchery-msr-madness/MSR_Madness_v2.9_INFILTRATE.pptx)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"aliases": [
3+
"CVE-2019-7245 ", " FEYE-2019-0002"
4+
],
5+
"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.",
6+
"affected_packages": [],
7+
"references": [],
8+
"date_published":""
9+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# FEYE-2020-0020
2+
## Description
3+
4+
Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`.
5+
6+
## Impact
7+
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.
8+
9+
## Exploitability
10+
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.
11+
12+
## CVE Reference
13+
CVE-2020-12878
14+
15+
## Technical Details
16+
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`.
17+
18+
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`.
19+
20+
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.
21+
22+
## Resolution
23+
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.
24+
25+
## Discovery Credits
26+
- Jake Valletta, FireEye Mandiant
27+
- Sam Sabetan, FireEye Mandiant
28+
29+
## Disclosure Timeline
30+
31+
- 13 February 2020 - Issue reported to vendor
32+
- 11 March 2020 - Issue confirmed by Digi International
33+
- 14 May 2020 - CVE reserved with MITRE
34+
- May 2020 - Digi Releases Patch
35+
- 17 February 2021 - FireEye Mandiant advisory published
36+
37+
## References
38+
39+
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878
40+
- https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html
41+
- https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"aliases": ["CVE-2020-12878", " FEYE-2020-0020"],
3+
"summary": "Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`.",
4+
"affected_packages": [],
5+
"references": [
6+
{
7+
"reference_id": "", "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878", "severities": []},
8+
{
9+
"reference_id": "", "url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html", "severities": []},
10+
{
11+
"reference_id": "", "url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html", "severities": []}],
12+
"date_published":""
13+
}

vulnerabilities/tests/test_fireeye.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@
66
# See https://github.com/nexB/vulnerablecode for support or download.
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
9+
import json
10+
import os
911
from unittest import TestCase
1012

1113
from vulnerabilities.importer import Reference
1214
from vulnerabilities.importers.fireeye import get_aliases
1315
from vulnerabilities.importers.fireeye import get_references
1416
from vulnerabilities.importers.fireeye import md_list_to_dict
17+
from vulnerabilities.importers.fireeye import parse_advisory_data
18+
from vulnerabilities.tests import util_tests
19+
20+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
21+
TEST_DATA = os.path.join(BASE_DIR, "test_data/fireeye")
1522

1623

1724
class TestFireeyeImporter(TestCase):
@@ -120,7 +127,20 @@ def test_get_ref(self):
120127
Reference(url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0934"),
121128
Reference(url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10948"),
122129
]
123-
130+
assert get_references(
131+
[
132+
"- [Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)",
133+
]
134+
) == [
135+
Reference(url="https://www.cve.org/CVERecord?id=CVE-2021-42712"),
136+
]
137+
assert get_references(
138+
[
139+
"[Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)",
140+
]
141+
) == [
142+
Reference(url="https://www.cve.org/CVERecord?id=CVE-2021-42712"),
143+
]
124144
assert get_references([]) == []
125145

126146
def test_get_aliases(self):
@@ -129,3 +149,22 @@ def test_get_aliases(self):
129149
"MNDT-2021-0012",
130150
]
131151
assert get_aliases("MNDT-2021-0012", []) == ["MNDT-2021-0012"]
152+
153+
def test_parse_advisory_data_1(self):
154+
with open(os.path.join(TEST_DATA, "fireeye_test1.md")) as f:
155+
mock_response = f.read()
156+
expected_file = os.path.join(TEST_DATA, f"fireeye_test1_expect.json")
157+
158+
imported_data = parse_advisory_data(mock_response)
159+
result = imported_data.to_dict()
160+
161+
util_tests.check_results_against_json(result, expected_file)
162+
163+
def test_parse_advisory_data_2(self):
164+
with open(os.path.join(TEST_DATA, "fireeye_test2.md")) as f:
165+
mock_response = f.read()
166+
expected_file = os.path.join(TEST_DATA, f"fireeye_test2_expect.json")
167+
imported_data = parse_advisory_data(mock_response)
168+
result = imported_data.to_dict()
169+
170+
util_tests.check_results_against_json(result, expected_file)

0 commit comments

Comments
 (0)