[PATCH 44 of 54] Add CVE parsing (from OpenVAS GSA)

Wald Commits scm-commit at wald.intevation.org
Wed Jan 7 10:57:01 CET 2015


# HG changeset patch
# User Benoît Allard <benoit.allard at greenbone.net>
# Date 1419867214 -3600
# Node ID b87f2a6e613a0399b3ca2431583fe59d9d193036
# Parent  9ed24f48df016a415524db9b192b679df9a5dea3
Add CVE parsing (from OpenVAS GSA)

diff -r 9ed24f48df01 -r b87f2a6e613a CHANGES
--- a/CHANGES	Mon Dec 29 15:00:59 2014 +0100
+++ b/CHANGES	Mon Dec 29 16:33:34 2014 +0100
@@ -4,6 +4,7 @@
 Main changes since FarolLuz 1.0:
 --------------------------------
 * Add parsing for CPE.
+* Add parsing for CVEs format from the OpenVAS Greenbone Security Manager.
 
 
 FarolLuz 1.0 (2014-11-05)
diff -r 9ed24f48df01 -r b87f2a6e613a farolluz/document.py
--- a/farolluz/document.py	Mon Dec 29 15:00:59 2014 +0100
+++ b/farolluz/document.py	Mon Dec 29 16:33:34 2014 +0100
@@ -82,6 +82,7 @@
 class CVRFTracking(object):
     STATUSES = ('Draft', 'Interim', 'Final')
     def __init__(self, _id, status, version, initial, current):
+        """ version must be a tuple of (max four) ints """
         self._identification = _id
         self._status = status
         self._version = version
@@ -148,6 +149,7 @@
 
 class CVRFRevision(object):
     def __init__(self, number, date, description):
+        """ number is a tuple of (max four) ints """
         self._number = number
         self._date = date
         self._description = description
