Od luki do wytrycha – narzędzia skryptowe ułatwiające zarządzanie podatnościami systemów

Informacje o podatnościach, czyli lukach w bezpieczeństwie systemów informatycznych, stanowią – a przynajmniej tak powinno być – treść porannej, obowiązkowej lektury i jednocześnie stały rytuał każdego „bezpiecznika”. Tego typu

vulninformacji można rzecz jasna szukać bezpośrednio, przy pomocy przeglądarki weboewej, w specjalistycznych serwisach internetowych, choćby na stronie cve.mitre.org, lecz nie zawsze jest to wygodne. Jeśli chcemy kierować do bazy danych zaawansowane zapytania, a nie tylko pytać o proste dopasowania do wzorców łańcuchów znakowych, to na pewno przyda nam się lokalna baza danych o podatnościach i trochę skryptowych narzędzi, które operując na tej bazie – jak zwykle – uczynią nasze życie lżejszym. Warto mieć do dyspozycji tego rodzaju narzędzia funkcjonujące w trybie off-line także wtedy, gdy nie posiadamy dostępu do Internetu, choć w dzisiejszych czasach taka sytuacja zyskuje wymiar niemal katastrofy ;-).

Dziś chciałbym uczynić tematem kolejnego artykułu narzędzie skryptowe do zarządzania bazą informacji o podatnościach. Przygotowałem takowe i celu dalszego rozwoju kodu, do czego oczywiście gorąco Was zachęcam.

Jak widać, narzędzie zostało napisane w języku Python. Posiada prosty interfejs użytkownika oparty na opcjach wywołania programu. Podpowiedź odnośnie poprawnej składni wywołania i znaczenia poszczególnych opcji można bardzo prosto uzyskać w następujący sposób:

