23. OpenVAS Scanner Protokoll

Das OpenVAS Scanner Protocol (OSP) ist eine XML-basierte zustandslose Request-Response API die eine einheitliche Abstraktion für Schwachstellen-Scanner bietet. Der Greenbone Security Manager ist in der Lage OSP-Scanner bruchfrei in das Schwachstellenmanagement zu integrieren. OSP-Scanner werden alle auf die gleiche Weise gesteuert und die Ergebnisse sind in der Datenbank in ein und derselben Struktur abgelegt. Es kann eine beliebige Zahl gleicher oder verschiedener OSP-Scanner angebunden werden.

Der Begriff “Schwachstellen-Scanner” ist hierbei sehr weit gefasst zu sehen. Es kann sich auch um Abfragen in ein Patch-Management System handeln oder eine reine Asset-Abfrage. Als Ergebnis eines Scanners wird lediglich erwartet, dass es sich um Schwachstellen-Details oder um Asset-Informationen handelt. Letzteres können installierte Software-Pakete, offene Ports, laufende Dienste, TLS-Zertifkate und ähnliches sein.

Einige OSP Scanner sind bereits in die GSM Appliance integriert worden und können über das GOS-Admin-Menü aktiviert werden. Es ist jedoch auch möglich externe OSP Scanner hinzuzufügen.

23.1. Aktivierung zusätzlicher OSP-Scanner

Zusätzliche Scanner können über das GOS-Admin-Menü aktiviert werden. Der GSM hat in seiner Werkseinstellung nur den OpenVAS-Scanner aktiv. Kein weiterer Scanner ist eingeschaltet.

Bemerkung

Beginnend mit GOS 3.1.17 können ausgewählte Anwender zusätzliche Scanner aktivieren. Diese Funktionalität wird in späteren Versionen allen Benutzer zur Verfügung stehen. Um bereits jetzt diese Funktionalität zu nutzen, kontaktieren Sie den Greenbone Support.

Um die zusätzlichen Scanner einzuschalten, rufen Sie das GOS-Admin-Menü auf und wählen Sie Advanced/Scanner Management. Das folgende Menü wird ihnen angezeigt:

_images/enable-scanner.png

Aktivierung zusätzlicher Scanner

Um z.B. den w3af Scanner einzuschalten, wählen Sie Add OSP w3af Scanner. In Abhängigkeit des gewählten Scanners, werden möglicherweise weitere Informationen oder Lizenzen angezeigt:

_images/accept-scanner.png

Akzeptieren Sie die Bedingungen

Der Scanner ist erzeugt worden. Der Scanner ist nach einem Neustart verfügbar.

_images/reboot-scanner.png

Ein Neustart ist erforderlich nachdem der Scanner eingeschaltet wurde.

Mithilfe des gleichen Menüs können Sie die Scanner auch aktualisieren und wieder abschalten.

23.2. Wie man einen OSP Wrapper baut

Die OSP Protokoll-Dokumentation ist auf der Greenbone Website verfügbar unter

http://docs.greenbone.net/API/OSP/osp.html

23.2.1. Die Aufgabe: debsecan als OSP-Scanner

Im Folgenden stellen wir uns die Aufgabe, das Werkzeug “debsecan” mit einem OSP Wrapper zu versehen. Dieses Werkzeug ist für Debian GNU/Linux Systeme verfügbar und ermittelt für das System auf dem es ausgeführt wird, eine Liste von Schwachstellen zu den installierten Paketen. Es geht dabei über die herkömmliche Abfrage nach fehlenden Updates hinaus und holt sich über eine Online-Verbindung Informationen zu noch in Bearbeitung befindlichen Schwachstellen.

Weitere Details zu diesem Werkzeug finden such auf der debsecan Homepage:

http://www.enyo.de/fw/software/debsecan/

Für die Ausführung von debsecan reichen einfache Anwender-Privilegien aus:

$ debsecan
CVE-2015-3333 chromium (remotely exploitable, high urgency)
CVE-2015-3334 chromium (remotely exploitable, medium urgency)
CVE-2015-3336 chromium (remotely exploitable, medium urgency)
TEMP-0000000-EA424A libbluray1
CVE-2014-9447 libelf1 (remotely exploitable, medium urgency)
CVE-2014-8354 libmagickcore5
CVE-2014-8355 libmagickcore5
CVE-2014-8562 libmagickcore5
CVE-2014-8716 libmagickcore5
TEMP-0000000-2FC21E libmagickcore5 (low urgency)
TEMP-0000000-7C079F libmagickcore5
TEMP-0000000-EEF23C libmagickcore5 (low urgency)
TEMP-0000000-FDAC72 libmagickcore5
TEMP-0773834-5EB6CF libmagickcore5
CVE-2013-4288 libpolkit-gobject-1-0 (low urgency)
CVE-2002-2439 libstdc++6-4.7-dev (low urgency)
CVE-2014-5044 libstdc++6-4.7-dev
...

Wie man sieht sind viele Schwachstellen schon direkt mit einer CVE verknüpft. Diese Scan-Informationen wollen wir nun OpenVAS zugängig machen.

23.2.2. Die Basis: ospd

OSP it letztlich nur eine Spezifikation. Man könnte also selbst einen OSP Wrapper in einer beliebigen Programmiersprache entwickeln.

Um sofort mit der eigentlichen Anbindung zu starten, kann man aber auch auf das OpenVAS-Modul “ospd” zurückgreifen. Dies ist in Python geschrieben und stellt eine Basis-Klasse sowie Hilfsfunktionen zur Verfügung. Damit ist die gesamte Service-Funktionalität inklusive TLS-Verschlüsselung bereits fertig.

Das aktuelle ospd Paket kann hier heruntergeladen werden:

http://www.openvas.org/install-source-de.html

Wir arbeiten mit Version 1.0.0:

https://wald.intevation.org/frs/download.php/1999/ospd-1.0.0.tar.gz

Und prüfen natürlich die Signatur:

https://wald.intevation.org/frs/download.php/1999/ospd-1.0.0.tar.gz.sig

$ gpg --verify ospd-1.0.0.tar.gz.sig

$ tar xzf ospd-1.0.0.tar.gz
$ cd ospd-1.0.0/

Wir installieren das Paket an einem temporären Ort:

$ mkdir /tmp/osptest
$ export PYTHONPATH=/tmp/osptest/lib/python2.7/site-packages/

Nun wird ospd dorthin installiert:

$ python setup.py install --prefix=/tmp/osptest

Natürlich führen viele Wege nach Rom. Man kann das Paket auch an andere Orte und auf andere Weise installieren. Wer selbst häufiger mit Python zu tun hat, kann hier gerne den bevorzugten Weg gehen.

23.2.3. Das Grundgerüst für den neuen OSP Scanner

Zunächst legen wir ein passendes Verzeichnis und die zentrale Datei “wrapper.py” an.

Die beiden wichtigsten Elemente dort sind zunächst eine Ableitung des “OSPDaemon” mit einer fürs erste sehr einfachen Selbstauskunft (“OSPDdebsecan”) sowie die Hauptroutine für den Service selbst (“main”).

$ mkdir -p debsecan/ospd_debsecan
$ cd debsecan/ospd_debsecan
$ gvim wrapper.py
from ospd.ospd import OSPDaemon
from ospd.misc import main as daemon_main
from ospd_debsecan import __version__

class OSPDdebsecan(OSPDaemon):

    """ Class for ospd-debsecan daemon. """

    def __init__(self, certfile, keyfile, cafile):
        """ Initializes the ospd-debsecan daemon's internal data. """
        super(OSPDdebsecan, self).__init__(certfile=certfile, keyfile=keyfile,
                                    cafile=cafile)
        self.server_version = __version__
        self.scanner_info['name'] = 'debsecan'

    def check(self):
        """ Checks that debsecan command line tool is found and is executable. """
        return True

