Mercurial > farol > farolluz
changeset 43:b87f2a6e613a
Add CVE parsing (from OpenVAS GSA)
author | Benoît Allard <benoit.allard@greenbone.net> |
---|---|
date | Mon, 29 Dec 2014 16:33:34 +0100 |
parents | 9ed24f48df01 |
children | b7e64d0a3a7c |
files | CHANGES farolluz/document.py farolluz/parsers/cve.py farolluz/producttree.py tests/testParseCVE.py |
diffstat | 5 files changed, 280 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- 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)
--- 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
--- /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@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
--- 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:
--- /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()