301 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /*  XML parser for the NIST's National Vulnerability Database (NVD)
 | |
| *   Open Source license, NCSA/U. of Illinois
 | |
| *   Version 1.01, September 2, 2005
 | |
| *   Pascal Meunier
 | |
| *   Purdue University CERIAS
 | |
| *   Contact: pmeunier
 | |
| 
 | |
| *  Uses the basic XML parser included with PHP
 | |
| *  that doesn't perform schema validation
 | |
| *   see http://us2.php.net/xml
 | |
| *
 | |
| *  The NVD's XML schema doesn't have conflicting name tags
 | |
| *  at various nesting levels, so its parsing is relatively
 | |
| *  simple.  Therefore, a flat namespace (independent of nesting level)
 | |
| *  works fine.  This simple approach doesn't work, for example, with Microsoft's
 | |
| *  XML export of vulnerabilities.
 | |
| 
 | |
| *  Note that this parser does not have cases for all tags in the NVD schema.
 | |
| *  Simply add "case" statements for the additional tags that matter to you.
 | |
| *
 | |
| *  The data from parsing needs further validation or escaping if it is
 | |
| *  to be inserted into another database.
 | |
| 
 | |
| *  -Replace the first line with the path to your installation of PHP
 | |
| *  for command line execution.  Delete the path to PHP on the first line for
 | |
| *  execution with an Apache web server.
 | |
| *  -Choose a source for the XML file (see below)
 | |
| 
 | |
| *ChangeLog:
 | |
| * 1.0 first version August 29, 2005
 | |
| * 1.01 September 2, 2005
 | |
|    -Changed the name nvdcve-1999-2002.xml to nvdcve-2002.xml to match nvd changes
 | |
|    -Removed comment "from Apache" regarding https locations, as they work from the command line too, depending on how PHP was installed
 | |
|    -Now uses command line argument if present as XML source
 | |
|    -Now captures the content of <REF> tags
 | |
| */
 | |
| 
 | |
| error_reporting (E_ALL);
 | |
| 
 | |
| $debug = true; // will print messages about every field found if true
 | |
| $count_entries = 0; // count number of new CVE entries found today
 | |
| 
 | |
| // Choose a source for parsing
 | |
| // Check if a source was passed as argument
 | |
| if (isset($argv[1])) {
 | |
|    parse_this($argv[1]);
 | |
| } else {
 | |
|    parse_this("nvdcve-2004.xml");
 | |
|    // other possibilities:
 | |
|    //parse_this("http://nvd.nist.gov/download/nvdcve-2002.xml");
 | |
|    //parse_this("http://nvd.nist.gov/download/nvdcve-2003.xml");
 | |
|    //parse_this("http://nvd.nist.gov/download/nvdcve-2004.xml");
 | |
|    //parse_this("http://nvd.nist.gov/download/nvdcve-2005.xml");
 | |
|    //parse_this("http://nvd.nist.gov/download/nvdcve-2006.xml");
 | |
|    //
 | |
|    // The following do not work with vanilla PHP installs
 | |
|    // without the wrapper "https" (e.g., MacOS X default install)
 | |
|    //parse_this("https://nvd.nist.gov/download/nvdcve-2002.xml");
 | |
|    //parse_this("https://nvd.nist.gov/download/nvdcve-2003.xml");
 | |
|    //parse_this("https://nvd.nist.gov/download/nvdcve-2004.xml");
 | |
|    //parse_this("https://nvd.nist.gov/download/nvdcve-2005.xml");
 | |
|    //parse_this("https://nvd.nist.gov/download/nvdcve-2006.xml");
 | |
| }
 | |
| 
 | |
| echo "Found $count_entries entries in this NVD location".PHP_EOL;
 | |
| die; // not necessary, added for clarity
 | |
| 
 | |
