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.
Funktionsweise
Im sicheren Modus informiert lsof nur den Benutzer "root" umfassend. Aber auch im unsicheren Modus darf 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 |
Ein Aufruf von lsof ohne Parameter liefert zu viele Informationen, um sie zu überschauen. Mit gezielt gewählten Optionen beschränkt sich lsof auf die gerade gewünschten Daten. Bei der Kombination mehrerer Optionen verknüpft lsof diese per Vorgabe mit ODER-Logik. Um sicherzustellen, dass alle Bedingungen erfüllt sind, muss zusätzlich die Option "-a" (UND-Verknüpfung) angegeben werden.
Installation
Systemkompatibilität und Sicherheit sprechen gegen vorkompilierte Versionen. lsof sollte deshalb im Idealfall in einer aktuellen Version auf dem Zielsystem selber kompiliert werden, um den vollen Funktionsumfang und beste Stabilität zu garantieren.
Folgende Befehle holen die Quellen aus dem Internet, prüfen die Signatur mit GNU PG, konfigurieren die Quellen und kompilieren sie.
$ 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
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.
$ ./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 es allerdings einem Eindringling gelingen, den Kernel (etwa mit einem Kernel-Rootkit) zu ä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 sämtliche 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 |
-a | UND-Verknüpfung. |
-d Dateideskriptor | Anzeige aller gerade geöffneten Dateien, wobei anstatt der sonst üblichen Prozessnummer der angegebene Dateideskriptor aufgelistet wird. Dadurch ergibt sich eine ähnlcieh Liste, wie sie mit "ps aux" zu sehen ist. "txt" steht für Programmtext (weitere Dateideskriptoren finden sich über "man lsof").
$ lsof -d txt |
+D Verzeichnis | Anzeige aller gerade geöffneten Dateien im angegebenen Verzeichnis und seinen Unterverzeichnissen, ohne dabei auf symbolische Links zu achten.
$ lsof +D /tmp |
-F Felder | Diese besonders formatierte Ausgabe sorgt dafür, dass nachgeschaltete Programme einzelne Felder besser parsen können. Details dazu finden sich im Manpage-Abschnitt "Output for other programs". |
+L Linkzähler | 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 +L 1 |
-i | Anzeige aller gerade geöffneten netzwerkrelevanten Dateien.
$ lsof -i Anzeige aller gerade vom Benutzer "www-data" geöffneten Netzwerkdateien (UND-Verknüpfung durch "-a"). $ lsof -a -i -u www-data Anzeige aller gerade geöffneten netzwerkrelevanten Dateien, ohne die Portnummern als Dienstbezeichnung auszuschreiben und ohne die Hostnamen aufzulösen (daher deutlich performanter). $ lsof -i -P -n Anzeige aller aktiven Verbindungen. $ lsof -i | grep '\->' |
-i4 | Anzeige aller gerade geöffneten IPV4-bezogenen Dateien.
$ lsof -i4 |
-i6 | Anzeige aller gerade geöffneten IPV6-bezogenen Dateien.
$ lsof -i6 |
-n | Unterdrücken der Namensauflösung. |
-p Prozessnummer | Anzeige aller Dateien, die gerade vom Prozess mit der angegebenen Prozessnummer (PID) geöffnet sind.
$ lsof -p 2326 Anzeige aller Dateien, die gerade von den Prozessen mit den angegebenen Prozessnummern (PID) geöffnet sind. $ lsof -p 2326,5400,5720 |
-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
Identifikation der einen Datenträger blockierenden Prozesse
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 Rechnernetz kopiert, verteilte Netzattacken ausführt oder Spam 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 folgende Aufruf darf in dieser Umgebung nur Prozesse listen, die "/usr/bin/apache2" als Benutzer "www-data" ausführen. Normalerweise trifft das nur auf die Apache-Prozesse zu. Die Option "-a" sorgt für UND-Verknüpfung, "-d txt" listet nur ausgeführte Dateien und "-u www-data" beschränkt die Ausgabe auf den Benutzer "www-data".
$ lsof -a -d txt -u www-data
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 Benutzer 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 usser den 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 als 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 dafür, 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-EOF Status vor der Aenderung: $OLD Status nach der Aenderung: $NEW EOF-EOF fi OLD="$NEW" done
Zum Testen der Anomalie-Erkennung kann der Systemverwalter einen Port öffnen. Folgender Befehl startet nc im Modus "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, so beispilsweise 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 |