Update Eigener Parser
parent
62ec794f5b
commit
0a4b58a585
@ -114,12 +114,11 @@ for run in list_of_all:
|
|||||||
run.testssl_results.append(Result(path, nr, testssl_parse(path)))
|
run.testssl_results.append(Result(path, nr, testssl_parse(path)))
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Das Herzstück des Parsers ist die obige Schleife, sie durchläuft alle Runs und ruft für die gefundenen Logs (Otseca, Lynis oder TestSSL) den für dieses Tool zugehörigen Parser auf. Die Ergebnisse werden dann an das Ursprungsobjekt gebunden.
|
||||||
|
|
||||||
### Stage 3.1: Lynis
|
### Stage 3.1: Lynis
|
||||||
|
|
||||||
* Was ist wichtig?
|
Da Lynis den umfangreichsten Report liefert haben wir hier auch fast alle Informationen gespeichert, die verfügbar waren. Alle Informationen wurden aus dem Konsolenoutput geparsed, da die Logdatei sehr viel unwichtiges entielt und die DAT Datei zu wenig Informationen gespeichert hat.
|
||||||
* Nur das Console-Log benutzt
|
|
||||||
* Dat und Lynis-Log waren unbrauchbar
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
LYNIS_REGEX = {
|
LYNIS_REGEX = {
|
||||||
@ -131,6 +130,8 @@ LYNIS_REGEX = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Da der Konsolenoutput mit Farben arbeit, war es für uns sehr einfach nach Farben zu filtern. In obigem Auschnitt sieht man die Definitionen der Regular Expressions der benutzen Matcher.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
LYNIS_BLOCKS = {
|
LYNIS_BLOCKS = {
|
||||||
"Boot and services",
|
"Boot and services",
|
||||||
@ -140,6 +141,8 @@ LYNIS_BLOCKS = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Weiter gibt es noch ein Array, welches alle Lynis Blocks definiert, welche wir speichern wollen.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
with open(path_to_log) as handle:
|
with open(path_to_log) as handle:
|
||||||
text = handle.read()
|
text = handle.read()
|
||||||
@ -159,14 +162,17 @@ for block in blocks:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Weiter wurden noch waringCount, bla bla
|
|
||||||
|
|
||||||
Nach dem generellen step wurden dann noch spezielle props aus den obigen blocks extrahiert, dies ginge aber zu weit, genaueres in der datei [lynis.py](https://gitlab.com/marcel.schwarz/it-security-2/-/blob/master/scan_output_parser/lynis.py)
|
Um nun zu parsen, wurde das Logfile an jeder Kapitelüberschrift geteilt und die darin enthaltenen Anzahlen an grünen, gelben, roten und weißen angaben gespeichert.
|
||||||
|
|
||||||
|
Weiter wurden noch der Warning Count, Suggestion count, Hardening Index und die Anzahl an ausgeführen Tests gespeichert.
|
||||||
|
|
||||||
|
Nach dem generellen step wurden dann noch spezielle props aus den obigen Blocks extrahiert, dies ginge aber zu weit, genaueres kann direkt aus dem Quellcode in der Datei [lynis.py](https://gitlab.com/marcel.schwarz/it-security-2/-/blob/master/scan_output_parser/lynis.py) herausgelesen werden.
|
||||||
|
|
||||||
### Stage 3.2: Testssl
|
### Stage 3.2: Testssl
|
||||||
* Einer der leichtesten Parser
|
Der TestSSL Parser ist einer der leichtesten zu implementierenden Parser gewesen. Die Logfile musste lediglich bei jedem neuen Scan geteilt werden und dann nach bestimmten Keywords ausschau gehalten werden.
|
||||||
* Leider zu spät bemerkt, dass es einen JSON export gegeben hätte.
|
|
||||||
* TXT datei hat auch funktioniert
|
Leider haben wir erst im Nachhinein bemerkt, dass es die Möglichkeit gegeben hätte, als Output-Format `JSON` anzugeben, was die programmatische verarbeitung noch einfacher gemacht hätte. So hatten wir nur eine Textdatei.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def testssl_parse(path):
|
def testssl_parse(path):
|
||||||
@ -199,15 +205,11 @@ def testssl_parse(path):
|
|||||||
continue
|
continue
|
||||||
return testssl_overall
|
return testssl_overall
|
||||||
```
|
```
|
||||||
|
Der Parser Arbeitet mit einem festen Array an Werten, die durch unser Run-Skript vorgegeben waren. Während des Parsens wird dann nur noch der entsprechende Boolean auf `True` gesetzt, sobald das Keyword gefunden wurde. Der oben genannte Trenner für die Tests wurde als "## Scan startet as: " gewählt.
|
||||||
* Gelöst durch initialisierung mit default Werten und dann das setzen auf bestimmte regex
|
|
||||||
* Vorher trennung der einzelnen logs der ports durch einen split bei "## Scan startet as: "
|
|
||||||
|
|
||||||
|
|
||||||
### Stage 3.3: Otseca
|
### Stage 3.3: Otseca
|
||||||
* HTML result einfacher parse
|
|
||||||
* Nur nach rot, gelb und grün und gesamt
|
|
||||||
|
|
||||||
|
Der Otseca parser war ebenfalls einfach umzusetzen, da alle Logfiles als HTML vorlagen. Da Otseca eher wenig Hinweise gibt, haben wir uns lediglich auf die Anzahl der grünen, gelben und roten Kästen im Output beschränkt. Der Parser zählt hierfür einfach das Vorkommen der CSS-Direktive "background-color" mit dem ensprechenden Wert.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def otseca_box_counts(path_to_report):
|
def otseca_box_counts(path_to_report):
|
||||||
@ -225,7 +227,7 @@ def otseca_box_counts(path_to_report):
|
|||||||
return counts
|
return counts
|
||||||
```
|
```
|
||||||
|
|
||||||
* Weitere Distro info, was in lynis gefehlt hat.
|
Außerdem wurden weitere Betriebssystem-Infos, geholt welche in Lynis nicht verfügbar waren. Dazu gehören hauptsächlich die Gesamtanzahl Pakete, sowie die entsprechenden Counts für "Upgraded", "Newly Installed", "Remove" und "Not Upgraded".
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def otseca_distro_info(path_to_report):
|
def otseca_distro_info(path_to_report):
|
||||||
@ -251,16 +253,20 @@ with open("export.json", "w") as handle:
|
|||||||
handle.write(Run.schema().dumps(list_of_all, many=True))
|
handle.write(Run.schema().dumps(list_of_all, many=True))
|
||||||
```
|
```
|
||||||
|
|
||||||
* Problem mit JSON
|
Zuerst wollten wir alles als JSON Objekt ausgeben, dies stellte sich aber sehr schell als unübersichtlich heraus. Die Resultierende JSON-Datei hatte für alle 56 Durchläufe etwa 19.000 Zeilen.
|
||||||
|
|
||||||
|
Also Entschieden wir uns noch für eine andere Art der Ausgabe, eine strukturiertere, SQLite.
|
||||||
|
|
||||||
### Stage 4.2: SQLite-Ausgabe
|
### Stage 4.2: SQLite-Ausgabe
|
||||||
* Warum SQLite?
|
SQlite bietet sich hierfür sehr gut an, da es sich um eine serverlose strukturierte Datenbank handelt. Weiter gibt es uns auch zur Auswertung ganz neue Möglichkeiten durch die Datenbankfunktionen. Außerdem bietet Python eine native implementation von SQlite, ohne das Zusatzpakete benötigt werden.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
for run in list_of_all:
|
for run in list_of_all:
|
||||||
write_run_to_db(run)
|
write_run_to_db(run)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Der letzte Schritt des Parsers ist es, jeden Run an die Datenbankschnittstellt zu übergeben.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def write_run_to_db(run):
|
def write_run_to_db(run):
|
||||||
conn = sqlite3.connect(DB_NAME)
|
conn = sqlite3.connect(DB_NAME)
|
||||||
@ -282,8 +288,12 @@ def write_run_to_db(run):
|
|||||||
conn.execute("INSERT OR IGNORE INTO lynis_results VALUES (" + "".join("?," * 37) + "?)", data)
|
conn.execute("INSERT OR IGNORE INTO lynis_results VALUES (" + "".join("?," * 37) + "?)", data)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In dieser Funktion werden dann in vier Tabellen alle gesamelten Informationen gespeichert die Tabellen umfassen zwischen fünf und 37 Spalten. Jeder Run wird hierbei in die `Run` Tabelle gespeichert. Außerdem bekommt jedes Tool ebenfalls seine eigene Tabelle (Lynis, TestSSL und Otseca). Die Tabellen von den Tools werden dann nach und nach gefüllt, und über Fremdschlüssen mit der Primärtabelle verbunden. Zuletzt wird die Transaktion dann geschrieben.
|
||||||
|
|
||||||
# Ergebnisse
|
# Ergebnisse
|
||||||
* Insgesamt etwa 30 RegEx um zu parsen
|
Insgesamt lässt sich sagen, dass etwa 30 Regular Expressions nötig waren, um alle gewollten Logfiles über alle Tools zu parsen. Die Laufzeit beträgt hierbei etwa 10 Sekunde, wobei das Einlesen der doch relativ langen Textdateien am längsten benötigt.
|
||||||
* laufzeit unter 10 Sekunden
|
|
||||||
* Extremer Vorteil durch das abfragen mit SQL Syntax
|
Der Export als SQlite hat sich im Nachhinein als sehr Vorteilhaft entpuppt, da SQlite nativ mit JSON Daten umgehen kann, was sehr leichte Abfragemechanismen erlaubt.
|
||||||
* Keine Flüchtigkeits- oder Übertragungsfehler durch menschliches verschulden
|
|
||||||
|
Weiter wurden sogut wie alle menschlichen Fehler, die durch Unaufmerksamkeit verschultet wären, ausgeschlossen. Auch Ablese- oder Tippfehler sind kein Problem.
|
Loading…
Reference in New Issue
Block a user