| function startElement($parser, $name, $attrs) {
 | |
|    global $all_data, $CVE, $debug, $insert, $vendor, $product, $count_entries, $desc_type;
 | |
|    if ($debug) {
 | |
|       echo "processing tag named '$name'".PHP_EOL;
 | |
|    }
 | |
|    //
 | |
|    // the switch statement should enumerate all of the possible name tags.
 | |
|    // State is kept using global variables, so that nested tags can
 | |
|    // access their context.
 | |
|    //
 | |
|    switch ($name) {
 | |
|       case 'ENTRY':
 | |
|          $count_entries++;
 | |
|          // get name and publication date
 | |
|          $CVE = validate_CVE($attrs['NAME']);
 | |
|          $published = validate_date($attrs['PUBLISHED']);
 | |
|          // other attributes, such as severity, are available
 | |
|          // see http://nvd.nist.gov/download/nvdcve-xmldoc.cfm
 | |
|          if ($debug) {
 | |
|             echo "found CVE entry $CVE with publication date {$attrs['PUBLISHED']}".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'DESCRIPT':
 | |
|          // if there ever was more than one type of description
 | |
|          // remember which source with a global
 | |
|          // NVD or CVE
 | |
|          $desc_type = $attrs['SOURCE'];
 | |
|          // reset data accumulator
 | |
|          $all_data = '';
 | |
|          // need to wait for end tag to get contents
 | |
|          break;
 | |
|       case 'PROD':
 | |
|          $vendor = $attrs['VENDOR'];
 | |
|          $product = $attrs['NAME'];
 | |
|          if ($CVE == '') {
 | |
|             echo "error, no CVE number";
 | |
|             die;
 | |
|          }
 | |
|          if ($debug) {
 | |
|             echo "found vendor $vendor";
 | |
|             echo " found product $product".PHP_EOL;
 | |
|          }
 | |
|          if ($vendor == "") {
 | |
|             // this happens
 | |
|             echo "NVD integrity alert: no vendor for product $product, CVE is $CVE".PHP_EOL;
 | |
|          }
 | |
|          if ($product == "") {
 | |
|             echo "NVD integrity alert: no product, CVE is $CVE".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'LOSS_TYPES':
 | |
|          break;
 | |
|       case 'VULNS_TYPES':
 | |
|          break;
 | |
|       case 'REF':
 | |
|          // source, url, etc...
 | |
|          if ($debug) {
 | |
|             echo " reference from {$attrs['SOURCE']}";
 | |
|             echo " is available at {$attrs['URL']}";
 | |
|             echo PHP_EOL;
 | |
|          }
 | |
|          $all_data = '';
 | |
|          // need to wait for end tag to get contents
 | |
|          break;
 | |
|       case 'VERS':
 | |
|          if ($debug) {
 | |
|             echo " version {$attrs['NUM']}";
 | |
|             echo " of product $product is vulnerable".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case '':
 | |
|          break;
 | |
|    }
 | |
| }
 | |
| 
 | |
| function endElement($parser, $name) {
 | |
|    global $all_data, $CVE, $debug;
 | |
|    switch ($name) {
 | |
|       case 'ENTRY':
 | |
|          $CVE = '';
 | |
|          break;
 | |
|       case 'DESCRIPT':
 | |
|          if ($CVE == '') {
 | |
|             echo "error, no CVE number";
 | |
|             die;
 | |
|          }
 | |
|          if ($debug) {
 | |
|             echo "found description $all_data".PHP_EOL." for CVE entry $CVE".PHP_EOL;
 | |
|          }
 | |
| 
 | |
|          // reset data accumulator
 | |
|          $all_data = '';
 | |
|          break;
 | |
|       case 'REF':
 | |
|          if ($debug) {
 | |
|             echo "found reference content '$all_data'".PHP_EOL." for CVE entry $CVE".PHP_EOL;
 | |
|          }
 | |
|          // reset data accumulator
 | |
|          $all_data = '';
 | |
|          break;
 | |
|       case 'AVAIL':
 | |
|          if ($debug) {
 | |
|             echo "loss type is availability".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'CONF':
 | |
|          if ($debug) {
 | |
|             echo "loss type is confidentiality".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'INT':
 | |
|          if ($debug) {
 | |
|             echo "loss type is integrity".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'SEC_PROT':
 | |
|          if ($debug) {
 | |
|             echo "loss type is security protection".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'ACCESS':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is access".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'INPUT':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is input".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'DESIGN':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is design".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'EXCEPTION':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is exception".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'ENV':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is environment".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'CONFIG':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is configuration".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'RACE':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is race".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'OTHER':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability type is other".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'REMOTE':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability range is remote".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'LOCAL':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability range is local".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case 'USER_INIT':
 | |
|          if ($debug) {
 | |
|             echo "vulnerability range is through a user".PHP_EOL;
 | |
|          }
 | |
|          break;
 | |
|       case '':
 | |
|          break;
 | |
|    }
 | |
| }
 | |
| 
 | |
| function characterData($parser, $data) {
 | |
|    global $all_data;
 | |
|    // concatenate due to bug with & according to a user entry in PHP docs
 | |
|    // entry by sam at cwa dot co dot nz, Sept 2000
 | |
|    // This issue might have been fixed later.  The fix can't hurt though.
 | |
|    $all_data .= $data;
 | |
| }
 | |
| 
 | |
| function parse_this($url) {
 | |
|    // code for this function is from http://us2.php.net/xml
 | |
|    $xml_parser = xml_parser_create();
 | |
|    // use case-folding so we are sure to find the tag in $map_array
 | |
|    xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
 | |
|    xml_set_element_handler($xml_parser, "startElement", "endElement");
 | |
|    xml_set_character_data_handler($xml_parser, "characterData");
 | |
|    if (!($fp = fopen($url, "r"))) {
 | |
|       die("could not open XML input");
 | |
|    }
 | |
|    while ($data = fread($fp, 4096)) {
 | |
|       if (!xml_parse($xml_parser, $data, feof($fp))) {
 | |
|           die(sprintf("XML error: %s at line %d",
 | |
|                       xml_error_string(xml_get_error_code($xml_parser)),
 | |
|                       xml_get_current_line_number($xml_parser)));
 | |
|       }
 | |
|    }
 | |
|    xml_parser_free($xml_parser);
 | |
| }
 | |
| 
 | |
| function validate_CVE($input) {
 | |
|    // expecting something like CAN-2004-0002
 | |
|    // NVD documentation suggests this reg exp,
 | |
|    // (http://nvd.nist.gov/download/nvdcve-xmldoc.cfm)
 | |
|    // (CAN|CVE)-/d/d/d/d-/d/d/d/d
 | |
|    // which doesn't work.  See below
 | |
|    if (preg_match('/(CAN|CVE)\-\d\d\d\d\-\d+/', $input, $matches)) {
 | |
|       return $matches[0];
 | |
|    } else {
 | |
|       // if there was an error (false) or if 0 matches
 | |
|       die ("Invalid CVE number encountered: $input");
 | |
|    }
 | |
| }
 | |
| 
 | |
| function validate_Date($input) {
 | |
|    // expecting something like 2004-03-03
 | |
|    if (preg_match('/\d{4}\-\d{2}\-\d{2}/', $input, $matches)) {
 | |
|       return $matches[0];
 | |
|    } else {
 | |
|       // if there was an error (false) or if 0 matches
 | |
|       die ("Invalid date encountered: $input");
 | |
|    }
 | |
| }
 | |
| 
 | |
| ?>
 |