Share with Your Network
I heard from our software consulting engineers that our customers would like a risk meter that scores CISA vulnerabilities. So here is my implementation.
First off, CISA stands for Cybersecurity & Infrastructure Security Agency. You know it has to be secure with “security” twice in the name. The CISA mission is to work with partners to defend against today’s threats and collaborates to build a more secure and resilient infrastructure for the future; and, lead the national effort to understand, manage, and reduce risk to our cyber and physical infrastructure. It publishes a vulnerability catalog which can be obtained via an API endpoint. Here are some reasons why the CISA catalog should be used. If you’re interested, there is a small stand-alone program to obtain the CISA vulnerability catalog, get_cisa_catalog.py
.
Here is an outline of how I created a “CISA Risk Meter:”
- Obtain the CISA vulnerability catalog labeled by a CVE ID.
- Create a risk meter that has a CISA custom field criteria
- For each CVE ID in the CISA catalog, find the Kenna vulnerabilities associated with the CVE ID and mark the vulnerability’s CISA custom field so a risk meter will pick it up.
The code for this blog is in blog_build_cisa_risk_meter.py
; however, the latest code is at build_cisa_risk_meter.py
. And remember, a risk meter score is the vulnerability score of an asset group. The terms risk meter and asset group are used interchangeably.
Logging is used throughout the program. The log is in the file cisa_vulns.log
. It is not cleared or managed, so you will have to clean it up from time to time.
Check for Custom Field
The first thing the program does is check to see if the “CISA” custom field is present in: check_for_custom_field()
on line 65. I was disappointed to discover that custom fields can only be created in the UI. After a custom field is created, it is assigned a custom field ID and a none
value. This search can be skipped if the custom field ID is entered on the command line.
The “Search Vulnerabilities” API is used to filter all vulnerabilities with the CISA custom field with the value of none
. Search parameters are in the URL query parameters with the format:
custom_fields:
, and for our CISA example: custom_fields:CISA[]=none
Here is the code:
68 # Assemble the search URL. 69 page_size_query = f"per_page={SEARCH_PAGE_SIZE}" 70 custom_field_query = f"custom_fields:{custom_field}[]=none" 71 search_url = f"{base_url}vulnerabilities/search?{page_size_query}&{custom_field_query}"
Note the return HTTP error codes. Error code 422
means the CISA custom field is not present and needs to be created in the UI.
After error checking, the CISA custom field ID is obtained and returned. We only need the first vulnerability to obtain the custom field ID.
97 # Return the CISA custom field ID based on the first vulnerability found with CISA custom field. 98 vulns = resp_json['vulnerabilities'] 99 if len(vulns) == 0: 100 return 0 101 cisa_custom_field = get_custom_field(vulns[0], CISA_CUSTOM_FIELD_NAME) 102 return cisa_custom_field['custom_field_definition_id']
In the above code snippet, get_custom_field()
for the vulnerability. This function runs through all the custom fields of a vulnerability looking for a custom field name in our case “CISA,” and returns the CISA custom field or None
. Finally, in line 102, the custom field ID is extracted and returned.
Get CISA catalog
After the “CISA” custom field ID is obtained, the CISA vulnerability catalog is fetched in get_cisa_catalog()
. This function is straightforward, invoking the API endpoint, https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
and returning the CISA vulnerabilities along with a vulnerability count and request ISO 8601 date. Here is what one of the CISA vulnerabilities looks like:
{ "cveID": "CVE-2021-27102", "dateAdded": "2021-11-03", "dueDate": "2021-11-17", "product": "FTA", "requiredAction": "Apply updates per vendor instructions.", "shortDescription": "Accellion FTA 9_12_411 and earlier is affected by OS command execution via a local web service call.", "vendorProject": "Accellion", "vulnerabilityName": "Accellion FTA OS Command Injection Vulnerability" },
The catalog is returned. Some fields are extracted for logging purposes, but the array of vulnerabilities is what we really want. Later in the code, you’ll see that for each CISA vulnerability, the CVE ID is used. This CVE ID is the link between CISA vulnerabilities and Kenna vulnerabilities.
Creating the CISA Risk Meter
As you may recall, risk meter creation was discussed in the blog, Automating Risk Meter Creation, where asset tags were used as criteria. Here vulnerability custom fields are used as criteria. But first, before a risk meter is created, the code checks to see if one already exists in get_a_risk_meter()
by the name, “CISA Exploited Vulnerabilities.”
Looking at get_a_risk_meter()
, you’ll see the code takes into account there can be more than 100 risk meters. This is done by scanning through all the risk meter pages.
175 page_size_query = f"per_page={RISK_METER_PAGE_SIZE}" 176 list_risk_meters_url = f"{base_url}asset_groups?{page_size_query}&page={page_num}" 177 logging.info(f"List risk meter URL: {list_risk_meters_url}")
Note that the page number is in the query parameters and the maximum page size for risk meters is 100. If the risk meter is found, it is returned. However, if the “CISA Exploited Vulnerabilities” are not found, the code creates one in create_risk_meter()
.
The salient point in: create_risk_meter()
is the risk meter criteria.
206 vuln_custom_fields = f"custom_fields:{cisa_custom_field_id}:{CISA_CUSTOM_FIELD_NAME}" 207 create_data = { 208 "name": risk_meter_name, 209 "query": { 210 "status": [ 211 "active" 212 ], 213 "vulnerability": { 214 vuln_custom_fields: [ 215 "any" 216 ], 217 "status": [ 218 "open" 219 ] 220 } 221 } 222 }
The custom field key format which for me is custom_fields:8114:CISA
. This custom field key is located vulnerability
array as an element with the value of any
. The vulnerability
array also contains the vulnerability status
key with the value of open
. With the above risk meter criteria, the “Create Asset Group” API is invoked.
Search for CISA Vulnerabilities
Now with a risk meter in hand, it is time to run through all the CISA vulnerabilities and see what Kenna vulnerabilities are associated with each CISA vulnerability. As stated early, CISA vulnerabilities are unique by CVE ID.
305 print(f"Search vulnerabilities associated with CISA CVE IDs") 306 for cisa_vuln in cisa_vulns: 307 cisa_cve_id = cisa_vuln['cveID'] 308 309 # Search for vulns associated with the CVE ID. 310 cve_vulns = search_vulns_for_cve_id(base_url, headers, cisa_cve_id) 311 print(".", end='', flush=True) 312 313 # Check in CISA custom field is assiocated with each vulnerability. 314 # If not, add it to the list. 315 # If the number of vulnerabilities is over the update limit, do a bulk update. 316 for cve_vuln in cve_vulns: 317 if not custom_field_is_set(cve_vuln, CISA_CUSTOM_FIELD_NAME): 318 cve_vuln_id = cve_vuln['id'] 319 logging.info(f"Add Vuln ID {cve_vuln_id} to custom field update list") 320 vulns_to_update.append(cve_vuln_id) 321 if len(vulns_to_update) > VULN_UPDATE_LIMIT: 322 update_cisa_vulns(base_url, headers, vulns_to_update, cisa_custom_field_id) 323 logging.info(f"Updated CISA custom field for {len(vulns_to_update)} vulns") 324 vulns_updated += len(vulns_to_update) 325 vulns_to_update.clear()
The outer loop at line 386 goes through each CISA vulnerability. On line 310, the “Search Vulnerability” API is invoked with the search criteria being the CVE-ID. An array of Kenna vulnerabilities is returned.
The inner loop at line 316 goes through each Kenna vulnerability for the CISA vulnerability. In this loop, the existence of the “CISA” custom field is checked. If the custom field for the vulnerability does not exist, the vulnerability ID is added to the vulns_to_update
array in line 320.
When the length of vulns_to_update
becomes 5000 or greater, all the vulnerabilities in: vulns_to_update
are updated with the CISA custom field with the “Bulk Update Vulnerabilities” API. If there are any vulnerabilities to be updated after running through the loops, a final vulnerability bulk update is invoked in line 329.
Bulk Vulnerabilities Update
The Bulk Update Vulnerabilities is used to set the “CISA” custom fields for the vulnerabilities that are associated with the CVE IDs from the CISA catalog to a “true” value. Remember that custom field values can only be strings or dates.
150 bulk_update_url = f"{base_url}vulnerabilities/bulk" 151 update_custom_field_id = f"{custom_field_id}" 152 update_data = { 153 "vulnerability_ids": vuln_ids, 154 "vulnerability": { 155 "custom_fields": { 156 update_custom_field_id: "true" 157 } 158 } 159 }
Here the update_custom_field_id
is composed only of the custom field ID.
Conclusion
Here is the output of the program:
Build CISA Exploited Vulnerabilities Risk Meter 400329 vulnerabilities with CISA custom field. Search vulnerabilities associated with CISA CVE IDs ...................................................................................... ...................................................................................... ...................................................................................... ...................................................................................... ...................................................................................... ...................................................................................... ..................................................................................... ......... CISA Exploited Vulnerabilities created with 1577 vulnerabilities updated.
This run took approximately 7.5 minutes. Each .
represents a search for vulnerabilities associated with a CISA CVE ID.
I hope this program helps with vulnerability risk management and provides you with a clear indication of which vulnerabilities are associated with the CISA catalog along with a risk score.
I would like to thank Jared Kalmus for his assistance and suggestions. As you may have noticed, most of the functions have been written with re-usability in mind. For example, check_for_custom_fields() has a custom field parameter instead of hardcoding “CISA” in the function. As always this code is in Kenna Security’s GitHub repository.
Until next time,