Using the NVD Database and API to Keep Up with Vulnerabilities and Patches – Tool Drop: CVEScan (Part 3 of 3), (Mon, Jan 11th)

This post was originally published on this site

Now with a firm approach to or putting an inventory and using the NVD API (https://isc.sans.edu/forums/diary/Using+the+NIST+Database+and+API+to+Keep+Up+with+Vulnerabilities+and+Patches+Part+1+of+3/26958/ and https://isc.sans.edu/forums/diary/Using+the+NIST+Database+and+API+to+Keep+Up+with+Vulnerabilities+and+Patches+Playing+with+Code+Part+2+of+3/26964/), for any client I typically create 4 inventories:

  • Devices/appliances and applications that are exposed at the perimeter (public internet or other firewalled trust boundary)
  • Server applications and devices/appliances
  • Workstation  applications
  • IOT devices (in workstation or dedicated VLANs/subnets)

As we've noted, you can use nmap as a short-cut for a first draft of any products that have listening ports.  To just get your the CPE list for a subnet or range of IPs, this does the trick nicely:

nmap -p <ports> -sV –open <subnet> | grep -i cpe | awk -F" "  "{print $NF}"

(-F gives the delimiter, the print command prints the $NF field.  Since $NF is the number of fields, it prints the last one, which happens to be the CPE).

Let's focus on the first one of these target list – perimeter services for an actual customer.
Starting with Cisco FTD (Firepower Threat Defense), we see that even the titles vary from version to version, and the versions are very granular for this product
>type official-cpe-dictionary_v2.3.xml | grep -i title | grep -i cisco | grep -i firepower | grep -i -v management | grep -i "threat defense"
( excerpt only)

    <title xml:lang="en-US">Cisco Firepower Threat Defense (FTD) 6.5.0.3</title>
    <title xml:lang="en-US">Cisco Firepower Threat Defense 6.5.0.5</title>
    <title xml:lang="en-US">Cisco Firepower Threat Defense 6.6.0</title>
    <title xml:lang="en-US">Cisco Firepower Threat Defense (FTD) 6.6.1</title>

Since this is such a lengthy (and version-specific) list, let's try to consolidate.  From cisco's download site, the latest and recommended version (as of today) is 6.6.1.  Knowing that this client will be "close to current" on this, a quick look for FTD 6.6:

"cpe:2.3:a:cisco:firepower_threat_defense:6.6.*:*:*:*:*:*:*:*"

gives us these hits:

cpe:2.3:a:cisco:firepower_threat_defense:6.6.0:*:*:*:*:*:*:*
cpe:2.3:a:cisco:firepower_threat_defense:6.6.1:*:*:*:*:*:*:*

So our final input data file has the following (hostname followed by the cpe "blanket" query):

"dc01-fw01","cpe:2.3:a:cisco:firepower_threat_defense:6.6.*"

Let's add in the Citrix Netscaler Gateway (now called ADC).  The ADC is a pretty versatile appliance, it can be a load balancer, a firewall, a front-end for a Citrix farm, or (just like everyone else these days) and SD-WAN solution.  In our case it's a front-end for a Citrix XenServer farm.
The current version is 13.x, so let's search for all of 13.*:

cpe:2.3:o:citrix:application_delivery_controller_firmware:13.*

Finally, this client also has an application that uses Apache Struts, which they have been very particular about monitoring since the Equifax breach:
The current stable version is 2.5.26, let's hunt for cpe:

cpe:2.3:a:apache:struts:2.5.*

So our perimeter input file will look like this (again, the fields are hostname,cpe):

"dc01-fw01","cpe:2.3:a:cisco:firepower_threat_defense:6.6.*"
"dc01-adc01","cpe:2.3:o:citrix:application_delivery_controller_firmware:13.*"
"dc01-appsrv01","cpe:2.3:a:apache:struts:2.5.*"

We'll call our code with (note the input filename):

cvescan.ps1 -i Customername.Perimeter.in -d 90

This will give us the CVEs for the indicated platforms, for the last 90 days, sorted from high severity to low.

And our code will look like the listing below (maintained at https://github.com/robvandenbrink/CVEScan ):

##########################################################################

# CVESCAN

# Version 1.iscisc0

# Assess an inventoried infrastructure from pre-inventoried CPEs and published CVEs

#

# Hosted at https://github.com/robvandenbrink/CVEScan

#

# Further documentation at:

#         https://isc.sans.edu

#         https://isc.sans.edu

#         https://isc.sans.edu

#

# Syntax:

#         CVEScan.ps1  -i <input file> -d <how many days back to look>

##########################################################################

 

param (

[alias("i")]

$infile,

[alias("d")]

$daterange

)

 

function helpsyntax {

write-host "CVESCAN: Assess a known inventory against current CVEs"

write-host "Parameters:"

write-host "    -i          <input file name>"

write-host "Optional Parameters:"

write-host "    -d          <CVEs for last "n" days>"

write-host "cvescan -i perimeterdevices.in -d 60"

exit

}

 

if ($daterange -eq 0) { write-host "ERROR: Must specify input filename and date range`n" ; helpsyntax }

 

# setup

$allCVEs = @()

$CVEDetails = @()

 

$apps = Import-Csv -path $infile

$now = get-date

$outfile = $infile.replace(".in",$now.tostring("yyyy-MM-dd_hh-mm")+"_"+$daterange+"-days.html")

$StartDate = $now.adddays(-$daterange).tostring("yyyy-MM-dd")+ "T00:00:00:000%20UTC-00:00"

 

# Collect host to CVEs table

foreach ($app in $apps) {

    $request = "https://services.nvd.nist.gov/rest/json/cves/1.0?modStartDate=" + $StartDate + "&cpeMatchString=" + $app.cpe

    $CVEs = (invoke-webrequest $request | ConvertFrom-Json).result.CVE_items.cve.CVE_data_meta.id

    foreach ($CVE in $CVEs) {

        $tempobj = [pscustomobject]@{

            Hostname = $app.hostname

            CVE = $CVE

           }

        $allCVEs += $tempobj

        }

    }

 

$Header = @"

<style>

TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}

TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;}

TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;VERTICAL-ALIGN: TOP; font-size: 15px}

</style>

"@

 

$filepath = gci $infile

 

$Title = @()

$Title += [pscustomobject]@{ Organization="Scope";bbb=$filepath.basename.split(".")[1] }

$Title += [pscustomobject]@{ Organization="From Date:"; bbb=($now.adddays(-$daterange).tostring("yyyy-MM-dd")) }

$Title += [pscustomobject]@{ Organization="To Date:";bbb=$now.tostring("yyyy-MM-dd") }

 

(($Title | convertto-HTML -title "CVE Summary" -Head $header) + "<br><br><br>").replace("bbb",$filepath.basename.split(".")[0]) | out-file  $outfile

 

(($allCVEs | Convertto-HTML -Head $header) + "<br><br>") | out-file -append $outfile

 

#parse out just the CVEs

$justCVEs = $allCVEs | select CVE | Sort-Object | Get-Unique -AsString

 

# collect CVE info

foreach ($CVE in $justCVEs) {

    $h = ""

    $request = "https://services.nvd.nist.gov/rest/json/cve/1.0/" + $CVE.CVE

    $cvemetadata = (invoke-webrequest $request) | convertfrom-json

    $CVEURLs = $cvemetadata.result.cve_items.cve.references.reference_data.url

    $affectedApps = ($cvemetadata.result.CVE_items.configurations.nodes.children.cpe_match) | where {$_.vulnerable -eq "true" } | select cpe23Uri,versionendincluding

 

    # add the affected hosts back into the detailed listing

    # write-host $CVE.CVE

    foreach ($ac in $allCVEs) {

        if ($ac.CVE -eq $CVE.CVE) {

            $h += ($ac.Hostname + "<br>")

            }

        }

 

    $tempobj = [pscustomobject]@{

        CVE = $CVE.CVE

        Hosts = $h

        # Just the datestamp, remove the clock time

        "Published Date" = ($cvemetadata.result.cve_items.publishedDate).split("T")[0]

        "CVE Description" = $cvemetadata.result.cve_items.cve.description.description_data.value

        Vector = $cvemetadata.result.CVE_items.impact.basemetricv3.cvssv3.attackVector

        "Attack Complexity" = $cvemetadata.result.CVE_items.impact.basemetricv3.cvssv3.attackComplexity

        "User Interaction" = $cvemetadata.result.CVE_items.impact.basemetricv3.cvssv3.userInteraction

        "Base Score" = $cvemetadata.result.CVE_items.impact.basemetricv3.cvssv3.baseScore

        "Severity" = $cvemetadata.result.CVE_items.impact.basemetricv3.cvssv3.baseSeverity

        "Reference URLs" = ($CVEURLs | ft -hidetableheaders | out-string).replace("`n","`n<br>")

        "Affected Apps" = ($affectedapps | ft -HideTableHeaders | out-string).replace("`n","`n<br>")

        }

    $CVEDetails += $tempobj

    }

 

# to just view the detailed output

# $CVEDetails | out-gridview

 

# to output to HTML

$Header = @"

<style>

TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}

TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;}

TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;VERTICAL-ALIGN: TOP; font-size: 15px}

</style>

"@

 

# Note that the <br> tags get escaped, these are un-escaped below

# this is a horrible hack, but I can't find a decent "elegant" way to do this

# … in less than 5x the time it took me to do it the ugly way  🙂

 

 

(($CVEDetails | sort -descending -property "Base Score" )| Convertto-HTML -Head $header) -replace '&lt;br&gt;', '<br>' | out-file  -append $outfile

 

Our output is dumped into: Customername.Perimeter-dateandtime-days.html, so for this example and today's date: Customername.Perimeter2021-01-11_09-50_90-days.html (note that the output filename mirrors the input filename – change that if you need)

Note also in the output that I had to un-escape all of the line breaks that were in the output (sometimes the quick and dirty methods win over perfect code)

Looking at that file, our output (truncated) looks as below.  The lead in is the customer and date range info, followed by the CVE's found on which host.  The final table contains all the CVE details, in descending / unique order of "Base Score" of Severity:

 

 As mentioned, the code is on my github – use it or modify it to suit your needs.  For the most part it's a short list of API requests, with parsing, formatting and I/O bolted on – so if you'd prefer this to be in a different language of course feel free!

If you were able to head off a "situation" in your environment, or if that nmap trick finds something unexpected in your environment, please do post to our comment form (subject to NDA's of course)

===============
Rob VandenBrink
rob@coherentsecurity.com

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.