def main():
    """ OSP debsecan main function. """
    daemon_main('OSPD - debsecan wrapper', OSPDdebsecan)

Nun komplettieren wir das Gerüst um einen lauffähigen, wenn auch noch sehr dummen Service zu erhalten. Dafür legen wir noch “__init__.py” im selben Verzeichnis von “wrapper.py” an:

__version__ = '1.0b1'

Und das Steuerungs-Modul für das Paket: debsecan/setup.py:

from setuptools import setup

from ospd_debsecan import __version__

setup(
    name='ospd-debsecan',
    version=__version__,

    packages=['ospd_debsecan'],

    url='http://www.openvas.org',
    author='OpenVAS Development Team',
    author_email='info@openvas.org',

    license='GPLV2+',
    install_requires=['ospd==1.0.0'],

    entry_points={
        'console_scripts': ['ospd-debsecan=ospd_debsecan.wrapper:main'],
    },
)

Damit läßt sich unser neuer Server installieren und starten:

$ python setup.py install --prefix=/tmp/osptest

Falls noch nicht geschehen, sollte der Pfad für den Server angepasst werden:

$ export PATH=$PATH:/tmp/osptest/bin

Dann kann der Aufruf direkt erfolgen:

$ ospd-debsecan --version
OSP Server for debsecan version 1.0b1
OSP Version: 1.0
Using: OSPd 1.0.0
Copyright (C) 2014, 2015 Greenbone Networks GmbH
License GPLv2+: GNU GPL version 2 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Testen wir als nächstes die Server-Funktion und schicken den Server in den Hintergrund, so dass er auf unserem System an Port 2346 auf Anfragen wartet:

$ ospd-debsecan -b 127.0.0.1 -p 2346 \
  -k /tmp/osptest/var/lib/openvas/private/CA/clientkey.pem \
  -c /tmp/osptest/var/lib/openvas/CA/clientcert.pem \
  --ca-file /tmp/osptest/var/lib/openvas/CA/cacert.pem  &

Die verwendeten Schlüssel und Zertifikate wurden tatsächlich für den OpenVAS Scanner durch das Werkzeug “openvas-mkcert” erzeugt. Um die Komplexität zu reduzieren, verwenden wir sie hier einfach anstelle ein eigenes Schlüsselpaar zu erzeugen. Wenn Sie den OpenVAS-Scanner mit einem anderen Prefix installiert haben, nutzen Sie den entsprechenden Pfad statt “/tmp/osptest/”.

Wie man bereits vermuten kann, erfolgt die Authentifizierung bei einem OSP-Server via Klient-Zertifikat. Eine Anmeldung mit einer Kombination von Benutzername und Passwort ist nicht vorgesehen.

Mit dem “omp” Kommandozeilen-Werkzeug des “openvas-cli” Paketes kontaktieren wir den Server:

$ omp -h 127.0.0.1 -p 2346 --use-certs -X "<get_version/>"
<get_version_response status_text="OK" status="200"><protocol><version>1.0</versi...

Voreingestellt verwendet “omp” mit “–use-certs” die Standardpfade für die Schlüssel und Zertifikate. Das funktioniert natürlich nur, wenn wir in der selben Umgebung des Servers arbeiten.

Etwas hübscher und besser lesbar ist die Antwort mit der zusätzlichen Option “–pretty-print”:

$ omp -h 127.0.0.1 -p 2346 --use-certs --pretty-print  -X "<get_version/>"
    <get_version_response status_text="OK" status="200">
      <protocol>
        <version>1.0</version>
        <name>OSP</name>
      </protocol>
      <daemon>
        <version>1.0.0</version>
        <name>OSPd</name>
      </daemon>
      <scanner>
        <version>No version</version>
        <name>debsecan</name>
      </scanner>
    </get_version_response>

