Skip to content

Commit 43ee624

Browse files
committed
Migrate Fireeye importer to Advisory V2 , Add a test
Signed-off-by: ziad hany <[email protected]>
1 parent 33c24d4 commit 43ee624

File tree

8 files changed

+332
-35
lines changed

8 files changed

+332
-35
lines changed

vulnerabilities/pipelines/v2_importers/fireeye_importer_v2.py

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
from vulnerabilities.importer import AdvisoryData
1818
from vulnerabilities.importer import ReferenceV2
19+
from vulnerabilities.importer import VulnerabilitySeverity
1920
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
21+
from vulnerabilities.severity_systems import GENERIC
2022
from vulnerabilities.utils import build_description
2123
from vulnerabilities.utils import create_weaknesses_list
2224
from vulnerabilities.utils import cwe_regex
2325
from vulnerabilities.utils import dedupe
26+
from vulnerabilities.utils import get_advisory_url
2427

2528
logger = logging.getLogger(__name__)
2629

@@ -46,28 +49,33 @@ def steps(cls):
4649
)
4750

4851
def advisories_count(self):
49-
files = filter(
50-
lambda p: p.suffix in [".md", ".MD"], Path(self.vcs_response.dest_dir).glob("**/*")
52+
base_path = Path(self.vcs_response.dest_dir)
53+
return sum(
54+
1
55+
for p in base_path.glob("**/*")
56+
if p.suffix.lower() == ".md" or p.stem.upper() == "README"
5157
)
52-
return len(list(files))
5358

5459
def clone(self):
5560
self.log(f"Cloning `{self.repo_url}`")
5661
self.vcs_response = fetch_via_vcs(self.repo_url)
5762

5863
def collect_advisories(self) -> Iterable[AdvisoryData]:
5964
base_path = Path(self.vcs_response.dest_dir)
60-
files = filter(
61-
lambda p: p.suffix in [".md", ".MD"], Path(self.vcs_response.dest_dir).glob("**/*")
62-
)
63-
for file in files:
64-
if Path(file).stem == "README":
65+
for file_path in base_path.glob("**/*"):
66+
if file_path.suffix.lower() != ".md":
6567
continue
68+
69+
if file_path.stem.upper() == "README":
70+
continue
71+
6672
try:
67-
with open(file, encoding="utf-8-sig") as f:
68-
yield parse_advisory_data(raw_data=f.read(), file=file, base_path=base_path)
73+
with open(file_path, encoding="utf-8-sig") as f:
74+
yield parse_advisory_data(
75+
raw_data=f.read(), file_path=file_path, base_path=base_path
76+
)
6977
except UnicodeError:
70-
logger.error(f"Invalid file {file}")
78+
logger.error(f"Invalid File UnicodeError: {file_path}")
7179

7280
def clean_downloads(self):
7381
if self.vcs_response:
@@ -78,39 +86,39 @@ def on_failure(self):
7886
self.clean_downloads()
7987

8088

81-
def parse_advisory_data(raw_data, file, base_path) -> AdvisoryData:
89+
def parse_advisory_data(raw_data, file_path, base_path) -> AdvisoryData:
8290
"""
8391
Parse a fireeye advisory repo and return an AdvisoryData or None.
8492
These files are in Markdown format.
8593
"""
86-
relative_path = str(file.relative_to(base_path)).strip("/")
87-
advisory_url = (
88-
f"https://github.com/mandiant/Vulnerability-Disclosures/blob/master/{relative_path}"
89-
)
9094
raw_data = raw_data.replace("\n\n", "\n")
9195
md_list = raw_data.split("\n")
9296
md_dict = md_list_to_dict(md_list)
9397

9498
database_id = md_list[0][1::]
9599
summary = md_dict.get(database_id[1::]) or []
96100
description = md_dict.get("## Description") or []
97-
impact = md_dict.get("## Impact") # not used but can be used to get severity
98-
exploit_ability = md_dict.get("## Exploitability") # not used
101+
impact = md_dict.get("## Impact")
99102
cve_ref = md_dict.get("## CVE Reference") or []
100-
tech_details = md_dict.get("## Technical Details") # not used
101-
resolution = md_dict.get("## Resolution") # not used
102-
disc_credits = md_dict.get("## Discovery Credits") # not used
103-
disc_timeline = md_dict.get("## Disclosure Timeline") # not used
104103
references = md_dict.get("## References") or []
105104
cwe_data = md_dict.get("## Common Weakness Enumeration") or []
106105

106+
advisory_id = file_path.stem
107+
advisory_url = get_advisory_url(
108+
file=file_path,
109+
base_path=base_path,
110+
url="https://github.com/mandiant/Vulnerability-Disclosures/blob/master/",
111+
)
112+
107113
return AdvisoryData(
108-
advisory_id=base_path.stem,
114+
advisory_id=advisory_id,
109115
aliases=get_aliases(database_id, cve_ref),
110116
summary=build_description(" ".join(summary), " ".join(description)),
111117
references_v2=get_references(references),
118+
severities=get_severities(impact),
112119
weaknesses=get_weaknesses(cwe_data),
113120
url=advisory_url,
121+
original_advisory_text=raw_data,
114122
)
115123

116124

@@ -124,11 +132,11 @@ def get_references(references):
124132
"""
125133
urls = []
126134
for ref in references:
127-
if ref.startswith("- "):
128-
urls.append(matcher_url(ref[2::]))
129-
else:
130-
urls.append(matcher_url(ref))
131-
135+
clean_ref = ref.strip()
136+
clean_ref = clean_ref.lstrip("-* ")
137+
url = matcher_url(clean_ref)
138+
if url:
139+
urls.append(url)
132140
return [ReferenceV2(url=url) for url in urls if url]
133141

134142

@@ -150,7 +158,8 @@ def get_aliases(database_id, cve_ref) -> List:
150158
>>> get_aliases("MNDT-2021-0012", ["CVE-2021-44207"])
151159
['CVE-2021-44207', 'MNDT-2021-0012']
152160
"""
153-
cve_ref.append(database_id)
161+
cleaned_db_id = database_id.strip()
162+
cve_ref.append(cleaned_db_id)
154163
return dedupe(cve_ref)
155164

156165

@@ -174,12 +183,11 @@ def md_list_to_dict(md_list):
174183
def get_weaknesses(cwe_data):
175184
"""
176185
Return the list of CWE IDs as integers from a list of weakness summaries, e.g., [379].
177-
178-
>>> get_weaknesses([
179-
... "CWE-379: Creation of Temporary File in Directory with Insecure Permissions",
180-
... "CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')"
181-
... ])
182-
[379, 362]
186+
>>> get_weaknesses([
187+
... "CWE-379: Creation of Temporary File in Directory with Insecure Permissions",
188+
... "CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')"
189+
... ])
190+
[379, 362]
183191
"""
184192
cwe_list = []
185193
for line in cwe_data:
@@ -188,3 +196,37 @@ def get_weaknesses(cwe_data):
188196

189197
weaknesses = create_weaknesses_list(cwe_list)
190198
return weaknesses
199+
200+
201+
def get_severities(impact):
202+
"""
203+
Return a list of VulnerabilitySeverity extracted from the impact string.
204+
>>> get_severities([
205+
... "High - Arbitrary Ring 0 code execution",
206+
... ])
207+
[VulnerabilitySeverity(system="generic", value="high")]
208+
>>> get_severities([
209+
... "Low - The `ValidationKey` and `DecryptionKey` values would need to be obtained via a separate vulnerability or other channel."
210+
... ])
211+
[VulnerabilitySeverity(system="generic", value="low")]
212+
>>> get_severities([])
213+
[]
214+
"""
215+
if not impact:
216+
return []
217+
218+
impact_text = impact[0]
219+
value = ""
220+
if " - " in impact_text:
221+
value = impact_text.split(" - ")[0]
222+
elif ": " in impact_text:
223+
value = impact_text.split(": ")[0]
224+
else:
225+
parts = impact_text.split(" ")
226+
if parts:
227+
value = parts[0]
228+
229+
if not value.lower() in ["high", "medium", "low"]:
230+
return []
231+
232+
return [VulnerabilitySeverity(system=GENERIC, value=value)]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from pathlib import Path
11+
from unittest.mock import Mock
12+
from unittest.mock import patch
13+
14+
import pytest
15+
16+
from vulnerabilities.pipelines.v2_importers.fireeye_importer_v2 import FireeyeImporterPipeline
17+
from vulnerabilities.tests import util_tests
18+
19+
TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "fireeye_v2"
20+
21+
TEST_CVE_FILES = [
22+
TEST_DATA / "fireeye_test1.md",
23+
TEST_DATA / "fireeye_test2.md",
24+
TEST_DATA / "fireeye_test3.md",
25+
]
26+
27+
28+
@pytest.mark.django_db
29+
@pytest.mark.parametrize("md_file", TEST_CVE_FILES)
30+
def test_fireeye_advisories_per_file(md_file):
31+
pipeline = FireeyeImporterPipeline()
32+
pipeline.vcs_response = Mock(dest_dir=TEST_DATA)
33+
34+
with patch.object(Path, "glob", return_value=[md_file]):
35+
result = [adv.to_dict() for adv in pipeline.collect_advisories()]
36+
37+
expected_file = md_file.with_name(md_file.stem + "-expected.json")
38+
util_tests.check_results_against_json(result, expected_file)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[
2+
{
3+
"advisory_id": "fireeye_test1",
4+
"aliases": [
5+
"CVE-2019-7245 ",
6+
"FEYE-2019-0002"
7+
],
8+
"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.",
9+
"affected_packages": [],
10+
"references_v2": [],
11+
"patches": [],
12+
"severities": [
13+
{
14+
"system": "generic_textual",
15+
"value": "High",
16+
"scoring_elements": ""
17+
}
18+
],
19+
"date_published": null,
20+
"weaknesses": [],
21+
"url": "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/fireeye_test1.md"
22+
}
23+
]
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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[
2+
{
3+
"advisory_id": "fireeye_test2",
4+
"aliases": [
5+
"CVE-2020-12878",
6+
"FEYE-2020-0020"
7+
],
8+
"summary": "Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`.",
9+
"affected_packages": [],
10+
"references_v2": [
11+
{
12+
"reference_id": "",
13+
"reference_type": "",
14+
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878"
15+
},
16+
{
17+
"reference_id": "",
18+
"reference_type": "",
19+
"url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html"
20+
},
21+
{
22+
"reference_id": "",
23+
"reference_type": "",
24+
"url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html"
25+
}
26+
],
27+
"patches": [],
28+
"severities": [
29+
{
30+
"system": "generic_textual",
31+
"value": "High",
32+
"scoring_elements": ""
33+
}
34+
],
35+
"date_published": null,
36+
"weaknesses": [],
37+
"url": "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/fireeye_test2.md"
38+
}
39+
]
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

0 commit comments

Comments
 (0)