Od luki do wytrycha – narzędzia skryptowe ułatwiające zarządzanie podatnościami systemów
2015/12/02 Dodaj komentarz
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
informacji 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)