Schauen wir uns das in der GSM Web-Oberfläche an:

Über den Menüpunkt Configuration/Scanners wird ein neuer Scanner angelegt, siehe Abbildung Anlegen eines neuen OSP Scanners.. Die Zertifikate sind die gleichen wir beim Start des Servers.

_images/create-osp-scanner.png

Anlegen eines neuen OSP Scanners.

Es werden direkt nach Erstellung die Details zum Scanner angezeigt, siehe Abbildung Online Rückmeldungstest für den OSP Scanner.. Diese “Online Response” zeigt uns die schon oben abgefragte Versionsnummer sowie auch einen Parameter: “debug_mode” ist ein Standardparameter der durch die Basisklasse “OSPDaemon” mitgeliefert wird.

_images/osp-online-test.png

Online Rückmeldungstest für den OSP Scanner.

So weit, so gut: Wir haben einen funktionierenden Server. Leider kann er noch nichts von seiner eigentlichen Bestimmung. Das werden wir nun ändern.

23.2.4. Verbindung schaffen zwischen debsecan und OSP

Dafür müssen wir eine weitere Methode für die Klasse “OSPDaemon” definieren, nämlich “exec_scan”:

def exec_scan(self, scan_id, target):
    """ Starts the debsecan scanner for scan_id scan. """

    # run the debsecan command
    result = subprocess.check_output(["debsecan"])

    # parse the output of the debsecan command and create
    # respective alarms
    for line in result.split("\n"):
        words = line.split()
        if words.__len__() > 2 and words[0].split("-")[0] == "CVE":
            self.add_scan_alarm(scan_id, host=target, name=words[0],
                                value=line)

Wie oben installieren und starten wir die neue Version und testen danach die Funktion erstmal auf der Kommandozeile:

$ omp -h 127.0.0.1 -p 2346 --use-certs --pretty-print \
   -X "<start_scan target='localhost'><scanner_params/></start_scan>"

<start_scan_response status_text="OK" status="200">
  <id>8f48d691-c136-488f-8f31-d8761e1c75e1</id>
</start_scan_response>

Damit haben wir die ID unseres Scans. Schauen wir die Details dazu an:

$ omp -h 127.0.0.1 -p 2346 --use-certs --pretty-print \
   -X "<get_scans scan_id='8f48d691-c136-488f-8f31-d8761e1c75e1'/>"

<get_scans_response status_text="OK" status="200">
  <scan id="8f48d691-c136-488f-8f31-d8761e1c75e1" target="localhost"
    end_time="1428004156" progress="100" start_time="1428004156">
    <results>
      <result host="localhost" severity="" test_id="" name="CVE-2015-3333"
       type="Alarm">CVE-2015-3333 chromium (remotely exploitable, high urgency)
      </result>
      <result host="localhost" severity="" test_id="" name="CVE-2015-3334"
       type="Alarm">CVE-2015-3334 chromium (remotely exploitable, medium urgency)
      </result>
      <result host="localhost" severity="" test_id="" name="CVE-2015-3336"
       type="Alarm">CVE-2015-3336 chromium (remotely exploitable, medium urgency)
      </result>
      <result host="localhost" severity="" test_id="" name="CVE-2014-9447"
       type="Alarm">CVE-2014-9447 libelf1 (remotely exploitable, medium urgency)
      </result>
      <result host="localhost" severity="" test_id="" name="CVE-2013-4288"
       type="Alarm">CVE-2013-4288 libpolkit-gobject-1-0 (low urgency)</result>
      <result host="localhost" severity="" test_id="" name="CVE-2002-2439"
       type="Alarm">CVE-2002-2439 libstdc++6-4.7-dev (low urgency)</result>
      <result host="localhost" severity="" test_id="" name="CVE-2013-2074"
       type="Alarm">CVE-2013-2074 kdelibs5-plugins (remotely exploitable, low urgency)
      </result>
    </results>
  </scan>
