Understanding Different Techniques for Vulnerability Prioritization
In the last article, we discussed how vulnerability is indeed a growing problem. More code, more CVEs, accelerated deployment speed are all contributors to the challenge of trying to protect your organization's code against bad actors. We also concluded that the strategy on how we approach vulnerability prioritization also needed to evolve.
There’s a silver lining: As much as vulnerability grew as a problem, possible tools and techniques to find, prioritize, and fix vulnerabilities also grew.
Vulnerability prioritization evolution
The Open Worldwide Application Security Project (OWASP), founded in 2001, is a nonprofit foundation that works to improve the security of software. Among many different projects, they maintain the OWASP Juice Shop, which probably is the most modern and sophisticated purposefully insecure web application that can be used for security research. We are going to use it for the examples below.
First, we want to understand what are, if any, the dependency vulnerabilities that are part of this application. For that job, there's no better tool than a Software Composition Analysis (SCA), that tries to detect publicly disclosed vulnerabilities contained within a project’s dependencies. There are multiple different SCA scanners in the market, both in commercial and open-source flavors. OWASP itself maintains the Dependency Check, a open-source SCA. Another great option that is commercial but has a free tier for open-source codebases is Snyk Open Source, which we will use here.
If you run a Snyk Open Source scan against juice-shop’s repository, at least as of 08/10/2024, you’d find the following:
- 959 total dependencies. Despite the application having 72 direct dependencies, each of them might have their own dependencies — which are called transitive dependencies to the original app — and they also might bring vulnerabilities into the application.
- 174 vulnerable paths. To understand this, imagine you are pinpointing a given vulnerability in one of the dependencies. Draw a line from this dependency, crossing through this dependency's dependency and all the way to the actual application. You'll find 174 of these in the application.
- 77 known unique vulnerabilities. As you might've guessed, since this number is lower than the number of vulnerable paths, there are vulnerabilities that repeat themselves in different paths, like the application depending on two different dependencies that depend on the same one vulnerable dependency. See the image below for a visual explanation.
This is a purposefully vulnerable application, like it was mentioned before, so we are definitely expecting multiple vulnerabilities here. However, having a modern application vulnerable to 77 unique vulnerabilities isn’t unheard of, so let’s discuss how we could prioritize them.
The usual way
The U.S. Department of Commerce’s National Institute of Standards and Technology (NIST) maintains the National Vulnerability Database (NVD). The NVD is synchronized with CVE such that any updates to the CVE List — explained in the previous article — appear in the NVD, which augments the CVE List with additional enrichment.
One of the 77 unique vulnerabilities that were found by the SCA scan above, was the CVE-2023–37466. The NVD has an entry on this CVE, and it can be seen below:
The traditional way to vulnerability prioritization has been to use the Common Vulnerability Scoring System (CVSS) score. This is because there's a well-defined and tested formula, revised regularly, of how that score is calculated, defining a qualitative measure of severity. So, the expectation is that an organization should handle a vulnerability with CVSS score 10 prior to one with score 9.9.
The NVD entry goes beyond CVSS score, though. The score is calculated using different metrics and data, for instance what the Attack Vector is, whether privileges are required to exploit the vulnerability, etc. For instance, one could prioritize vulnerabilities that have the Network as Attack Vector only if the application is, indeed, exposed in the network.
This is a basic way of looking at risk, instead of looking simply at vulnerability severity, since a network exploitable vulnerability in a network exposed application is riskier than the same vulnerability in a non-exposed application.
Will it be exploited, though?
Expanding the idea of looking at risk versus severity, how useful would it be if one could predict what the chances are of having any given vulnerability exploited if directly exposed to the internet? A lot, and that's exactly why the good folks at the global Forum of Incident Response and Security Teams (FIRST) do exactly that since 2021.
From their own definition, the Exploit Prediction Scoring System (EPSS) is a data-driven effort for estimating the likelihood (probability) that a software vulnerability will be exploited in the wild. For any given CVE, a EPSS entry is provided and it contains:
- epss : the EPSS score representing the probability [0–1] of exploitation in the wild in the next 30 days (following score publication)
- percentile : the percentile of the current score, the proportion of all scored vulnerabilities with the same or a lower EPSS score
Access to this data is free and couldn’t be easier to access. In case you have a SCA tool, verify if this data isn't already there, since many of them, like Snyk, have this data. Or simply make a HTTP GET request to the EPSS’ API, no authentication required. Example:
curl -s https://api.first.org/data/v1/epss\?cve\=CVE-2023-37466 | jq .
{
"status": "OK",
"status-code": 200,
"version": "1.0",
"access-control-allow-headers": "x-requested-with",
"access": "public",
"total": 1,
"offset": 0,
"limit": 100,
"data": [
{
"cve": "CVE-2023-37466",
----->"epss": "0.008380000",<-----
----->"percentile": "0.824310000",<-----
"date": "2024-09-09"
}
]
}
With that we can easily see that the CVE-2023–37466’s EPSS is 0.008380000, meaning that the probability of having this CVE exploited in the wild in the next 30 days is of only 0.838%.
Now let's make the exercise of plotting all vulnerabilities with CVE in a bidimensional scatterplot graphic, where the CVE is positioned according to its CVSS Score (x axis) and its EPPS Probability (y axis). Let's now draw a 45 degree line from (0,0), all the way to the top. The vulnerabilities closer to the top of this line can be considered riskier and should be prioritized first, because they have a high CVSS to EPPS combination, while vulnerabilities close to its bottom can be deprioritized. See below:
In summary, EPSS is another excellent tool that the AppSec community can leverage in order to help prioritize vulnerabilities. In this case here, we can see that CVE-2019–10744, with a CVSS score of 9.1 and EPSS score of 02082, is risker than the CVE-2023–37466 discussed above, since it lands further away from the top of the line that we draw.
Am I even running the vulnerable code?
There’s another data point that has been gaining some traction recently on helping to prioritize vulnerability handling. Let’s pretend for a moment that we have a “math” package, version 1.0, where there’s a critical vulnerability on the “multiply” function that allows Code Injection. This means anyone running an application that depends on version 1.0 of this package would be running an application that depends on a vulnerable package. But is the application really vulnerable?
If said application indeed uses the vulnerable multiply function, this dependency should be updated as soon as possible to a non-vulnerable version. But what if the application never used the vulnerable multiply function in the first place? Although I particularly believe that this vulnerable dependency shouldn’t be ignored, since in the future the app developer might decide to leverage this vulnerable function without remembering or knowing that it is vulnerable, many believe this vulnerability should have its priority lowered when compared to an identical vulnerability that actually triggers vulnerable code, since it is less risky than the others. This concept is widely known as reachability.
See a representation of this concept below:
In the example above, considering both vulnerabilities have the same CVE and EPSS, it makes more sense to invest time to fix the vulnerable dependency xyz 1.0 than fixing math 1.0, since the vulnerable multiply function isn’t reached by the application.
Although I couldn’t find an open source tool that enables anyone to do this kind of analysis easily, some commercial SCA scanners, Snyk’s included, can find reachable vulnerabilities and leverage this data to help its user prioritize vulnerability remediation.
What about understanding which vulnerability to fix among different applications?
So far we’ve discussed strategies of how to prioritize vulnerabilities amongst the same application. The reality is that one might be responsible for prioritizing vulnerability fixing between many different applications, some of which you may at first lack awareness of. How to prioritize them?
For this scenario let’s pretend a new high profile critical vulnerability was released, just like log4shell once was, and one is trying to figure out first, if their applications are affected, and second, what the order of priority should be when it comes to fixing it across multiple different applications.
Dependency tracking
The easiest way to figure out if an application is affected by a specific CVE, even without running it against any kind of scanner, is to know what dependencies the application has. There are many tools out there that one could leverage, including a properly named Dependency Track from OWASP or, you guessed it, Snyk.
The simplest way to achieve that, is to make sure that you have a Software Bill of Materials (SBOM) of each one of the current builds of your projects. SBOM is a big hot topic on its own and it extrapolates the goal of this article to go over it in detail. What you should know right now is that a SBOM is, in the most simplistic definition, a document that keeps track of all dependencies that are part of a given application and their versions. So one could just check if the CVE’s affected dependency and versions are part of any of their applications SBOMs.
Application context that you didn’t know you had
Now that one knows that applications are affected by this fictional CVE, how to prioritize among them? Unfortunately, the reality is that much of the workforce involved in protecting applications is unaware of their context. But maybe they might have access to more context than they realized at first.
It’s not rare to find organizations where the development team is using some kind of Internal Developer Portal (IDP), such as the CNCF’s Backstage, originally created by Spotify. Some other examples that you might be familiar with ServiceNow CMDB, Atlassian Compass, Datadog Service Catalog, Harness, OpsLevel, etc.
This kind of platform hosts interesting metadata that might be valuable for this assessment. See the example below:
From a glance we can see to which System an application belongs to, its owner, or if it’s in production and running. This is huge! A vulnerability in an application that is running in production and is part of some kind of Payment system, definitely is riskier than having the same vulnerability in an application that is still in staging, and belongs to some kind of a minor system.
If you are in charge of vulnerability prioritization in your organization, make sure you check with your Dev or SRE teams if your organization is already using some kind of IDP in order to help you do your prioritization job.
Is it even in the wild?
But deployed doesn’t necessarily mean public facing. Most likely, an application that is public facing will be in a riskier position than one at is internal facing only.
Although there’s an infinitude of ways, depending on the tech stack, to verify if an application is public facing, we can use Kubernetes as an example here. See below:
$kubectl get ingress my-app
NAME HOSTS ADDRESS PORTS AGE
my-app * DNS-Name-Of-Your-ALB 80 15m
$curl DNS-Name-Of-Your-ALB
Hello World!
This way we quickly checked that this application has an Application Load Balancer attached to it and is reachable from the internet, meaning it poses a higher risk than one that isn't.
Show me the Memory
What if there are more than one critical applications that are in production and public facing, which one is in a riskier position then? An additional strategy might be to figure out if the vulnerable dependency is even loaded in memory.
This isn't trivial since to get this information on demand usually means instrumenting your runtime environment and/or your application. So, if you organization is using an AppSec platform like Snyk, or maybe a continuous profiling platform, you might be able to get this information to help your prioritization task.
Conclusion
In the first article of this series, I explained how my journey to answer if there actually were more published vulnerabilities today than ever before, led me to a resound yes as an answer. But I also mentioned how it triggered my curiosity on how modern high performing AppSec teams are handling this constant stream of new vulnerabilities.
In this article, we saw how the techniques to help prioritize vulnerabilities based on Risk also evolved over time. Tools like EPPS, Reachability and IDPs can and should be used to help identify where the highest risk lies in your organization so you can focus on what matters the most: protecting your customer data.
I hope you have learned as much as I did while I researched and wrote the two longest articles I've ever written. Were you already aware of these tools? Which one do you use the most? Which one were the most surprising to you?