Hallo Forum,
derzeit bin ich ein wenig am programmieren unter Linux. Entstanden ist
ein Monitoring-Tool, das jeden Prozessaufruf/fork/exit mit Zeitstempel
dokumentiert. Entstehen soll ein IDS, das abnormales Verhalten erkennt.
Geholfen hat dabei vor allem der Code von forkstat
(http://kernel.ubuntu.com/git/cking/forkstat.git/ ). Den habe ich zum
Teil nach Perl portiert. Das Script verbindet sich auf mit dem Kernel
über das Netlink-Interface und erhält dort über das
NETLINK_CONNECTOR-Protokoll sämtliche Execs/Forks/Exits/Coredumps etc.
Jedes einzelne Event wird in einer Datenbank hinterlegt. Die
Datenbankeinträge werden durch Informationen zu den Prozessen aus
/proc/$pid/ angereichert. Also z.B. Name des Prozesses, Argumente,
Workdir, User-id unter der der Prozess läuft usw.
Der nächste Schritt wäre jetzt dem reinen Monitoring ein wenig
Intelligenz zu verleihen. Ich stell mir das so vor, dass ich ein
sauberes System eine Woche laufen lass, oder von mir aus auch einen
Monat und dabei eben alles mitprotokolliere. Danach füttere ich die
erhobenen Daten in einen - nennen wir ihn jetzt einfach mal -
Algorithmus. Der soll dann z.B. alarmieren, wenn plötzlich Prozesse
gestartet werden, die entweder noch nie zuvor gestartet wurden (das ist
einfach zu implementieren, ich erstelle von jedem gestarteten Prozess
eine sha256-checksum (die ich nebenbei mit Virenchecksummen vergleich))
oder, wenn z.B. ein Interpreterprozess alla Python gestartet wird, dann
sollen eben die Argumente dazu gecheckt werden (wird da gerade ein
Standardscript aufgerufen? Oder wurde der Parameter noch nie gesehen
zuvor?).
In meinem kindlichen Leichtsinn habe ich das erstmal ganz simpel
angegangen und habe meine Datenbankinhalte in einen
NaiveBayes-Algorithmus gesteckt (hier etwas Pseudocde zum
Veranschaulichen):
1
use Algorithm::NaiveBayes;
2
# SELECT `command`, `args`, `workdir`, `event`, `userid` FROM `command_stats`
3
while( @db_result = $sth->fetchrow_arrayref() ) {
4
$naive_bayes-> add_instance(
5
attributes => {
6
args => "X".$db_result[ args ], # awkward, but needs to be done to prevent strings being interpreted as numbers
7
workdir => $db_result[ workdir ],
8
uid => $db_result[ userid ],
9
what => $db_result[ event ],
10
},
11
label => $db_result[ command ] # z.B. /usr/bin/nslookup
12
}
13
14
$naive_bayes->train(); # Train the bayes-algorithm
Im nächsten Schritt hab ich dann einfach die Livedaten von einem
laufenden System an die predict()-Funktion geschickt und hätte gehofft,
dass mir dort mehr oder minder sinnvolle Wahrscheinlichkeiten für den
jeweiligen Prozess präsentiert werden. Zum Testen nutze ich ein
openSUSE, welches standardmäßig einen Alias für `ls` besitzt, sodass `ls
-N --color=tty -T 0` ausgeführt wird. Sprich, wenn ich in meiner Konsole
`ls` ausführe, hätte ich erwartet, dass der Bayes-Algorithmus mit hoher
Wahrscheinlichkeit den Befehl `ls` ausgibt. Denn kein anderes Programm
verwendet diese Argumente. Die Wahrscheinlichkeiten die ich erhalte sind
aber eher bei 3%, teilweise sogar richtig klein mit irgendeinem 10^-54
Wert.
Daher jetzt meine Frage, ob ihr mir da irgendwie weiterhelfen könntet,
wie man so etwas implementiert. Gibt es geeignetere Algorithmen dafür?
Einigermaßen schnell sollte er sein (nicht umbedingt beim Anlernen, aber
dann beim Füttern mit Beispielwerten). Zum Bayes-Algorithmus gibt's zwei
ganz interessante Papers:
https://www.scribd.com/document/236482283/Detecting-Intrusion-in-Data-Mining-using-Naive-Bayes-Algorithmhttp://cs.fit.edu/~pkc/id/related/schultz-ieeesp01.pdf
Hoffe, das war jetzt nicht zu viel Text und schon mal Danke für alle
Hinweise.
Moritz
P.S. der Code ist derzeit noch sehr alpha, daher hab ich ihn bisher noch
nirgends hoch geladen. Wenn wirklich Interesse besteht, könnt ihr euch
per PM an mich wenden.
hmm - ich versteh dein Ziel in dem Code nicht ganz.
Ich interpretiere das so:
Du versuchst einen Bayes Klasifikator zu trainieren, anhand der
Parameter, der User id und des Working direktory, auf basis einzelner
Events das Programm herauszufinden. oder was ist 'command'?
Was imho fehlt ist eine saubere einteilung der Testdaten in Klassen, die
du zu zu erkennen veruchst, nämlich: böse und gut. (bzw die
böshaftigkeit, als Wahrscheinlickeit)
Der Trick beim Machine Learning ist es, möglichst gute
Unterscheidungsmermale zu finden, die die Klassen gut trennen und einen
geeigneten Klassifikator, der in dem vorhandenen Merkmalsraum gut
trennen kann.
Das Problem, was ich bei dir sehe ist vor allem, du betrachtest die
Events getrennt. Der Klassifikator sieht aber keinen Zusammenhang
zwischen zwei zu klassifizierenden Merkmalsvektoren.
Er betrachtet an sich jeden Datensatz unabhängig und versucht
gemeinsamkeiten zu finden. Ob ein Programm in der Regel einen Fork pro
minute oder 1000 macht, wird er aus deinen Daten nicht lernen können.
(vielleicht hab ich aber auch deinen Code, oder Ansatz auch nicht
verstanden)
Aussagekräftiger wird es, wenn du statistiken über das Programm erhebst
und diese als Merkmale benutzt.
Wenn ich unter Windows einen Blick in die möglichen Spalten des Process
Explorers von SysInternals werfe, drängen sich diese Statisticen gerade
zu auf. ODer auch ein paar andere Kennzahlen.
An der Aussagekraft von Forkstatistiken hege ich aber große Zweifel.
Ein Tool, was zB in Kurzer Zeit (!) massenweise Dateien beschreibt
könnte ein Verschlüsselungstrojaner sein, oder auch nur ein
Kopierprogramm oder RCS.
Hallo Vlad,
danke für deine Antowrt.
Das Problem mit der Klassifizierung ist, ich habe leider nur
Positiv-Daten, also ich kann nur sagen, welche Prozessaufrufe einem
normalen Verhalten entsprechen. Deswegen war meine Idee eben den
Prozessnamen als Klasse zu verwenden und zu schauen, wie nah die
Parameter für den Aufruf des Prozesses an den "guten" Patterns ist. Wenn
die mit einer hohen Wahrscheinlichkeit auf den Prozess passen, dann ist
alles i.O.
Wenn die Aufrufparameter dem Prozess nicht zugeordnet werden können,
dann stimmt da etwas nicht.
Als Daten in der Datenbank hab ich eine Tabelle mit den Namen der
Befehle, wann sie erstmals aufgerufen wurden und einer sha256sum, um zu
sehen, ob die Datei binär noch gleich ist beim nächsten Aufruf.
Eine zweite Tabelle speichert dann bei jedem Event
(Exec/Fork/Exit/Coredump/...) eine Zeile mit den Aufrufparametern. Also
z.B. Workdir, aufrufender Benutzer, Argumente, Zeitstempel des Aufrufs,
etc.
Den Ansatz das ganze statistisch weiter aufzuwerten kann ich mir mal
ansehen. Denkbar wären ja z.B. die Länge die ein Prozess typischer weise
läuft, oder evtl. auch direkt die fork-raten. Bei Befehlen die
eigentlich nur durch Cron aufgerufen werden, sollte auch ein Muster zu
erkennen sein, dass der Befehl immer zur Stunde X, Minute Y ausgeführt
wird.
Das sind halt leider alles Kriterien, die ich als Mensch ganz gut
verstehe, aber derzeit habe ich noch Verständnisprobleme, wie ich den
Algorithmus dazu bringe, das ebenfalls richtig einzuordnen.
Ich gebe dir ebenfalls recht mit der schwierigen Unterscheidung eines
einfachen, forkenden Kopierprozesses und einem Verschlüsselungsvirus.
Wobei der Virus immerhin als neuer Prozess auftauchen würde, der vorher
gar nicht in der Sammlung der "guten" Programme auftaucht.
Auch müsste es ja z.B. leicht zu erkennen sein, wenn z.B. Python
plötzlich vom Webserver ausgeführt wird, was in den gesammelten Daten
nie der Fall war.
Das könnte man alles händisch programmieren, aber interessant, wäre eben
eine selbstlernende Universallösung. Ob da der Bayes-Algorithmus
überhaupt der richtige ist, weiß ich leider auch nicht.
Orientiert sich die Wahrscheinlichkeit nicht am Kontext aller Prozesse
einer ganzen Woche oder eines Monats? Da sind 3 % schon ordentlich.
Versuch gute Kluster der Daten zu bilden und klassifiziere alles
innerhalb als gut. Der Prozess der außerhalb des Klusters läuft ist
verdächtig.