</get_scans_response>

Das sieht soweit gut aus. Nun ein Scan über die GUI. Dafür brauchen wir zuerst noch eine Scan Configuration für debsecan, auch wenn diese eigentlich leer beibt. Diese wird über das Menü Configuration/Scan Configs neu erstellt, siehe Abbildung Anlegen einer neuen Scan-Konfiguration für den debscan OSP-Scanner..

_images/osp-scanconfig.png

Anlegen einer neuen Scan-Konfiguration für den debscan OSP-Scanner.

Nun den Task erstellen wie in Abbildung Anlegen eines Tasks für den debscan OSP-Scanners.:

_images/osp-newtask.png

Anlegen eines Tasks für den debscan OSP-Scanners.

Wenn wir dann auf die übliche Weise den Scan starten, erhalten wir die Ergebnisse wie in Abbildung Ein Scan-Ergebnis eines OSP debscans..

_images/osp-scanresults.png

Ein Scan-Ergebnis eines OSP debscans.

Der Schweregrad der Ergebnisse wird automatisch, basierend auf der internen CVE Datenbank, durch den GSM bestimmt.

23.2.5. Fazit

Wir haben mit 40 Zeilen Python-Quelltext eine OSP Scanner-Anbindung geschaffen die die CVE-Schwachstellen-Informationen des Werkzeuges debsecan an den Greenbone Security Manager liefern kann.

Durch die Verwendung als ganz regulären Task können wir die debsecan-basierten Prüfungen nun mit einer zeitgesteuerten Ausführung versehen. Wir können Notizen und Übersteuerungen anlegen und alle weiteren Funktionen eines vollwertigen Schwachstellen-Managements nutzen.

Aber noch ist der OSP-Wrapper für debsecan recht rudimentär. Es fehlen noch Fehlerbehandlungen und wir können durch bessere Aufbereitung noch mehr aus den Scan-Resultaten für den Greenbone Security Manager herausholen.

Abseits dieser Erweiterungen ist ein weiteres Ziel erstrebenswert: Der Ausbau des OSP-debsecan zu einem Remote-Scanner. Bisher wird das Zielsystem ignoriert und einfach immer das lokale System geprüft. Doch könnte sich der OSP-Wrapper unter Verwendung von über den GSM konfigurierten Zugangsdaten per SSH auf den genannten Zielsystemen einloggen und den dort installierten debsecan ausführen und die Resultate entsprechend aufbereiten. Damit liessen sich dann ganze Netzwerke prüfen, ohne dass auf jedem System ein ospd-debsecan installiert wäre. Der OSP-Scanner “ospd-ovaldi” ist ein Beispiel für einen solchen Remote-Scanner.

Der aktuelle Stand von OSP-debsecan ist natürlich als Open Source unter der GPLv2+ lizensiert und hier verfügbar:

https://wald.intevation.org/scm/viewvc.php/trunk/osp-servers/debsecan/?root=openvas

23.2.6. Erste Fehlerbehandlung einbauen

OSP erlaubt das Senden einer Fehlermeldung, falls irgendein Problem während des Scans auftrat. Diese Meldungen werden in der Benutzeroberfläche im Abschnitt “Fehler” auftauchen.

Die Methode, die hierfür verwendet wird, heisst “add_scan_error” und funktioniert analog zur Methode “add_scan_alarm”. Die Ausführung des externen Werkzeuges “debsecan” könnte fehlschlagen, weil es beispielsweise gar nicht installiert ist. Das wollen wir nun in der Methode “exec_scan” berücksichtigen:

# run the debsecan command
try:
    result = subprocess.check_output(["debsecan"])
except:
    self.add_scan_error(scan_id, host=target,
      value="A problem occurred trying to execute 'debsecan'.")
    return