lsof
Der Shell-Befehl lsof (list open files) zeigt dem unprivilegierten Benutzer alle geöffneten Dateien - also sowohl reguläre Dateien wie auch spezielle Blockdateien, ausführbare Programme, Bibliotheken, Verzeichnisse, rechnerinterne Datenströme (Unix Domain Sockets) und Netzwerkverbindungen. Dem Benutzer "root" zeigt lsof dagegen alle geöffneten Verbindungen, Ports und Dateien im Netzwerk.
Mit Hilfe der Befehle fuser, ps, netstat und top können ähnliche Informationen gesammelt werden - allerdings weit weniger bequem.
Glsof und Jlsof sind grafische Oberflächen für lsof.
Installation
Systemkompatibilität und Sicherheit sprechen gegen vorkompilierte Versionen. lsof sollte deshalb in einer aktuellen Version auf dem Zielsystem selber kompiliert werden, um den vollen Funktionsumfang und beste Stabilität zu garantieren.
Holen der Quellen aus dem Netz, Prüfen der Signatur mit GNU PG, Konfiguration der Quellen und Kompilierung. Während der Konfiguration muss der Anwender interaktiv Entscheidungen treffen: die Antworten [y], [y], [y], [n], [n], [n] und [y] erzeugen eine typische Umgebung mit Blick auf Sicherheit. Wichtig sind die Optionen "HASSECURITY" und "HASNOSOCKSECURITY": um nur dem Benutzer "root" die Anzeige einer Liste aller offenen Dateien und Sockets aller Benutzer zu erlauben, muss hier mit [y] und [n] geantwortet werden.
$ wget ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/lsof.tar.bz2 $ tar xjf lsof.tar.bz2 $ cd lsof_4.77 $ wget ftp://lsof.itap.purdue.edu/pub/Victor_A_Abell.gpg $ gpg --import Victor_A_Abell.gpg $ gpg --verify lsof_4.77_src.tar.sig lsof_4.77_src.tar $ tar xf lsof_4.77_src.tar $ cd lsof_4.77_src $ ./configure linux $ make -s
Um lsof an einem sicheren Ort aufzubewahren, genügt es, nach dem Kompilieren auf "make install" zu verzichten und die Binärdatei auf einem schreibgeschützten Datenträger unterzubringen, etwa auf einer CD-ROM. Sollte allerdings ein Eindringling den Kernel (etwa mit einem Kernel-Rootkit) ändern, so ist leider auch den Ausgaben des unveränderten lsof nicht mehr zu trauen...
Anzeige der Optionen, mit denen lsof übersetzt wurde. Die Ausgabe "Only root can list all files" bedeutet, dass normale Benutzer lsof nicht verwenden können, um systemkritische Informationen einzusehen.
$ ./lsof -v
Optionen
Option | Beschreibung |
---|---|
Ohne Option werden alle offenen Dateien angezeigt, die gerade von irgendeinem Prozess geöffnet sind.
$ lsof | |
Datei | Anzeige der Prozesse, die gerade auf die angegebene Datei zugreifen. Im Beispiel werden die Prozesse angezeigt, die gerade auf die Datei "/usr/bin/vim" zugreifen (also mit vi arbeiten).
$ lsof /usr/bin/vim |
Gerätedatei | Anzeige der Prozesse, die gerade auf die angegebene Gerätedatei zugreifen. Im Beispiel werden alle Prozesse angezeigt, die gerade auf die CD-ROM-Gerätedatei "/dev/hdc" zugreifen.
$ lsof /dev/hdc |
-i4 | Anzeige aller IPV4-Verbindungen. |
-n | Unterdrücken der Namensauflösung. |
-p Prozessnummer | Anzeige aller offenen Dateien, die vom Prozess mit der angegebenen Prozessnummer (PID) geöffnet sind.
$ lsof -p 2326 |
-P | Unterdrücken der Portnamensauflösung. |
-u Benutzername | Anzeige aller Dateien, die gerade vom angegebenen Benutzer geöffnet sind.
$ lsof -u max Anzeige aller offenen Dateien, die nicht der Benutzer "root" geöffnet hat. $ lsof -u ^root |
Verwendung
Im sicheren Modus informiert lsof nur den Benutzer "root" umfassend. Aber auch im unsicheren Modus darf z. B. nur der Benutzer "root" auf alle Details im Verzeichnis "/proc" zugreifen.
lsof gibt die je nach Parameterliste gefilterten Informationen der offenen Dateien in einem tabellarischen Format aus. Per Vorgabe zeigt es folgende Spalten:
Spalte | Beschreibung |
---|---|
COMMAND | Name des Prozesses |
PID | Prozess-ID |
USER | Name des Systembenutzers, unter dem der Prozess läuft |
FD | Dateideskriptor |
TYPE | Dateityp |
DEVICE | Gerät |
SIZE | Dateigrösse |
NODE | Verbindung |
NAME | Vollständiger Name |
Die Ausgabe lässt sich über "lsof -F <Felder>" auch für die Übergabe an weiterverarbeitende Programme aufbereiten. Eine spezielle Formatierung sorgt dafür, dass die nachgeschalteten Tools einzelne Felder besser parsen können. Details dazu finden sich im Manpage-Abschnitt "Output for other programs".
Ein Aufruf ohne Parameter liefert zu viele Informationen, um sie zu überschauen. Mit gezielt gewählten Aufrufparametern beschränkt sich lsof auf die gerade gewünschten Daten. Bei der Kombination mehrerer Parameter verknüpft lsof diese per Vorgabe mit ODER-Logik. Um sicherzustellen, dass alle Bedingungen erfüllt sind (UND-Verknüpfung), muss zusätzlich die Option "-a" angegeben werden.
Beenden aller Prozesse, die noch auf ein ins CD-ROM-Laufwerk eingelegtes Medium zugreifen.
$ kill $(lsof -t /cdrom)
Anzeige aller offenen Dateien im Verzeichnis "/tmp" und seinen Unterverzeichnissen, ohne dabei auf symbolische Links zu achten.
$ lsof +D /tmp
Anzeige einer ähnlichen Prozessliste wie ps aux durch Auflisten der Einträge mit Dateideskriptoreintrag "txt" statt der sonst üblichen Nummer ("txt" steht für Programmcode und Daten, also eine ausgeführte Datei).
$ lsof -d txt
Anzeige aller gelöschten Dateien, die noch geöffnet sind und daher Plattenplatz verbrauchen, aber in keinem Verzeichnis erscheinen (Dateien mit weniger als einem Link).
$ lsof +L1
Anzeige aller netzwerkrelevanten Dateien.
$ lsof -i
Anzeige aller netzwerkrelevanten Dateien, ohne die Portnummern als Dienstbezeichnung auszuschreiben und ohne die Hostnamen aufzulösen (daher deutlich performanter).
$ lsof -i -P -n
Anzeige aller IPV6-bezogenen Dateien.
$ lsof -i6
Anzeige aller aktiven Verbindungen.
$ lsof -i | grep '\->'
Anzeige aller derzeit vom Benutzer "www-data" geöffneten Netzwerkdateien (UND-Verknüpfung durch "-a").
$ lsof -a -i -u www-data
Identifikation von einen Datenträger blockierenden Prozessen
lsof kann auch zur Identifikation von Prozessen dienen, die verhindern, dass der Benutzer einen Datenträger aushängen kann. Der lsof-Aufruf mit der Option "-t <Verzeichnis>" liefert eine Liste numerischer Prozess-IDs, die auf die CD-ROM zugreifen. Die Methode ist ebenso drastisch wie wirkungsvoll.
$ umount /dev/cdrom umount: /cdrom: device is busy $ kill -15 $(lsof -t /dev/cdrom) $ umount /dev/cdrom $ eject
Wiederherstellung gelöschter Daten
Dies klappt mit lsof und cat, solange irgendein Prozess die Dateien noch geöffnet hält. lsof ermittelt dann Datei und Prozess und je nachdem, auf welche Weise der Prozess die Datei geöffnet hat, ist ihr Inhalt an einer anderen Stelle im "/proc"-Dateisystem noch zugänglich.
Stark angestiegene Netzwerklast auf LAMP-System
Angenommen wird, dass die zusätzliche Last nicht über Seitenzugriffe, sondern durch einen Eindringling verursacht wird, der Dateien übers Netzwerk kopiert, verteilte Netzattacken ausführt oder Spam-E-Mails verschickt. In einer LAMP-Umgebung ist die Schnittstelle von PHP zum System eines der Hauptangriffsziele, da PHP an etlichen konzeptionellen Schwächen leidet, auch schlampig geschriebene Skripte laden Angreifer ein. Der Webserver Apache läuft standardmässig als eigener Benutzer (als "www-data", "apache", "httpd" oder gar als "nobody"). In der Regel laufen zusätzliche Prozesse als Benutzer "root", um privilegierte Ports und die Logdateien zu öffnen. Die Datenkommunikation dagegen wird durch unprivilegierte Prozesse erledigt. Auf einem Webserver gibt es also einen bestimmten Erwartungshorizont an Kombinationen aus Benutzern, auszuführenden Dateien und geöffneten Ports.
So führt etwa der Benutzer "www-data" unter Debian das Programm "/usr/bin/apache2" aus. Der Aufruf "lsof -a -d- txt -u www-data" darf in dieser Umgebung nur Prozesse listen, die "/usr/bin/apache2" als Benutzer "www-data" ausführen (Die Option "-a" sorgt für UND-Verknüpfung, "-d txt" listet nur ausgeführte Dateien, "-u www-data" beschränkt die Ausgabe auf den Benutzer "www-data"). Normalerweise trifft das nur auf die Apache-Prozesse zu.
Ist es einem Angreifer gelungen, PHP oder PHP-Skripte zu manipulieren und Systembefehle und Programme auf dem Server auszuführen, so laufen diese üblicherweise als derselbe Benutezr wie Apache - ausser wenn der Eindringling über weitere Lücken die Rechte von Benutzer "root" erlangen und seine eigenen Spuren verschleiern konnte. Zeigen sich Prozesse des Apache-Benutzers, die auf anderen Binärdateien basieren oder unerwartete Ports offenhalten, so ist Vorsicht angebracht: mit "lsof -p <PID>" können verdächtige Prozesse detailliert auf Netzwerkverbindungen, geladene Bibliotheken, geöffnete Dateien und mehr untersucht werden.
Da Cracker gerne ihre eigenen Server für FTP, IRC, Telnet oder SSH mitbringen, gehört zur Erstanalyse auch die Suche nach offenen Ports. Folgender Aufruf listet alle IP-Sockets ("-i"), die der Apache-Benutzer ("-u www-data") geöffnet hat und die als Server auf Verbindungen warten (daher "grep LISTEN").
$ lsof -a -i -u www-data | grep LISTEN
Alles abseits der Ports 80 (HTTP) und 443 (HTTPS) ist verdächtig. Ähnliche Resultate liefert zwar auch netstat, doch hilft lsof auch bei der weiteren Analyse und erspart somit einen Programmwechsel.
Mit Hilfe eines kleinen Skripts kann ein vordefinierter Systemzustand mit dem aktuellen Zustand verglichen und bei Abweichungen nach vordefinierten Regeln behandelt werden. Folgende Zeile weist lsof an, netzwerkbezogene Dateien auszugeben ("-i TCP"), ohne dabei Portnummern asls Servicename auszuschreiben ("-P") und ohne IP-Adressen in Hostnamen aufzulösen ("-n"). awk fahndet in der Ausgabe nach Ports im Status "LISTEN" und formatiert sie neu als "Benutzername/Prozessname/IP:Port", wobei die IP-Adresse "*" für Server steht, die auf allen Schnittstellen lauschen. Das abschliessende sort ordnet die Ausgabe alphabetisch und sorgt dafpr, dass jede Benutzer-Dienst-Kombination nur einmal erscheint ("-u").
$ lsof -i TCP -n -P | awk '/LISTEN/ { print $1"/"$3"/"$8 }' | sort -u
Folgendes lsof-Mini-IDS-Skript merkt sich die aktuelle Port-Konfiguration. Alle 10 Sekunden ("while sleep 10; do") holt lsof die Liste der geöffneten Ports und vergleicht sie mit dem vorigen Status. Hat sich etwas verändert, so wird eine E-Mail mit dem Vorher-Nachher-Status geschickt und der neue Status als neue Basis für weitere Vergleiche benutzt.
#!/bin/bash MAILTO="root" HOSTAME=$(hostname) getports() { lsof -i -n -P | awk '/LISTEN/ { print $1"/"$3"/"$8 }' | sort -u } OLD="$(getports)" echo -e "Beginne mit folgender Port-Belegung:\n$OLD" while sleep 10; do NEW="$(getports)" if test "$OLD" != "$NEW"; then echo "Aenderung der Portbelegung! Informiere Systemverwalter per E-Mail" mail -s "Achtung: $HOSTNAME LISTEN-Status geaendert" $MAILTO <<EOF Status vor der Aenderung: $OLD Status nach der Aenderung: $NEW EOF fi OLD="$NEW" done
Zum Testen der Anomalie-Erkennung kann der Systemverwalter einen Port öffnen. Folgender Befehl startet Netcat im Mosus "LISTEN" ("-l") und hält den Port 12345 offen. Nach spätestens zehn Sekunden sollte das in seiner Schleife verharrende Shellskript die Veränderung bemerken und mit einer entsprechenden Warn-E-Mail reagieren.
$ nc -l -p 12345
Achtung: Manche Prozesse verändern ihre für lsof sichtbare Portbelegung, z. B. E-Mail-Server, die je nach dem Zustand der eingehenden Verbindungen zusätzliche Prozesse forken und sie unter anderen Namen laufen lassen. Unter Umständen lösen also auch solche Prozesse einen (falschen) Alarm aus. Das ist durch eine Anpassung in der Abfragelogik des obigen Skripts aber rasch zu unterbinden: ein angehängtes "| grep -v <Temporärer Dienst>" in der Funktion "getports()" sollte genügen.
Weblinks
Herausgeber | Sprache | Webseitentitel | Anmerkungen |
---|---|---|---|
Johannes Franken | eng, ger | lsofwbm | Anwendungsbeispiele für lsof |
Wikipedia | eng | lsofwbm | Enzyklopädischer Artikel |