Skip to content

Commit ca6a6bf

Browse files
authored
Migrate Fireeye importer to advisory V2 (#2087)
* Initial Fireeye importer migration to Advisory V2 Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Migrate Fireeye importer to Advisory V2 , Add a test Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Fix a doc test and resolve merge conflict Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Fix code format test Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Update FireEye importer to avoid using the identifier as an alias Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Update FireEye importer to use filename as advisory id Signed-off-by: ziad hany <ziadhany2016@gmail.com> * Add missing CVEs to aliases field for MNDT files Signed-off-by: ziad hany <ziadhany2016@gmail.com> --------- Signed-off-by: ziad hany <ziadhany2016@gmail.com>
1 parent a8aa6da commit ca6a6bf

File tree

9 files changed

+478
-0
lines changed

9 files changed

+478
-0
lines changed

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
elixir_security_importer as elixir_security_importer_v2,
5050
)
5151
from vulnerabilities.pipelines.v2_importers import epss_importer_v2
52+
from vulnerabilities.pipelines.v2_importers import fireeye_importer_v2
5253
from vulnerabilities.pipelines.v2_importers import github_osv_importer as github_osv_importer_v2
5354
from vulnerabilities.pipelines.v2_importers import gitlab_importer as gitlab_importer_v2
5455
from vulnerabilities.pipelines.v2_importers import istio_importer as istio_importer_v2
@@ -97,6 +98,7 @@
9798
npm_importer.NpmImporterPipeline,
9899
nginx_importer.NginxImporterPipeline,
99100
pysec_importer.PyPIImporterPipeline,
101+
fireeye_importer_v2.FireeyeImporterPipeline,
100102
apache_tomcat.ApacheTomcatImporter,
101103
postgresql.PostgreSQLImporter,
102104
debian.DebianImporter,
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
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+
import logging
10+
import re
11+
from pathlib import Path
12+
from typing import Iterable
13+
14+
from fetchcode.vcs import fetch_via_vcs
15+
16+
from vulnerabilities.importer import AdvisoryData
17+
from vulnerabilities.importer import ReferenceV2
18+
from vulnerabilities.importer import VulnerabilitySeverity
19+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
20+
from vulnerabilities.severity_systems import GENERIC
21+
from vulnerabilities.utils import build_description
22+
from vulnerabilities.utils import create_weaknesses_list
23+
from vulnerabilities.utils import cwe_regex
24+
from vulnerabilities.utils import dedupe
25+
from vulnerabilities.utils import find_all_cve
26+
from vulnerabilities.utils import get_advisory_url
27+
28+
logger = logging.getLogger(__name__)
29+
30+
31+
class FireeyeImporterPipeline(VulnerableCodeBaseImporterPipelineV2):
32+
spdx_license_expression = "CC-BY-SA-4.0 AND MIT"
33+
license_url = "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/README.md"
34+
notice = """
35+
Copyright (c) Mandiant
36+
The following licenses/licensing apply to this Mandiant repository:
37+
1. CC BY-SA 4.0 - For CVE related information not including source code (such as PoCs)
38+
2. MIT - For source code contained within provided CVE information
39+
"""
40+
repo_url = "git+https://github.com/mandiant/Vulnerability-Disclosures"
41+
pipeline_id = "fireeye_importer_v2"
42+
43+
@classmethod
44+
def steps(cls):
45+
return (
46+
cls.clone,
47+
cls.collect_and_store_advisories,
48+
cls.clean_downloads,
49+
)
50+
51+
def advisories_count(self):
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"
57+
)
58+
59+
def clone(self):
60+
self.log(f"Cloning `{self.repo_url}`")
61+
self.vcs_response = fetch_via_vcs(self.repo_url)
62+
63+
def collect_advisories(self) -> Iterable[AdvisoryData]:
64+
base_path = Path(self.vcs_response.dest_dir)
65+
for file_path in base_path.glob("**/*"):
66+
if file_path.suffix.lower() != ".md":
67+
continue
68+
69+
if file_path.stem.upper() == "README":
70+
continue
71+
72+
try:
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+
)
77+
except UnicodeError:
78+
logger.error(f"Invalid File UnicodeError: {file_path}")
79+
80+
def clean_downloads(self):
81+
if self.vcs_response:
82+
self.log(f"Removing cloned repository")
83+
self.vcs_response.delete()
84+
85+
def on_failure(self):
86+
self.clean_downloads()
87+
88+
89+
def parse_advisory_data(raw_data, file_path, base_path) -> AdvisoryData:
90+
"""
91+
Parse a fireeye advisory repo and return an AdvisoryData or None.
92+
These files are in Markdown format.
93+
"""
94+
raw_data = raw_data.replace("\n\n", "\n")
95+
md_list = raw_data.split("\n")
96+
md_dict = md_list_to_dict(md_list)
97+
98+
database_id = md_list[0][1::]
99+
summary = md_dict.get(database_id[1::]) or []
100+
description = md_dict.get("## Description") or []
101+
impact = md_dict.get("## Impact")
102+
cve_refs = md_dict.get("## CVE Reference") or []
103+
cve_ids = md_dict.get("## CVE ID") or []
104+
cleaned_cve_ids = []
105+
for line in cve_ids:
106+
found_cves = find_all_cve(line)
107+
cleaned_cve_ids.extend(found_cves)
108+
109+
references = md_dict.get("## References") or []
110+
cwe_data = md_dict.get("## Common Weakness Enumeration") or []
111+
112+
advisory_id = file_path.stem
113+
aliases = dedupe([cve.strip() for cve in cleaned_cve_ids + cve_refs])
114+
aliases = [aliase for aliase in aliases if aliase != advisory_id]
115+
advisory_url = get_advisory_url(
116+
file=file_path,
117+
base_path=base_path,
118+
url="https://github.com/mandiant/Vulnerability-Disclosures/blob/master/",
119+
)
120+
121+
return AdvisoryData(
122+
advisory_id=advisory_id,
123+
aliases=aliases,
124+
summary=build_description(" ".join(summary), " ".join(description)),
125+
references_v2=get_references(references),
126+
severities=get_severities(impact),
127+
weaknesses=get_weaknesses(cwe_data),
128+
url=advisory_url,
129+
original_advisory_text=raw_data,
130+
)
131+
132+
133+
def get_references(references):
134+
"""
135+
Return a list of Reference from a list of URL reference in md format
136+
>>> get_references(["- http://1-4a.com/cgi-bin/alienform/af.cgi"])
137+
[ReferenceV2(reference_id='', reference_type='', url='http://1-4a.com/cgi-bin/alienform/af.cgi')]
138+
>>> get_references(["- [Mitre CVE-2021-42712](https://www.cve.org/CVERecord?id=CVE-2021-42712)"])
139+
[ReferenceV2(reference_id='', reference_type='', url='https://www.cve.org/CVERecord?id=CVE-2021-42712')]
140+
"""
141+
urls = []
142+
for ref in references:
143+
clean_ref = ref.strip()
144+
clean_ref = clean_ref.lstrip("-* ")
145+
url = matcher_url(clean_ref)
146+
if url:
147+
urls.append(url)
148+
return [ReferenceV2(url=url) for url in urls if url]
149+
150+
151+
def matcher_url(ref) -> str:
152+
"""
153+
Returns URL of the reference markup from reference url in Markdown format
154+
"""
155+
markup_regex = "\[([^\[]+)]\(\s*(http[s]?://.+)\s*\)"
156+
matched_markup = re.findall(markup_regex, ref)
157+
if matched_markup:
158+
return matched_markup[0][1]
159+
else:
160+
return ref
161+
162+
163+
def md_list_to_dict(md_list):
164+
"""
165+
Returns a dictionary of md_list from a list of a md file splited by \n
166+
>>> md_list_to_dict(["# Header","hello" , "hello again" ,"# Header2"])
167+
{'# Header': ['hello', 'hello again'], '# Header2': []}
168+
"""
169+
md_dict = {}
170+
md_key = ""
171+
for md_line in md_list:
172+
if md_line.startswith("#"):
173+
md_dict[md_line] = []
174+
md_key = md_line
175+
else:
176+
md_dict[md_key].append(md_line)
177+
return md_dict
178+
179+
180+
def get_weaknesses(cwe_data):
181+
"""
182+
Return the list of CWE IDs as integers from a list of weakness summaries, e.g., [379].
183+
>>> get_weaknesses([
184+
... "CWE-379: Creation of Temporary File in Directory with Insecure Permissions",
185+
... "CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')"
186+
... ])
187+
[379, 362]
188+
"""
189+
cwe_list = []
190+
for line in cwe_data:
191+
cwe_ids = re.findall(cwe_regex, line)
192+
cwe_list.extend(cwe_ids)
193+
194+
weaknesses = create_weaknesses_list(cwe_list)
195+
return weaknesses
196+
197+
198+
def get_severities(impact):
199+
"""
200+
Return a list of VulnerabilitySeverity extracted from the impact string.
201+
>>> get_severities([
202+
... "High - Arbitrary Ring 0 code execution",
203+
... ])
204+
[VulnerabilitySeverity(system=ScoringSystem(identifier='generic_textual', name='Generic textual severity rating', url='', notes='Severity for generic scoring systems. Contains generic textual values like High, Low etc'), value='High', scoring_elements='', published_at=None, url=None)]
205+
>>> get_severities([])
206+
[]
207+
"""
208+
if not impact:
209+
return []
210+
211+
impact_text = impact[0]
212+
value = ""
213+
if " - " in impact_text:
214+
value = impact_text.split(" - ")[0]
215+
elif ": " in impact_text:
216+
value = impact_text.split(": ")[0]
217+
else:
218+
parts = impact_text.split(" ")
219+
if parts:
220+
value = parts[0]
221+
222+
if not value.lower() in ["high", "medium", "low"]:
223+
return []
224+
225+
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 / "FEYE-2019-0002.md",
23+
TEST_DATA / "FEYE-2020-0020.md",
24+
TEST_DATA / "MNDT-2025-0009.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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"advisory_id": "FEYE-2019-0002",
4+
"aliases": [
5+
"CVE-2019-7245"
6+
],
7+
"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.",
8+
"affected_packages": [],
9+
"references_v2": [],
10+
"patches": [],
11+
"severities": [
12+
{
13+
"system": "generic_textual",
14+
"value": "High",
15+
"scoring_elements": ""
16+
}
17+
],
18+
"date_published": null,
19+
"weaknesses": [],
20+
"url": "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/FEYE-2019-0002.md"
21+
}
22+
]
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"advisory_id": "FEYE-2020-0020",
4+
"aliases": [
5+
"CVE-2020-12878"
6+
],
7+
"summary": "Digi International's ConnectPort X2e is susceptible to a local privilege escalation vulnerable to the privileged user `root`.",
8+
"affected_packages": [],
9+
"references_v2": [
10+
{
11+
"reference_id": "",
12+
"reference_type": "",
13+
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12878"
14+
},
15+
{
16+
"reference_id": "",
17+
"reference_type": "",
18+
"url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-one.html"
19+
},
20+
{
21+
"reference_id": "",
22+
"reference_type": "",
23+
"url": "https://www.fireeye.com/blog/threat-research/2021/02/solarcity-exploitation-of-x2e-iot-device-part-two.html"
24+
}
25+
],
26+
"patches": [],
27+
"severities": [
28+
{
29+
"system": "generic_textual",
30+
"value": "High",
31+
"scoring_elements": ""
32+
}
33+
],
34+
"date_published": null,
35+
"weaknesses": [],
36+
"url": "https://github.com/mandiant/Vulnerability-Disclosures/blob/master/FEYE-2020-0020.md"
37+
}
38+
]

0 commit comments

Comments
 (0)