diff -r 9ed24f48df01 -r b87f2a6e613a farolluz/parsers/cve.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/parsers/cve.py	Mon Dec 29 16:33:34 2014 +0100
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# Description:
+# Methods for parsing CVE XML documents
+#
+# Authors:
+# Benoît Allard <benoit.allard at greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Methods for parsing of CVE XML Documents
+
+Ref: http://scap.nist.gov/schema/vulnerability/0.4
+"""
+
+from __future__ import absolute_import
+
+import xml.etree.ElementTree as ET
+
+from .xml import parseDate
+
+from ..common import CVRFNote, CVRFReference
+from ..document import CVRF, CVRFPublisher, CVRFTracking, CVRFTrackingID, CVRFRevision
+from ..producttree import CVRFFullProductName
+from ..utils import utcnow
+from ..vulnerability import CVRFVulnerability, CVRFCVSSSet, CVRFCWE, CVRFProductStatus
+
+NAMESPACES = {
+    'cve':  "http://scap.nist.gov/schema/feed/vulnerability/2.0",
+    'vuln': "http://scap.nist.gov/schema/vulnerability/0.4",
+    'cvss': "http://scap.nist.gov/schema/cvss-v2/0.2",
+    'xml':  "http://www.w3.org/XML/1998/namespace",
+}
+
+
+def UN(ns, name):
+    """ returns a Universal Name """
+    return "{%s}%s" % (NAMESPACES[ns], name)
+
+def parseCVSS(xmlElem):
+    """ Make a vector out of a list of elements """
+    def get(name):
+        return xmlElem.findtext('/'.join([UN('cvss', 'base_metrics'), UN('cvss', name)]))
+
+    cvss_set = CVRFCVSSSet(float(get('score')))
+    vector = [
+        'AV:%s' % {'LOCAL': 'L',
+                   'ADJACENT_NETWORK': 'A',
+                   'NETWORK': 'N'}[get('access-vector')],
+        'AC:%s' % {'HIGH': 'H',
+                   'MEDIUM': 'M',
+                   'LOW': 'L'}[get('access-complexity')],
+        'Au:%s' % {'MULTIPLE': 'M',
+                   'SINGLE': 'S',
+                   'NONE': 'N'}[get('authentication')],
+        'C:%s' %  {'NONE': 'N',
+                   'PARTIAL': 'P',
+                   'COMPLETE': 'C'}[get('confidentiality-impact')],
+        'I:%s'  % {'NONE': 'N',
+                   'PARTIAL': 'P',
+                   'COMPLETE': 'C'}[get('integrity-impact')],
+        'A:%s'  % {'NONE': 'N',
+                   'PARTIAL': 'P',
+                   'COMPLETE': 'C'}[get('availability-impact')],
+    ]
+    cvss_set.setVector('/'.join(vector))
+    return cvss_set
+
+def parseXML(data):
+    """ returns am ET.Element from the input stuff.
+    input can be:
+      - a string
+      - a file handle
+      - an ET.Element instance
+    """
+    if isinstance(data, ET.Element):
+        return data
+    # To allow passing file handles
+    if hasattr(data, 'read'):
+        data = data.read()
+    # Parse it.
+    return ET.fromstring(data)
+
+def parse_CVE_from_GSA(data):
+    xml = parseXML(data)
+    return parse(xml.find('/'.join(['get_info', 'get_info_response', 'info', 'cve', 'raw_data', UN('cve', 'entry')])))
+
+def parse(xml):
+    xml = parseXML(xml)
+
+    # Create an extra-minimal document
+    doc = CVRF(xml.findtext(UN('vuln', 'cve-id')),
+               'Vulnerability Description')
+    pub = CVRFPublisher("Other")
+    doc.setPublisher(pub)
+    now = utcnow()
+    tracking = CVRFTracking(
+        CVRFTrackingID('000000'),
+        "Draft",
+        (0,),
+        now, now
+    )
+    doc.setTracking(tracking)
+    tracking.addRevision(CVRFRevision((0,), now, 'Document created'))
+
+    # Add the CVE to that document
+    return addToDoc(doc, xml)
+
+def addToDoc(doc, xml):
+    """ Adds the CVE as vulnerability in the document """
+    xml = parseXML(xml)
+
+    vulnid = xml.attrib['id']
+
+    # Get a new ordinal for our new Vulnerability
+    if len(doc._vulnerabilities) == 0:
+        ordinal = 1
+    else:
+        ordinal = doc._vulnerabilities[-1]._ordinal + 1
+
+    # Create a Vulnerability
+    vuln = CVRFVulnerability(ordinal)
+    doc.addVulnerability(vuln)
+
+    vulnerable_products = []
+    # Set the vulnerable products in productTree
+    for i, cpe in enumerate(xml.findall(
+                                '/'.join([UN('vuln', 'vulnerable-software-list'),
+                                          UN('vuln', 'product')]))):
+        if doc._producttree is None:
+            doc.createProductTree()
+        try:
+            prod = doc._producttree.getProductForCPE(cpe.text)
+        except KeyError:
+            prod = CVRFFullProductName('%s-P%d' % (vulnid, i), cpe.text, doc._producttree, cpe.text)
+            doc._producttree.addProduct(prod)
+        vulnerable_products.append(prod)
+
+    if vulnerable_products:
+        status = CVRFProductStatus('Known Affected')
+        for product in vulnerable_products:
+            status.addProductID(product._productid)
+        vuln.addProductStatus(status)
+
+    # Add the CVE-id
+    vuln.setCVE(xml.findtext(UN('vuln', 'cve-id')))
+
+    # The release date
+    vuln.setReleaseDate(parseDate(xml.findtext(UN('vuln', 'published-datetime'))))
+
+    # Add the CVSS
+    xmlcvss = xml.find(UN('vuln', 'cvss'))
+    if xmlcvss is not None:
+        vuln.addCVSSSet(parseCVSS(xmlcvss))
+
+    # Add the CWE id
+    xmlcwe = xml.find(UN('vuln', 'cwe'))
+    if xmlcwe is not None:
+        # XXX: Get a Description for the CWE !
+        vuln.addCWE(CVRFCWE(xmlcwe.attrib['id'], xmlcwe.attrib['id']))
+
+    # Add references
+    for xmlref in xml.findall(UN('vuln', 'references')):
+        vuln.addReference(CVRFReference(xmlref.find(UN('vuln','reference')).attrib['href'],
+                                        xmlref.findtext(UN('vuln', 'reference'))))
+
+    xmlsummary = xml.findtext(UN('vuln', 'summary'))
+    if xmlsummary is not None:
+        vuln.addNote(CVRFNote(
+            'Summary',
+            1,
+            xmlsummary
+        ))
+
+    return doc
diff -r 9ed24f48df01 -r b87f2a6e613a farolluz/producttree.py
--- a/farolluz/producttree.py	Mon Dec 29 15:00:59 2014 +0100
+++ b/farolluz/producttree.py	Mon Dec 29 16:33:34 2014 +0100
@@ -51,6 +51,12 @@
                 return product
         raise KeyError(productid)
 
+    def getProductForCPE(self, cpe):
+        for product in self._products:
+            if product._cpe == cpe:
+                return product
+        raise KeyError(cpe)
+
     def getGroupForID(self, groupid):
         for group in self._groups:
             if group._groupid == groupid:
diff -r 9ed24f48df01 -r b87f2a6e613a tests/testParseCVE.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testParseCVE.py	Mon Dec 29 16:33:34 2014 +0100
@@ -0,0 +1,81 @@
+import utils
+
+from farolluz.parsers.cve import parse
+
+FULL_CVE = """\
+<entry xmlns:scap-core="http://scap.nist.gov/schema/scap-core/0.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:patch="http://scap.nist.gov/schema/patch/0.1" xmlns:vuln="http://scap.nist.gov/schema/vulnerability/0.4" xmlns:cvss="http://scap.nist.gov/schema/cvss-v2/0.2" xmlns:cpe-lang="http://cpe.mitre.org/language/2.0" xmlns="http://scap.nist.gov/schema/feed/vulnerability/2.0" id="CVE-2014-7088">
+<vuln:vulnerable-configuration id="http://nvd.nist.gov/">
+<cpe-lang:logical-test operator="OR" negate="false">
+<cpe-lang:fact-ref name="cpe:/a:jdm_lifestyle_project:jdm_lifestyle:6.4::~~~android~~"/>
+</cpe-lang:logical-test>
+</vuln:vulnerable-configuration>
+<vuln:vulnerable-software-list>
+<vuln:product>
+cpe:/a:jdm_lifestyle_project:jdm_lifestyle:6.4::~~~android~~
+</vuln:product>
+</vuln:vulnerable-software-list>
+<vuln:cve-id>CVE-2014-7088</vuln:cve-id>
+<vuln:published-datetime>2014-10-18T21:55:17.027-04:00</vuln:published-datetime>
+<vuln:last-modified-datetime>2014-11-14T09:07:51.650-05:00</vuln:last-modified-datetime>
+<vuln:cvss>
+<cvss:base_metrics>
+<cvss:score>5.4</cvss:score>
+<cvss:access-vector>ADJACENT_NETWORK</cvss:access-vector>
+<cvss:access-complexity>MEDIUM</cvss:access-complexity>
+<cvss:authentication>NONE</cvss:authentication>
+<cvss:confidentiality-impact>PARTIAL</cvss:confidentiality-impact>
+<cvss:integrity-impact>PARTIAL</cvss:integrity-impact>
+<cvss:availability-impact>PARTIAL</cvss:availability-impact>
+<cvss:source>http://nvd.nist.gov</cvss:source>
+<cvss:generated-on-datetime>2014-11-14T09:07:51.290-05:00</cvss:generated-on-datetime>
+</cvss:base_metrics>
+</vuln:cvss>
+<vuln:cwe id="CWE-310"/>
+<vuln:references reference_type="UNKNOWN" xml:lang="en">
+<vuln:source>CERT-VN</vuln:source>
+<vuln:reference href="http://www.kb.cert.org/vuls/id/582497" xml:lang="en">VU#582497</vuln:reference>
+</vuln:references>
+<vuln:references reference_type="UNKNOWN" xml:lang="en">
+<vuln:source>MISC</vuln:source>
+<vuln:reference href="https://docs.google.com/spreadsheets/d/1t5GXwjw82SyunALVJb2w0zi3FoLRIkfGPc7AMjRF0r4/edit?usp=sharing" xml:lang="en">
+https://docs.google.com/spreadsheets/d/1t5GXwjw82SyunALVJb2w0zi3FoLRIkfGPc7AMjRF0r4/edit?usp=sharing
+</vuln:reference>
+</vuln:references>
+<vuln:summary>
+The JDM Lifestyle (aka com.hondatech) application 6.4 for Android does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers and obtain sensitive information via a crafted certificate.
+</vuln:summary>
+</entry>"""
+
+CVE_NO_CVSS = """\
+<entry xmlns:scap-core="http://scap.nist.gov/schema/scap-core/0.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:patch="http://scap.nist.gov/schema/patch/0.1" xmlns:vuln="http://scap.nist.gov/schema/vulnerability/0.4" xmlns:cvss="http://scap.nist.gov/schema/cvss-v2/0.2" xmlns:cpe-lang="http://cpe.mitre.org/language/2.0" xmlns="http://scap.nist.gov/schema/feed/vulnerability/2.0" id="CVE-2014-9388">
+<vuln:cve-id>CVE-2014-9388</vuln:cve-id>
+<vuln:published-datetime>2014-12-17T14:59:08.587-05:00</vuln:published-datetime>
+<vuln:last-modified-datetime>2014-12-17T14:59:09.620-05:00</vuln:last-modified-datetime>
+<vuln:references reference_type="UNKNOWN" xml:lang="en">
+<vuln:source>CONFIRM</vuln:source>
+<vuln:reference href="https://www.mantisbt.org/bugs/view.php?id=17878" xml:lang="en">https://www.mantisbt.org/bugs/view.php?id=17878</vuln:reference>
+</vuln:references>
+<vuln:references reference_type="UNKNOWN" xml:lang="en">
+<vuln:source>CONFIRM</vuln:source>
+<vuln:reference href="https://www.mantisbt.org/bugs/changelog_page.php?version_id=191" xml:lang="en">
+https://www.mantisbt.org/bugs/changelog_page.php?version_id=191
+</vuln:reference>
+</vuln:references>
+<vuln:references reference_type="UNKNOWN" xml:lang="en">
+<vuln:source>MLIST</vuln:source>
+<vuln:reference href="http://seclists.org/oss-sec/2014/q4/955" xml:lang="en">[oss-security] 20141207 MantisBT 1.2.18 Released</vuln:reference>
+</vuln:references>
+<vuln:summary>
+bug_report.php in MantisBT before 1.2.18 allows remote attackers to assign arbitrary issues via the handler_id parameter.
+</vuln:summary>
+</entry>"""
+
+class testCVEParsing(utils.TestCase):
+
+    def test_Full(self):
+        self.doc = parse(FULL_CVE)
+        self._validate()
+
+    def test_no_CVSS(self):
+        self.doc = parse(CVE_NO_CVSS)
+        self._validate()


More information about the Farol-commits mailing list