VulnCheck has some new, free API endpoints for the cybersecurity community.
Two extremely useful ones are for their extended version of CISA’s KEV, and an in-situ replacement for NVD’s sad excuse for an API and soon-to-be-removed JSON feeds.
There are two ways to work with these APIs. One is retrieve a “backup” of the entire dataset as a ZIP file, and the other is to use the API to retrieve individual CVEs from each “index”.
You’ll need a free API key from VulnCheck to use these APIs.
All code shown makes the assumption that you’ve stored your API key in an environment variable named VULNCHECK_API_KEY
.
After the curl
examples, there’s a section on a small Golang CLI I made to make it easier to get combined extended KEV and NVDv2 CVE information in one CLI call for a given CVE.
Backups
Retrieving the complete dataset is a multi-step process. First you make a call to the specific API endpoint for each index to backup. That returns some JSON with a temporary, AWS pre-signed URL (a method to grant temporary access to files stored in AWS S3) to download the ZIP file. Then you download the ZIP file, and finally you extract the contents of the ZIP file into a directory. The output is different for the NVDv2 and extended KEV indexes, but the core process is the same.
NVDv2
Here’s a curl
idiom for the NVDv2 index backup. The result is a directory of uncompressed JSON that’s in the same format as the NVDv2 JSON feeds.
# Grab the temporary AWS pre-signed URL for the NVDv2 index and then download the ZIP file.
curl \
--silent \
--output vcnvd2.zip --url "$(
curl \
--silent \
--cookie "token=${VULNCHECK_API_KEY}" \
--header 'Accept: application/json' \
--url "https://api.vulncheck.com/v3/backup/nist-nvd2" | jq -r '.data[].url'
)"
rm -rf ./nvd2
# unzip it
unzip -q -o -d ./nvd2 vcnvd2.zip
# uncompress the JSON files
ls ./nvd2/*gz | xargs gunzip
tree ./nvd2
./nvd2
├── nvdcve-2.0-000.json
├── nvdcve-2.0-001.json
├── nvdcve-2.0-002.json
├── nvdcve-2.0-003.json
├── nvdcve-2.0-004.json
├── nvdcve-2.0-005.json
├── nvdcve-2.0-006.json
├── nvdcve-2.0-007.json
├── nvdcve-2.0-008.json
├── nvdcve-2.0-009.json
├── nvdcve-2.0-010.json
├── nvdcve-2.0-011.json
├── nvdcve-2.0-012.json
├── nvdcve-2.0-013.json
├── nvdcve-2.0-014.json
├── nvdcve-2.0-015.json
├── nvdcve-2.0-016.json
├── nvdcve-2.0-017.json
├── nvdcve-2.0-018.json
├── nvdcve-2.0-019.json
├── nvdcve-2.0-020.json
├── nvdcve-2.0-021.json
├── nvdcve-2.0-022.json
├── nvdcve-2.0-023.json
├── nvdcve-2.0-024.json
├── nvdcve-2.0-025.json
├── nvdcve-2.0-026.json
├── nvdcve-2.0-027.json
├── nvdcve-2.0-028.json
├── nvdcve-2.0-029.json
├── nvdcve-2.0-030.json
├── nvdcve-2.0-031.json
├── nvdcve-2.0-032.json
├── nvdcve-2.0-033.json
├── nvdcve-2.0-034.json
├── nvdcve-2.0-035.json
├── nvdcve-2.0-036.json
├── nvdcve-2.0-037.json
├── nvdcve-2.0-038.json
├── nvdcve-2.0-039.json
├── nvdcve-2.0-040.json
├── nvdcve-2.0-041.json
├── nvdcve-2.0-042.json
├── nvdcve-2.0-043.json
├── nvdcve-2.0-044.json
├── nvdcve-2.0-045.json
├── nvdcve-2.0-046.json
├── nvdcve-2.0-047.json
├── nvdcve-2.0-048.json
├── nvdcve-2.0-049.json
├── nvdcve-2.0-050.json
├── nvdcve-2.0-051.json
├── nvdcve-2.0-052.json
├── nvdcve-2.0-053.json
├── nvdcve-2.0-054.json
├── nvdcve-2.0-055.json
├── nvdcve-2.0-056.json
├── nvdcve-2.0-057.json
├── nvdcve-2.0-058.json
├── nvdcve-2.0-059.json
├── nvdcve-2.0-060.json
├── nvdcve-2.0-061.json
├── nvdcve-2.0-062.json
├── nvdcve-2.0-063.json
├── nvdcve-2.0-064.json
├── nvdcve-2.0-065.json
├── nvdcve-2.0-066.json
├── nvdcve-2.0-067.json
├── nvdcve-2.0-068.json
├── nvdcve-2.0-069.json
├── nvdcve-2.0-070.json
├── nvdcve-2.0-071.json
├── nvdcve-2.0-072.json
├── nvdcve-2.0-073.json
├── nvdcve-2.0-074.json
├── nvdcve-2.0-075.json
├── nvdcve-2.0-076.json
├── nvdcve-2.0-077.json
├── nvdcve-2.0-078.json
├── nvdcve-2.0-079.json
├── nvdcve-2.0-080.json
├── nvdcve-2.0-081.json
├── nvdcve-2.0-082.json
├── nvdcve-2.0-083.json
├── nvdcve-2.0-084.json
├── nvdcve-2.0-085.json
├── nvdcve-2.0-086.json
├── nvdcve-2.0-087.json
├── nvdcve-2.0-088.json
├── nvdcve-2.0-089.json
├── nvdcve-2.0-090.json
├── nvdcve-2.0-091.json
├── nvdcve-2.0-092.json
├── nvdcve-2.0-093.json
├── nvdcve-2.0-094.json
├── nvdcve-2.0-095.json
├── nvdcve-2.0-096.json
├── nvdcve-2.0-097.json
├── nvdcve-2.0-098.json
├── nvdcve-2.0-099.json
├── nvdcve-2.0-100.json
├── nvdcve-2.0-101.json
├── nvdcve-2.0-102.json
├── nvdcve-2.0-103.json
├── nvdcve-2.0-104.json
├── nvdcve-2.0-105.json
├── nvdcve-2.0-106.json
├── nvdcve-2.0-107.json
├── nvdcve-2.0-108.json
├── nvdcve-2.0-109.json
├── nvdcve-2.0-110.json
├── nvdcve-2.0-111.json
├── nvdcve-2.0-112.json
├── nvdcve-2.0-113.json
├── nvdcve-2.0-114.json
├── nvdcve-2.0-115.json
├── nvdcve-2.0-116.json
├── nvdcve-2.0-117.json
├── nvdcve-2.0-118.json
├── nvdcve-2.0-119.json
├── nvdcve-2.0-120.json
└── nvdcve-2.0-121.json
1 directory, 122 files
VulnCheck’s Extended KEV
Here’s a curl
idiom for the extended KEV index backup. The result is a directory with a single uncompressed JSON that’s in an extended format of what’s in the CISA KEV JSON.s
# Grab the temporary AWS pre-signed URL for the NVDv2 index and then download the ZIP file.
curl \
--silent \
--output vckev.zip --url "$(
curl \
--silent \
--cookie "token=${VULNCHECK_API_KEY}" \
--header 'Accept: application/json' \
--url "https://api.vulncheck.com/v3/backup/vulncheck-kev" | jq -r '.data[].url'
)"
rm -rf ./vckev
# unzip it
unzip -q -o -d ./vckev vckev.zip
tree ./vckev
./vckev
└── vulncheck_known_exploited_vulnerabilities.json
1 directory, 1 file
Retrieving Information On Individual CVEs
While there are other, searchable fields for each index, the primary use case for most of us is getting information on individual CVEs. The API calls are virtually identical, apart from the selected index.
NOTE: the examples pipe the output through jq
to make the API results easier to read.
NVDv2
curl \
--silent \
--cookie "token=${VULNCHECK_API_KEY}" \
--header 'Accept: application/json' \
--url "https://api.vulncheck.com/v3/index/nist-nvd2?cve=CVE-2024-23334" | jq
{
"_benchmark": 0.056277,
"_meta": {
"timestamp": "2024-03-23T08:47:17.940032202Z",
"index": "nist-nvd2",
"limit": 100,
"total_documents": 1,
"sort": "_id",
"parameters": [
{
"name": "cve",
"format": "CVE-YYYY-N{4-7}"
},
{
"name": "alias"
},
{
"name": "iava",
"format": "[0-9]{4}[A-Z-0-9]+"
},
{
"name": "threat_actor"
},
{
"name": "mitre_id"
},
{
"name": "misp_id"
},
{
"name": "ransomware"
},
{
"name": "botnet"
},
{
"name": "published"
},
{
"name": "lastModStartDate",
"format": "YYYY-MM-DD"
},
{
"name": "lastModEndDate",
"format": "YYYY-MM-DD"
}
],
"order": "desc",
"page": 1,
"total_pages": 1,
"max_pages": 6,
"first_item": 1,
"last_item": 1
},
"data": [
{
"id": "CVE-2024-23334",
"sourceIdentifier": "security-advisories@github.com",
"vulnStatus": "Modified",
"published": "2024-01-29T23:15:08.563",
"lastModified": "2024-02-09T03:15:09.603",
"descriptions": [
{
"lang": "en",
"value": "aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. When using aiohttp as a web server and configuring static routes, it is necessary to specify the root path for static files. Additionally, the option 'follow_symlinks' can be used to determine whether to follow symbolic links outside the static root directory. When 'follow_symlinks' is set to True, there is no validation to check if reading a file is within the root directory. This can lead to directory traversal vulnerabilities, resulting in unauthorized access to arbitrary files on the system, even when symlinks are not present. Disabling follow_symlinks and using a reverse proxy are encouraged mitigations. Version 3.9.2 fixes this issue."
},
{
"lang": "es",
"value": "aiohttp es un framework cliente/servidor HTTP asíncrono para asyncio y Python. Cuando se utiliza aiohttp como servidor web y se configuran rutas estáticas, es necesario especificar la ruta raíz para los archivos estáticos. Además, la opción 'follow_symlinks' se puede utilizar para determinar si se deben seguir enlaces simbólicos fuera del directorio raíz estático. Cuando 'follow_symlinks' se establece en Verdadero, no hay validación para verificar si la lectura de un archivo está dentro del directorio raíz. Esto puede generar vulnerabilidades de directory traversal, lo que resulta en acceso no autorizado a archivos arbitrarios en el sistema, incluso cuando no hay enlaces simbólicos presentes. Se recomiendan como mitigaciones deshabilitar follow_symlinks y usar un proxy inverso. La versión 3.9.2 soluciona este problema."
}
],
"references": [
{
"url": "https://github.com/aio-libs/aiohttp/commit/1c335944d6a8b1298baf179b7c0b3069f10c514b",
"source": "security-advisories@github.com",
"tags": [
"Patch"
]
},
{
"url": "https://github.com/aio-libs/aiohttp/pull/8079",
"source": "security-advisories@github.com",
"tags": [
"Patch"
]
},
{
"url": "https://github.com/aio-libs/aiohttp/security/advisories/GHSA-5h86-8mv2-jq9f",
"source": "security-advisories@github.com",
"tags": [
"Exploit",
"Mitigation",
"Vendor Advisory"
]
},
{
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ICUOCFGTB25WUT336BZ4UNYLSZOUVKBD/",
"source": "security-advisories@github.com"
},
{
"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XXWVZIVAYWEBHNRIILZVB3R3SDQNNAA7/",
"source": "security-advisories@github.com",
"tags": [
"Mailing List"
]
}
],
"metrics": {
"cvssMetricV31": [
{
"source": "nvd@nist.gov",
"type": "Primary",
"cvssData": {
"version": "3.1",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"scope": "UNCHANGED",
"confidentialityImpact": "HIGH",
"integrityImpact": "NONE",
"availabilityImpact": "NONE",
"baseScore": 7.5,
"baseSeverity": "HIGH"
},
"exploitabilityScore": 3.9,
"impactScore": 3.6
},
{
"source": "security-advisories@github.com",
"type": "Secondary",
"cvssData": {
"version": "3.1",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
"attackVector": "NETWORK",
"attackComplexity": "HIGH",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"scope": "UNCHANGED",
"confidentialityImpact": "HIGH",
"integrityImpact": "NONE",
"availabilityImpact": "NONE",
"baseScore": 5.9,
"baseSeverity": "MEDIUM"
},
"exploitabilityScore": 2.2,
"impactScore": 3.6
}
]
},
"weaknesses": [
{
"source": "security-advisories@github.com",
"type": "Primary",
"description": [
{
"lang": "en",
"value": "CWE-22"
}
]
}
],
"configurations": [
{
"nodes": [
{
"operator": "OR",
"cpeMatch": [
{
"vulnerable": true,
"criteria": "cpe:2.3:a:aiohttp:aiohttp:*:*:*:*:*:*:*:*",
"versionStartIncluding": "1.0.5",
"versionEndExcluding": "3.9.2",
"matchCriteriaId": "CC18B2A9-9D80-4A6E-94E7-8FC010D8FC70"
}
]
}
]
},
{
"nodes": [
{
"operator": "OR",
"cpeMatch": [
{
"vulnerable": true,
"criteria": "cpe:2.3:o:fedoraproject:fedora:39:*:*:*:*:*:*:*",
"matchCriteriaId": "B8EDB836-4E6A-4B71-B9B2-AA3E03E0F646"
}
]
}
]
}
],
"_timestamp": "2024-02-09T05:33:33.170054Z"
}
]
}
VulnCheck’s Extended KEV
curl \
--silent \
--cookie "token=${VULNCHECK_API_KEY}" \
--header 'Accept: application/json' \
--url "https://api.vulncheck.com/v3/index/vulncheck-kev?cve=CVE-2024-23334" | jq
{
"_benchmark": 0.328855,
"_meta": {
"timestamp": "2024-03-23T08:47:41.025967418Z",
"index": "vulncheck-kev",
"limit": 100,
"total_documents": 1,
"sort": "_id",
"parameters": [
{
"name": "cve",
"format": "CVE-YYYY-N{4-7}"
},
{
"name": "alias"
},
{
"name": "iava",
"format": "[0-9]{4}[A-Z-0-9]+"
},
{
"name": "threat_actor"
},
{
"name": "mitre_id"
},
{
"name": "misp_id"
},
{
"name": "ransomware"
},
{
"name": "botnet"
},
{
"name": "published"
},
{
"name": "lastModStartDate",
"format": "YYYY-MM-DD"
},
{
"name": "lastModEndDate",
"format": "YYYY-MM-DD"
},
{
"name": "pubStartDate",
"format": "YYYY-MM-DD"
},
{
"name": "pubEndDate",
"format": "YYYY-MM-DD"
}
],
"order": "desc",
"page": 1,
"total_pages": 1,
"max_pages": 6,
"first_item": 1,
"last_item": 1
},
"data": [
{
"vendorProject": "aiohttp",
"product": "aiohttp",
"shortDescription": "aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. When using aiohttp as a web server and configuring static routes, it is necessary to specify the root path for static files. Additionally, the option 'follow_symlinks' can be used to determine whether to follow symbolic links outside the static root directory. When 'follow_symlinks' is set to True, there is no validation to check if reading a file is within the root directory. This can lead to directory traversal vulnerabilities, resulting in unauthorized access to arbitrary files on the system, even when symlinks are not present. Disabling follow_symlinks and using a reverse proxy are encouraged mitigations. Version 3.9.2 fixes this issue.",
"vulnerabilityName": "aiohttp aiohttp Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')",
"required_action": "Apply remediations or mitigations per vendor instructions or discontinue use of the product if remediation or mitigations are unavailable.",
"knownRansomwareCampaignUse": "Known",
"cve": [
"CVE-2024-23334"
],
"vulncheck_xdb": [
{
"xdb_id": "231b48941355",
"xdb_url": "https://vulncheck.com/xdb/231b48941355",
"date_added": "2024-02-28T22:30:21Z",
"exploit_type": "infoleak",
"clone_ssh_url": "git@github.com:ox1111/CVE-2024-23334.git"
},
{
"xdb_id": "f1d001911304",
"xdb_url": "https://vulncheck.com/xdb/f1d001911304",
"date_added": "2024-03-19T16:28:56Z",
"exploit_type": "infoleak",
"clone_ssh_url": "git@github.com:jhonnybonny/CVE-2024-23334.git"
}
],
"vulncheck_reported_exploitation": [
{
"url": "https://cyble.com/blog/cgsi-probes-shadowsyndicate-groups-possible-exploitation-of-aiohttp-vulnerability-cve-2024-23334/",
"date_added": "2024-03-15T00:00:00Z"
}
],
"date_added": "2024-03-15T00:00:00Z",
"_timestamp": "2024-03-23T08:27:47.861266Z"
}
]
}
vccve
There’s a project on Codeberg that has code and binaries for macOS, Linux, and Windows for a small CLI that gets you combined extended KEV and NVDv2 information all in one call.
The project README has examples and installation instructions.
Where Do We Go From Here?
Christian nationalists, the GOP, clueless Dems, & SCOTUS have done quite a bit of real, serious damage to the fragile state of democracy & discourse in the U.S. They’ve also set back over 70+ years of hard fought advancements at record speed.
It’s a big, complex problem to solve, and, if it all feels overwhelming, asymmetric & unfair, well, it is. Demoralizing us into anger-fueled apathy is one of the goals.
In one side of my profession, a process called “decomposition” is used to break a complex problem into smaller subproblems that are easier to solve. The subproblems are solved recursively or iteratively. The solutions to the subproblems are then combined to solve the original larger problem. Some subproblems can be solved in parallel, with different folks working on different subproblems; others need to wait for some subproblems to be solved first.
The big list of components of this present concern/danger needs to be enumerated & documented, so the actual subproblems can be identified. Nobody is going to do that for you/us. It’s a cognitively & emotionally painful, but important, step. But, it will also help folks explain the big picture to friends/family. The massive scope has to be understood, if only to help others grok that there is no quick, magic solution. It’s going to be a long, hard slog.
Once that settles in, the most essential thing happens next: what is the first subproblem that needs to be solved? It provides focus and — more importantly — an accomplishable goal.
I think it’s fair to say that one bigger subproblem that fits into the next category. It is working to ensure no member of the GOP gets elected to any office in any capacity anywhere in the U.S. for the next forty years. They’ve demonstrated they can’t be trusted with power, and that they have no integrity or shame.
That’s a big subproblem.
Decomposition ultimately brings that down to what an individual can do in the place they live, which means you & I need to prevent GOP-aligned folks from being elected to:
Organizations like Indivisible can help provide tangible, accomplishable tasks to complete in order to make that reality. Also: it kind of doesn’t matter “who” is opposite a GOP contender. What matters is that they’re neither in the GOP nor a fringe third-party with no chance of being elected. We’re all going to have to put away our pet desires/agendas for a few decades if we really want a foundation for change that can be built on.
A parallel, bigger subproblem, is watching what local, state, and federal legislation is being floated/worked on, and pushing back hard. GovTrack, POGO, Common Cause, Public Citizen and others can help with that, but the onus is on us to do the actual challenging.
And, another, parallel subproblem to work on is building community resilience and mutual aid networks because we’re not going fix every instance of every subproblem (e.g., despite solid efforts, GOP folks are almost certainly going to get elected to positions of power, and, thus, pose a real threat). GWU’s Center for Community Resilience is not a bad place to get started learning how to do that.
Those are three, tangible, accomplishable subproblems anyone can work on (even if you just focus on one of them).
It’s going to take a very long time to course correct. It will be very painful. And, far too many folks will get hurt along the way until things get better. But, giving in and doing nothing aren’t options.