If you are reading this blog post via a 3rd party source it is very likely that many parts of it will not render correctly (usually, the interactive graphs). Please view the post on dogesec.com for the full interactive viewing experience.
tl;dr
The NVD are still struggling to keep up with the backlog of CVEs to be analysed. With 26,876 added since February, it is no surprise.
Key Findings
- 26,876 CVEs were added to the NVD database between 1st February 2024 and 30th September 2024.
- 17,375 (64.6%) are yet to be fully analysed.
- 9,501 (35.4%) have been analysed in some way, including being rejected.
- 23,109 (86.3%) have a base score greater than 5.0, whilst 3,086 (11.5%) have a CVSS base score greater than 9.
- The Linux team have by far the largest number of CVEs submitted with no CVSS score (2,108).
- 23 of the analysed vulnerabilities (7,686) have an EPSS score greater than 9.0.
- 70 CVEs in this period are known to be exploited – all of which have been analysed by the NVD.
Why I decided to write this post
If you rely on NIST’s National Vulnerability Database analysis on CVEs for your vulnerability prioritisation efforts, you’re likely aware the they began slowing down their analysis of new vulnerabilities in February 2024.
Since then, the NVD has set themselves a target of clearing the backlog of CVEs yet to be analysed by the end of their fiscal year, which was September 30, 2024.
Now we’re into October, I decided to take a look to see how they’re doing whilst demonstrating the power of stix2arango and cve2stix…
An important note before I begin
This post is not designed to take a shot a NIST. Like most government departments, they’re chronically underfunded. This post is designed to show we need a more sustainable way to manage the vulnerability ecosystem.
Follow along
For this post I am going to use the STIX Objects created by cve2stix over this period.
If you would like to follow along, you can download the same dataset I will be using with the help of stix2arango.
Once you have installed stix2arango, all you need to run is;
python3 utilities/arango_cti_processor/insert_archive_cve.py \
--database cti_knowledge_base_store
This command will import all CVEs published by the NVD into the graph database (including those since this post was written), ready for querying in the ArangoDB web interface (usually http://127.0.0.1:8529/
).
Analysis
CVEs covered in this period
Warming up, I can define our base search to only include vulnerabilities published after 2024-02-01
, but before 2024-09-30
.
Here are some searches that will help you get familiar with the Arango query language, if you’re not already.
Total count for vulnerabilities in this period
RETURN LENGTH(
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
RETURN doc
)
[
26876
]
Print the STIX objects for vulnerabilities in this period
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
LET keys = ATTRIBUTES(doc)
LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
RETURN [KEEP(doc, filteredKeys)]
Here is an example result:
{
"created": "2024-02-02T02:15:17.277Z",
"created_by_ref": "identity--562918ee-d5da-5579-b6a1-fae50cc6bad3",
"description": "IBM PowerSC 1.3, 2.0, and 2.1 could allow a remote attacker to hijack the clicking action of the victim. By persuading a victim to visit a malicious Web site, a remote attacker could exploit this vulnerability to hijack the victim's click actions and possibly launch further attacks against the victim. IBM X-Force ID: 275128.\n\n",
"extensions": {
"extension-definition--2c5c13af-ee92-5246-9ba7-0b958f8cd34a": {
"extension_type": "toplevel-property-extension"
}
},
"external_references": [
{
"source_name": "cve",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-50938",
"external_id": "CVE-2023-50938"
},
{
"source_name": "cwe",
"url": "https://cwe.mitre.org/data/definitions/CWE-451.html",
"external_id": "CWE-451"
},
{
"source_name": "[email protected]",
"description": "VDB Entry,Vendor Advisory",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/275128"
},
{
"source_name": "[email protected]",
"description": "Patch,Vendor Advisory",
"url": "https://www.ibm.com/support/pages/node/7113759"
},
{
"source_name": "vulnStatus",
"description": "Analyzed"
},
{
"source_name": "sourceIdentifier",
"description": "[email protected]"
}
],
"id": "vulnerability--a555db5f-c28c-5709-966a-5a1fd89e27af",
"modified": "2024-02-02T15:12:44.273Z",
"name": "CVE-2023-50938",
"object_marking_refs": [
"marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487",
"marking-definition--562918ee-d5da-5579-b6a1-fae50cc6bad3"
],
"spec_version": "2.1",
"type": "vulnerability",
"x_cvss": {
"v3_1": {
"base_score": 4.3,
"base_severity": "MEDIUM",
"exploitability_score": 2.8,
"impact_score": 1.4,
"source": "[email protected]",
"type": "Primary",
"vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N"
}
}
}
List CVEs covered in this period
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
FOR ext_ref IN doc.external_references
FILTER ext_ref.source_name == "cve"
RETURN ext_ref.external_id
Here is a snippet of the results;
[
"CVE-2024-0704",
"CVE-2023-50938",
"CVE-2024-25001",
"CVE-2023-50934",
"CVE-2023-50328",
"CVE-2023-50936",
"CVE-2024-23034",
"CVE-2023-50327",
Vulnerabilities by analysis state
I decided to start by looking at the analysis state of all these vulnerabilities;
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
// Filter documents with source_name == 'vulnStatus' in the external_references array
FOR ext_ref IN doc.external_references
FILTER ext_ref.source_name == "vulnStatus"
COLLECT description = ext_ref.description INTO group
// Calculate count for each description
LET count = LENGTH(group)
SORT count DESC
// Collect results into an intermediate array
RETURN {
description: description,
count: count
}
[
{
"description": "Awaiting Analysis",
"count": 17375
},
{
"description": "Analyzed",
"count": 7686
},
{
"description": "Modified",
"count": 1105
},
{
"description": "Rejected",
"count": 561
},
{
"description": "Undergoing Analysis",
"count": 149
}
]
Of the 26,876 new vulnerabilities added to the database, 17,375 (64.6%) have yet to be fully analysed. 9,501 (35.4%) have been analysed in some form, including being rejected.
Vulnerabilities by CVSS Score
Even if a vulnerability is in Awaiting Analysis
state, it may still have a CVSS score assigned by the CNA who submitted it.
So how many vulnerabilities submitted since February have at least one CVSS assigned? In this search I will ignore those with vulnStatus
= Rejected
.
RETURN LENGTH(
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
AND doc.x_cvss != "{}"
AND NOT (
LENGTH(
FOR ref IN doc.external_references
FILTER ref.source_name == "vulnStatus"
AND ref.description == "Rejected"
RETURN ref
) > 0
)
RETURN doc
)
[
26315
]
Of the 26,876 new vulnerabilities, 26,315 have had CVSS scores assigned by the submitting CNA.
And what CVSS versions are they using to score the vulnerabilities;
LET filtered_docs = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
AND NOT (
LENGTH(
FOR ref IN doc.external_references
FILTER ref.source_name == "vulnStatus"
AND ref.description == "Rejected"
RETURN ref
) > 0
)
RETURN doc
)
LET empty_cvss_count = LENGTH(
FOR doc IN filtered_docs
FILTER doc.x_cvss == {}
RETURN doc
)
LET grouped_cvss = (
FOR doc IN filtered_docs
// Iterate over the keys of the x_cvss object to extract each cvss version
FILTER doc.x_cvss != {} // Filter out empty x_cvss
FOR cvssKey IN ATTRIBUTES(doc.x_cvss)
COLLECT key = cvssKey INTO group
RETURN {
cvss_version: key,
count: LENGTH(group)
}
)
// Add a row for the count of empty x_cvss
LET final_result = APPEND(grouped_cvss, [
{ cvss_version: "empty_x_cvss", count: empty_cvss_count }
])
// Sort by the count column in descending order
FOR entry IN final_result
SORT entry.count DESC
RETURN entry
[
{
"cvss_version": "v3_1",
"count": 21680
},
{
"cvss_version": "empty_x_cvss",
"count": 3291
},
{
"cvss_version": "v4_0",
"count": 1896
},
{
"cvss_version": "v2_0",
"count": 1860
},
{
"cvss_version": "v3_0",
"count": 1373
}
]
Most are using v3.1, which is to be expected given v4.0 is relatively new. It’ll be interesting to see how this changes over the next year.
The fact that almost all CVEs already have a CVSS score, even without an NVD analysis, it is still possible to understand their severity and thus help with prioritisation.
Looking at the vulnerabilities by their CVSS base score over time;
LET filtered_docs = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
AND NOT (
LENGTH(
FOR ref IN doc.external_references
FILTER ref.source_name == "vulnStatus"
AND ref.description == "Rejected"
RETURN ref
) > 0
)
RETURN doc
)
LET base_score_groups = (
FOR doc IN filtered_docs
// Iterate over each cvss version
FOR cvss IN VALUES(doc.x_cvss)
LET base_score = cvss.base_score
FILTER base_score != null
// Classify the base_score into different ranges using ternary operator
LET range =
base_score >= 0 AND base_score < 1 ? "0-1" :
base_score >= 1 AND base_score < 2 ? "1-2" :
base_score >= 2 AND base_score < 3 ? "2-3" :
base_score >= 3 AND base_score < 4 ? "3-4" :
base_score >= 4 AND base_score < 5 ? "4-5" :
base_score >= 5 AND base_score < 6 ? "5-6" :
base_score >= 6 AND base_score < 7 ? "6-7" :
base_score >= 7 AND base_score < 8 ? "7-8" :
base_score >= 8 AND base_score < 9 ? "8-9" :
base_score >= 9 AND base_score <= 10 ? "9-10" : "unknown"
COLLECT score_range = range INTO group
RETURN {
score_range: score_range,
count: LENGTH(group)
}
)
// Sort by the score range
FOR entry IN base_score_groups
SORT entry.score_range
RETURN entry
[
{
"score_range": "0-1",
"count": 1
},
{
"score_range": "1-2",
"count": 44
},
{
"score_range": "2-3",
"count": 242
},
{
"score_range": "3-4",
"count": 770
},
{
"score_range": "4-5",
"count": 2627
},
{
"score_range": "5-6",
"count": 5524
},
{
"score_range": "6-7",
"count": 5314
},
{
"score_range": "7-8",
"count": 5528
},
{
"score_range": "8-9",
"count": 3657
},
{
"score_range": "9-10",
"count": 3086
}
]
The majority of CVEs (23,109, 86.3%) have a base score greater than 5.0 (just 3,684 have a base score less than 5 – about the same number that have a base score of 9 and above (3,086, 11.5%)).
Vulnerabilities with no CVSS score by CNA
To understand which CNAs have the most outstanding CVEs without a CVSS score;
// First, collect the sourceIdentifier descriptions from the vulnerability documents
LET source_identifiers = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
AND doc.x_cvss == {}
AND NOT (
LENGTH(
FOR ref IN doc.external_references
FILTER ref.source_name == "vulnStatus"
AND ref.description == "Rejected"
RETURN ref
) > 0
)
// Collect the sourceIdentifier description
FOR ref IN doc.external_references
FILTER ref.source_name == "sourceIdentifier"
RETURN {
value: ref.description
}
)
// Now group by the description value, count occurrences, and sort by count
FOR identifier IN source_identifiers
COLLECT value = identifier.value INTO group
// Sort by count in descending order
LET count = LENGTH(group)
SORT count DESC
RETURN {
value: value,
count: count
}
CNA | Count of vulnerabilities with no CVSS |
---|---|
416baaa9-dc9f-4396-8d5f-8c081fb06d67 (Linux) | 2108 |
[email protected] | 570 |
[email protected] | 122 |
[email protected] | 65 |
[email protected] | 52 |
[email protected] | 42 |
[email protected] | 39 |
[email protected] | 38 |
[email protected] | 37 |
[email protected] | 28 |
Two CNAs stand out: Linux (416baaa9-dc9f-4396-8d5f-8c081fb06d67) (2,108), which has overwhelmed the CVE program with a high volume of vulnerabilities this year, and MITRE (570), the Root CNA responsible for managing the CVE program.
Vulnerabilities by EPSS Score
Another popular scoring mechanism for vulnerabilities is EPSS.
EPPS estimates the likelihood of a software vulnerability being exploited.
It is important to note, the EPSS for a CVE changes over time (older vulnerabilities are generally less likely to be exploited). The scores shown here are correct for today.
Using the list of vulnerabilities published in this period, it is possible to also get their EPSS scores as follows;
// Step 1: Collect all vulnerability IDs from the first search
LET vulnerability_ids = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
RETURN doc.id
)
// Step 2: Find all note objects that reference these vulnerability IDs
LET epss_data = (
FOR note IN nvd_cve_vertex_collection
FILTER note.type == "note"
// Check if any of the object_refs in the note matches the vulnerability IDs
FILTER LENGTH(
FOR ref IN note.object_refs
FILTER ref IN vulnerability_ids
RETURN ref
) > 0
// Ensure the note has x_epss data
FILTER LENGTH(note.x_epss) > 0
// Collect the epss data
FOR epss_entry IN note.x_epss
RETURN TO_NUMBER(epss_entry.epss)
)
// Step 3: Group the epss scores into buckets and count how many fall in each range
FOR epss_value IN epss_data
LET bucket =
epss_value >= 0 AND epss_value < 0.1 ? "0.0-0.1" :
epss_value >= 0.1 AND epss_value < 0.2 ? "0.1-0.2" :
epss_value >= 0.2 AND epss_value < 0.3 ? "0.2-0.3" :
epss_value >= 0.3 AND epss_value < 0.4 ? "0.3-0.4" :
epss_value >= 0.4 AND epss_value < 0.5 ? "0.4-0.5" :
epss_value >= 0.5 AND epss_value < 0.6 ? "0.5-0.6" :
epss_value >= 0.6 AND epss_value < 0.7 ? "0.6-0.7" :
epss_value >= 0.7 AND epss_value < 0.8 ? "0.7-0.8" :
epss_value >= 0.8 AND epss_value < 0.9 ? "0.8-0.9" :
epss_value >= 0.9 AND epss_value <= 1.0 ? "0.9-1.0" : "unknown"
// Group by bucket and count occurrences
COLLECT epss_range = bucket WITH COUNT INTO range_count
SORT epss_range
RETURN {
epss_range: epss_range,
count: range_count
}
EPPS Range | Count of CVEs |
---|---|
0.0-0.1 | 25947 |
0.1-0.2 | 11 |
0.2-0.3 | 5 |
0.3-0.4 | 5 |
0.5-0.6 | 2 |
0.6-0.7 | 3 |
0.7-0.8 | 3 |
0.8-0.9 | 5 |
0.9-1.0 | 23 |
You can see most have a score lower than 0.1 (25,947). All CVEs yet to be analysed (17,375) fall into this bucket. Even so, this highlights the fact most CVEs are not likely to be exploited.
There are however, 23 with a EPSS score above 9.0, which indicated the CVE is almost certain to be exploited.
If your interested what CVEs these are…
// Step 1: Collect all vulnerability IDs from the first search
LET vulnerability_ids = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
RETURN doc.id
)
// Step 2: Find all note objects that reference these vulnerability IDs and have EPSS scores greater than 0.9
FOR note IN nvd_cve_vertex_collection
FILTER note.type == "note"
// Check if any of the object_refs in the note matches the vulnerability IDs
FILTER LENGTH(
FOR ref IN note.object_refs
FILTER ref IN vulnerability_ids
RETURN ref
) > 0
// Ensure the note has x_epss data
FILTER LENGTH(note.x_epss) > 0
// Return CVE IDs and EPSS scores where EPSS score is greater than 0.9
FOR epss_entry IN note.x_epss
FILTER TO_NUMBER(epss_entry.epss) > 0.9
SORT epss_entry.epss DESC
RETURN {
cve: epss_entry.cve,
epss: epss_entry.epss
}
CVE | EPSS Score |
---|---|
CVE-2024-7593 | 0.97325 |
CVE-2024-34102 | 0.97284 |
CVE-2024-27198 | 0.96937 |
CVE-2024-4879 | 0.96556 |
CVE-2024-4040 | 0.96499 |
CVE-2024-3400 | 0.96471 |
CVE-2024-27348 | 0.96344 |
CVE-2024-4577 | 0.9632 |
CVE-2024-28995 | 0.96178 |
CVE-2024-5217 | 0.9616 |
CVE-2024-36401 | 0.9588 |
CVE-2024-23692 | 0.95648 |
CVE-2024-21683 | 0.94696 |
CVE-2024-1709 | 0.94435 |
CVE-2024-28987 | 0.94156 |
CVE-2024-24919 | 0.94034 |
CVE-2024-38856 | 0.93538 |
CVE-2024-6893 | 0.93338 |
CVE-2024-3273 | 0.93241 |
CVE-2024-29972 | 0.93 |
CVE-2024-29973 | 0.93 |
CVE-2024-4358 | 0.9275 |
CVE-2024-6670 | 0.90985 |
Vulnerabilities by Known Exploits
CISA KEV report vulnerabilities known to be exploited in the wild.
I wanted to see the analysis state of all CVEs actively being exploited;
// First, collect the list of external_id values and descriptions from sightings
LET cve_sightings = (
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "sighting"
FOR ext_ref IN doc.external_references
FILTER ext_ref.source_name == "cve"
RETURN {
cve_id: ext_ref.external_id,
sighting_description: doc.description
}
)
// Then, search for vulnerabilities with these external_id values and return both CVE ID, vulnStatus, and sighting description
FOR sighting IN cve_sightings
FOR doc IN nvd_cve_vertex_collection
FILTER doc.type == "vulnerability"
FILTER DATE_TIMESTAMP(doc.created) >= DATE_TIMESTAMP("2024-02-01T00:00:00Z")
FILTER DATE_TIMESTAMP(doc.created) <= DATE_TIMESTAMP("2024-09-30T00:00:00Z")
FOR ext_ref IN doc.external_references
FILTER ext_ref.external_id == sighting.cve_id
// Extract the vulnStatus and CVE ID, and include sighting description
FOR ext_ref2 IN doc.external_references
FILTER ext_ref2.source_name == "vulnStatus"
SORT doc.created DESC
RETURN {
cve_id: ext_ref.external_id,
vuln_status: ext_ref2.description,
created: doc.created,
sighting_description: sighting.sighting_description
}
[
{
"cve_id": "CVE-2024-8963",
"vuln_status": "Analyzed",
"created": "2024-09-19T18:15:10.600Z",
"sighting_description": "CISA KEV: Ivanti Cloud Services Appliance (CSA) Path Traversal Vulnerability\n\n As Ivanti CSA has reached End-of-Life status, users are urged to remove CSA 4.6.x from service or upgrade to the 5.0.x line of supported solutions, as future vulnerabilities on the 4.6.x version of CSA are unlikely to receive security updates.\n\n Action due by: 2024-10-10"
},
{
"cve_id": "CVE-2024-8190",
"vuln_status": "Analyzed",
"created": "2024-09-10T21:15:14.697Z",
"sighting_description": "CISA KEV: Ivanti Cloud Services Appliance OS Command Injection Vulnerability\n\n As Ivanti CSA has reached End-of-Life status, users are urged to remove CSA 4.6.x from service or upgrade to the 5.0.x line of supported solutions, as future vulnerabilities on the 4.6.x version of CSA are unlikely to receive future security updates.\n\n Action due by: 2024-10-04"
},
{
"cve_id": "CVE-2024-43461",
"vuln_status": "Analyzed",
"created": "2024-09-10T17:15:33.410Z",
"sighting_description": "CISA KEV: Microsoft Windows MSHTML Platform Spoofing Vulnerability\n\n Apply mitigations per vendor instructions or discontinue use of the product if mitigations are unavailable.\n\n Action due by: 2024-10-07"
},
The good news, all 70 of them have been analysed!
Vulmatch
Straightforward vulnerability management. Know when software you use is vulnerable, how it is being exploited, and how to detect an attack.
Discuss this post
Head on over to the DOGESEC community to discuss this post.
Never miss an update
Sign up to receive new articles in your inbox as they published.