#!/usr/bin/perl ############################################################################### ## ## Functions for downloading and parsing Common Vulnerability DB data ## ############################################################################### use strict; use warnings; use LWP::UserAgent; use Time::ParseDate; # libtime-modules-perl use POSIX qw(mktime); use HTML::Parser; ## Get details of given CVE entry from NIST DB sub fetchCVE { my $cve_id = shift; my $cve_base_url = shift; my $cvss_base_url = shift; my $url; trace " Fetching $cve_id\n"; $cve_id =~ s/^CAN/CVE/; ## ## get CVE Scores from NVD ## $url = $cvss_base_url . $cve_id; my $cvss = get $url; #die " Unable to fetch CVE $cve_id" unless defined $cve; unless (defined $cvss) { error "Failed to download CVE: $url\n"; #return ""; # Don't die, we might still get proper date info from the CVE at MITRE.. $cvss = ""; } # Check for error pages: referenced but unpublished CVEs :-/ if ($cvss =~ /is\ valid\ CVE\ format,\ but\ CVE\ was\ not\ found/) { error " $cve_id does not exist in NIST DB\n"; #return ""; # Don't die, we might still get proper date info from the CVE at MITRE.. } ## ## get CVE from MITRE ## $url = $cve_base_url . $cve_id; my $cve = get $url; unless (defined $cve) { error "Failed to download CVE: $url\n"; return ""; } # Check for error pages: referenced but unpublished CVEs :-/ if ($cvss =~ /Could\ not\ find\ a\ CVE\ entry\ or\ candidate\ named/) { error " $cve_id does not exist in MITRE DB\n"; return ""; } return (join ("",$cvss, $cve)); } ## Get CVE severity rating and report date and return ## (date base-score impact-score exploit-score) sub parseCVE($$) { my $cve_id = shift; my $cve = shift; # use worst-case info as defaults my $cve_date = parsedate("today"); my $cve_base = 10; my $cve_impact = 10; my $cve_exploit = 10; if ($cve eq "") { # No details means we assume worst-case (highest score, recent bug) if ($cve_id =~ /LOCAL-(.*)/) { note " Assuming worst-case ratings for LOCAL CVE $cve_id.."; $cve_date = parsedate("$1", NO_RELATIVE => 1, VALIDATE =>1); } else { error " Got bad data for CVE/CVSS $cve_id, using worst-case defaults.."; } return ($cve_date, $cve_base, $cve_impact, $cve_exploit); } # Reporting Date -> $cve_date unless ($cve =~ /Assigned:?\s+\((\d{4})(\d{2})(\d{2})\)<\/td>/ || $cve =~ /
CVE Version:\s+(\d{4})(\d{2})(\d{2})<\/div>/ || $cve =~ /Candidate assigned on\s+(\d{4})(\d{2})(\d{2})/) { error "\n\n$cve\nUnable to extract date from above CVE, assume worst-case (today)\n"; } else { $cve_date = parsedate("$2/$3/$1", NO_RELATIVE => 1, VALIDATE =>1); } # Base Score -> $cve_base unless ($cve =~ /CVSS\sv2\sBase\sScore:<\/span>(.*)<\/a>\s\(\w+\)/) { error " Unable to parse CVSSv2 Base Score for $cve_id, assume worst-case\n"; } else { $cve_base = $1; } # Impact Subscore -> $cve_impact unless ($cve =~ /Impact\ Subscore:<\/span>\s+(.*)<\/div>/) { error " Unable to parse CVSSv2 Impact Score for $cve_id, assume worst-case\n"; } else { $cve_impact = $1; } # Exploitability Subscore -> $cve_exploit unless ($cve =~ /Exploitability\ Subscore:<\/span>\s+(.*)<\/div>/) { error " Unable to parse CVSSv2 Exploitability Score for $cve_id, assume worst-case\n"; } else { $cve_exploit = $1; } #print "$cve_base\n"; #print "$cve_impact\n"; #print "$cve_exploit\n"; return ($cve_date, $cve_base, $cve_impact, $cve_exploit); } 1;