$ python collect_cve_data.py -h
    _____   _____
   / __\ \ / / __|  DATABASE TOOL
  | (__ \ V /| _|   -----------------------
   \___| \_/ |___|  by Janusz Nawrat (2015)
  _________________________________________

usage: collect_cve_data.py [-h] [--version] [-s] [-d] [-q QUERY] [-r REGEXP]

CVE Databese supplying, searching and reporting tool

optional arguments:
  -h, --help  show this help message and exit
  --version   show program's version number and exit
  -s          Supply CVE Database with data downloaded from MITRE.ORG
  -d          Download most current data from MITRE.ORG
  -q QUERY    Query database
  -r REGEXP   RegExp database query

Pierwszą niezbędną operacją do przeprowadzenia jest pobrane pełnej bazy informacji o podatnościach. Uzyskujemy to wywołując program z opcją -d.

python collect_cve_data.py -d

Na czas pobierania dość dużej ilości danych będziemy musieli uzbroić się w nieco cierpliwości.

$ python collect_cve_data.py -d

    _____   _____
   / __\ \ / / __|  DATABASE TOOL
  | (__ \ V /| _|   -----------------------
   \___| \_/ |___|  by Janusz Nawrat (2015)
  _________________________________________

[*] Collecting data from cve.mitre.org. Please wait...
 25% [.............                                       ] 16973824 / 66976710

Po pobraniu danych, należy zasilić nimi bazę (cve.db). Jeśli baza danych nie istnieje, to zostanie ona stworzona, a w niej powstanie tabela (vuln) z niezbędnymi definicjami struktury danych.

$ python collect_cve_data.py -s

    _____   _____
   / __\ \ / / __|  DATABASE TOOL
  | (__ \ V /| _|   -----------------------
   \___| \_/ |___|  by Janusz Nawrat (2015)
  _________________________________________

[*] Database cve.db created
[*] Table 'vuln' created in cve.db database
[*] 85414 records saved to cve.database

W dalszej kolejności, skrypt umożliwia odpytania bazy CVE w trybie zwykłych zapytań SQL-owych (opcja -q) oraz w trybie wyrażeń regularnych (opcja -r).

Zapytajmy przykładowo o podatności urządzeń Cisco ASA:

$ python collect_cve_data.py -r "[Cc]isco ASA"
...
------------------------------Results summary-------------------------------
|    1| CVE-2006-3073 | Multiple cross-site scripting (XSS) vulnerabili... |
|    2| CVE-2009-2631 | Multiple clientless SSL VPN products that run i... |
|    3| CVE-2009-4455 | The default configuration of Cisco ASA 5500 Ser... |
|    4| CVE-2010-0149 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|    5| CVE-2010-0150 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|    6| CVE-2010-0440 | Cross-site scripting (XSS) vulnerability in +CS... |
|    7| CVE-2010-0565 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|    8| CVE-2010-0566 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|    9| CVE-2010-0567 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|   10| CVE-2010-0568 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|   11| CVE-2010-0569 | Unspecified vulnerability in Cisco ASA 5500 Ser... |
|   12| CVE-2012-4629 | The Cisco ASA-CX Context-Aware Security module ... |
|   13| CVE-2012-5419 | Cisco Adaptive Security Appliance (ASA) softwar... |
|   14| CVE-2013-1203 | Cisco ASA CX Context-Aware Security Software al... |
|   15| CVE-2014-3382 | The SQL*Net inspection engine in Cisco ASA Soft... |
|   16| CVE-2014-3383 | The IKE implementation in the VPN component in ... |
|   17| CVE-2014-3384 | The IKEv2 implementation in Cisco ASA Software ... |
|   18| CVE-2014-3385 | Race condition in the Health and Performance Mo... |
|   19| CVE-2014-3386 | The GPRS Tunneling Protocol (GTP) inspection en... |
|   20| CVE-2014-3387 | The SunRPC inspection engine in Cisco ASA Softw... |
|   21| CVE-2014-3388 | The DNS inspection engine in Cisco ASA Software... |
|   22| CVE-2014-3389 | The VPN implementation in Cisco ASA Software 7.... |
|   23| CVE-2014-3390 | The Virtual Network Management Center (VNMC) po... |
|   24| CVE-2014-3391 | Untrusted search path vulnerability in Cisco AS... |
|   25| CVE-2014-3392 | The Clientless SSL VPN portal in Cisco ASA Soft... |
|   26| CVE-2014-3393 | The Clientless SSL VPN portal customization fra... |
|   27| CVE-2014-3394 | The Smart Call Home (SCH) implementation in Cis... |
|   28| CVE-2015-0678 | The virtualization layer in Cisco ASA FirePOWER... |
|   29| CVE-2015-0760 | The IKEv1 implementation in Cisco ASA Software ... |
----------------------------------------------------------------------------
[+] 29 results found

Pamiętajcie proszę o tym, że Python jest językiem ogromnie restrykcyjnym jeśli chodzi o format kodu źródłowego. Mają znaczenie wcięcia w kodzie, a nawet to, czy są one wykonywane przy pomocy tabulatora czy też serii spacji. Interpreter dopuszcza jedno i drugie, byle w obrębie jednego pliku z kodem źródłowym konsekwentnie stosować jeden z tych sposobów formatowania.
Pamiętajcie też o doinstalowaniu niestandardowych modułów, które importujemy w początkowej sekcji kodu.
Oto kod źródłowy programu (wget, csv, sqlite3).

#!/usr/bin/python
# -*- coding: utf-8 -*-
import wget, codecs, argparse
import sys, os, re, csv, signal
import sqlite3 as lite

def signal_handler(signum, stack):
    sys.stderr.write("[!] Received signal: %d. The program will be stopped\n" % signum)
    sys.stderr.write("[*] Program stopped\n")
    sys.exit(1)
# end of signal_handler()

def download_database():
    sys.stderr.write("[*] Collecting data from cve.mitre.org. Please wait...\n")
    try:
        file = wget.download("https://cve.mitre.org/data/downloads/allitems.csv")
        if os.path.isfile('cve.csv'):
            os.remove('cve.csv')
        os.rename('allitems.csv', 'cve.csv')
        sys.stderr.write("\n[*] Saving data to cve.csv file\n")    
    except:
        sys.stderr.write("\n[!] An error occured during file collection from cve.mitre.org server\n")
        sys.exit(1)
# end of download_database()

def supply_database():
    # Create or open cve.db database
    try:
        connection = lite.connect('cve.db')
        cursor = connection.cursor()
        sys.stderr.write("[*] Database cve.db created\n")
    except:
        sys.stderr.write("\n[!] Problem with cve.db database opening or creation\n")
        sys.exit(1)        

    # Create vuln table
    cursor.execute('SELECT name FROM sqlite_master WHERE name="vuln"')
    result = cursor.fetchone()
    # Fileds: "Name","Status","Description","References","Phase","Votes","Comments"
    try:
        if result:
            cursor.execute('DROP TABLE IF EXISTS vuln')
        cursor.execute('CREATE TABLE vuln (name TEXT, status TEXT, descr TEXT, refer TEXT, phase TEXT, votes TEXT, comments TEXT)')
        connection.commit()
        sys.stderr.write("[*] Table 'vuln' created in cve.db database\n")
    except:
        sys.stderr.write("[!] Problem with 'vuln' table creation in cve.db database\n")
        sys.exit(1)    

    try:
        id = 0
        with codecs.open("cve.csv", "rb", "latin-1", errors="ignore") as f:
            reader = csv.reader(f)
            for row in reader:
                if len(row) == 7 and re.search(r'CVE-\d{4}-\d{4,5}', row[0]):
                    cursor.execute('INSERT INTO vuln VALUES (?,?,?,?,?,?,?)', (row[0].decode('utf8'), row[1].decode('utf8'), row[2].decode('utf8'), row[3].decode('utf8'), row[4].decode('utf8'), row[5].decode('utf8'), row[6].decode('utf8')))
                    id += 1
            sys.stderr.write("[*] %d records saved to cve.database\n" % id)
    except:
        sys.stderr.write("[!] Problem with data supply data to vulnerability table\n")
        sys.stderr.write(u"Record: %s\n" % row)
        sys.exit(1)
    finally:
        connection.commit()
        connection.close()
# end of supply_database()    

def query_database(query):
    results = []; short = []
    try:
        with lite.connect('cve.db') as conn:
            cursor = conn.cursor()
            query_str = u"SELECT * FROM vuln WHERE descr LIKE ?"
            query = "%{0}%".format(query) 
            cursor.execute(query_str, (query,))

            for row in cursor.fetchall():
                name, status, descr, refer, phase, votes, comments = row
                results.append(name); short.append(descr)
                print '[%s] - Status: %s\nDescription: %s\nReference: %s\nPhase (%s), Votes %s\nComments: %s' % (name, status.decode('cp852'), descr.decode('cp852'), refer.decode('cp852'), phase.decode('cp852'), votes.decode('cp852'), comments.decode('cp852'))
    except:
        sys.stderr.write("[!] Problem with cve.db database opening in function guery_database\n")
        sys.exit(1)
    if len(results):
        count = 1
        print "{:-^76}".format('Results summary')
        for item in results:
            print "|{:>5}|{:^15}| {:<47}... |".format(count, item, short[count-1][:47])
            count += 1
        print "{:-^76}".format('-')
        if count 5}|{:^15}| {:<47}... |".format(count, item, short[count-1][:47])
            count += 1
        print "{:-^76}".format('-')
        if count < 3:
            print "[+] %d result found" % len(results)
        else:
            print "[+] %d results found" % len(results)    
# end of regexp_query_database()
    
banner = """
    _____   _____ 
   / __\ \ / / __|  DATABASE TOOL
  | (__ \ V /| _|   -----------------------
   \___| \_/ |___|  by Janusz Nawrat (2015)
  _________________________________________  
"""

if __name__ == "__main__":

    signal.signal(signal.SIGINT, signal_handler)
    reload(sys)
    sys.setdefaultencoding('utf-8')

    print banner

    parser = argparse.ArgumentParser(description="CVE Databese supplying, searching and reporting tool")

    parser.add_argument('--version', action='version', version='%(prog)s 1.0')

    parser.add_argument('-s', action='store_true', dest='supply',
                        help='Supply CVE Database with data downloaded from MITRE.ORG',
                        default=False)
                    
    parser.add_argument('-d', action='store_true', dest='download',
                        help='Download most current data from MITRE.ORG',
                        default=False)
                    
    parser.add_argument('-q', action='store', dest='query',
                        help='Query database')

    parser.add_argument('-r', action='store', dest='regexp',
                        help='RegExp database query')                        
                    
    options = parser.parse_args()

    if options.download:
        download_database()
    
    if options.supply:
        if os.path.isfile('cve.csv') and os.access('cve.csv', os.R_OK):
            supply_database()
        else:
            sys.stderr.write("[!] File cve.csv is missing or is not readable\n")
            sys.exit(1)

    if options.query:
        query_database(options.query)
    
    if options.regexp:
        regexp_query_database(options.regexp)

Informacje Janusz Nawrat
Just ordinary man who likes thinking...

Dodaj komentarz

Co Lepsze Kawałki

Kwiatki z sesji RPG. Oraz kwiecisty język, wyklątwy i bluźnierze.

Tomasz Wełna

visual artist & graphic designer

PRACOWNIA OKO

dr TOMASZ WEŁNA, dr DARIA RZEPIELA | Kraków | Plac Matejki 10 | tel 691 81 75 74

Piękno neurobiologii

Blog Jerzego Vetulaniego

Teoria muzyki, zasady muzyki, podstawy muzyki

Teoria muzyki, zasady muzyki, podstawy muzyki - czyli to co każdy amator muzyki wiedzieć powinien :)

Personal Development & Inspirations

Przemyślenia i refleksje, którymi warto się podzielić (blog by Janusz Nawrat)

Business IT Cooperation Platform

Biznes i IT - dwa światy, które muszą współdziałać