IP-Spoofing und Buffer Overflows verstehen
Linux Sicherheit

BetriebssystemKomponentenOpen SourceSicherheitSicherheitsmanagementSoftwareWorkspace

Per IP-Spoofing verbergen Angreifer ihre wahre Identität, um dadurch Zugriff auf einen geschützten Server oder Desktop-PC zu erhalten. Doch der Administrator kann sich dagegen wehren.

Gegenmaßnahmen.

IP-Spoofing und Buffer Overflows verstehen

Nicht jeder Cracker benutzt einen geklauten Account, um im lokalen Netzwerk Rechner zu kompromittieren. Manche fälschen einfach ihre IP-Adresse. Somit lässt sich die Adresse nicht zuordnen und eine eventuell eingeleitete Fahndung verläuft schnell im Sand. Dieser IP-Spoofing genannte Trick funktioniert vor allem gut in LANs, in denen der Angreifer gleich noch den restlichen Verkehr abhören kann. Zugute kommt dem Cracker dabei das Design der Protokollfamilie TCP/IP.

Protokollgrundlagen
Beim Internet Protocol (IP) handelt es sich um ein verbindungsloses Protokoll: Der Sender erhält keine Nachricht darüber, ob ein Paket sein Ziel erreicht hat. Die Header der IP-Pakete enthalten 12 Byte an Informationen über das Paket selbst, zum Beispiel dessen Länge. Die nächsten 4 Byte des Headers nehmen die Quelladresse des Pakets auf, danach folgen 4 Byte mit der Zieladresse. Kein Mechanismus schützt diese Daten. Sie können völlig falsch sein und lassen sich zudem von Dritten bei der Übertragung verändern. Das Transmission Control Protocol (TCP) unterscheidet sich vom IP dadurch, dass es verbindungsorientiert arbeitet.
Die Verbindung zwischen Sender und Empfänger funktioniert dabei über die Handshake- Sequenz »SYN«, »SYN/ACK« und » ACK«. Ist die Verbindung aufgebaut, müssen sich beide Teilnehmer gegenseitig über den Fortschritt der Verbindung auf dem Laufenden halten. Dies geschieht durch »Sequences« und »Acknowledgements «: Der Sender bekommt vom Empfänger für jedes empfangene Paket ein »OK« zurück. Trifft das »OK« nicht ein, schickt der Sender das Paket erneut. In den ersten 2 Bytes des TCPPaketheaders befindet sich die Nummer des Quellports, anschließend kommt die Nummer des Zielports. Dieser Information folgen weitere 4 Bytes für die Sequence- Nummer. Den Schluss macht die Acknowledge-Nummer.
Die Sequence-Nummer identifiziert das Paket, die Acknowledge-Nummer die Sequence-Nummer des nächsten erwarteten Pakets. So einigen sich beide Seiten ? Absender und Empfänger ?, dass das richtige Paket abgeschickt, korrekt übertragen und emfangen wurde. Die Protokolle TCP und IP haben allerdings zwei dicke Sicherheitslöcher.


Sicherheitslöcher im TCP/IP

IP-Spoofing und Buffer Overflows verstehen

Die Quelladresse ist offensichtlich beliebig änderbar. Ein Angreifer kann somit einem Kommunikationspartner eine andere Maschine vorgaukeln. Gefährlicher ist das sogenannte »Session Hijacking«, mit dem sich sogar ganze Sessions übernehmen lassen. Dazu muss sich der Angreifer an einer passenden Stelle zwischen den kommunizierenden Maschinen befinden und in der Lage sein, die nächste Sequenznummer vorherzusagen. Nach einer initialen Authentifizierung ist diese Nummer der einzige Mechanismus, den die beiden Partner verwenden können, um sicherzustellen, dass sie mit dem richtigen Partner kommunizieren.

Unbemerktes Eindringen
Ein Angreifer, der diese Sequenznummer kennt, kann also unbemerkt an die Stelle eines der beiden Kommunikationspartner treten. In den 80ern waren die Sequenz- Nummern so einfach geformt, dass das Erraten keine Kunst war. Die Nummer heute vorherzusagen, ist immer noch möglich, aber mit einem deutlich größerem Aufwand verbunden. Für den Angreifer unangenehm ist die Tatsache, dass ein Angriffsversuch mit Hilfe von IP-Spoofing quasi blind erfolgt. Da er eine gefälschte IP-Adresse verwendet, kommen keine Antwortpakete beim Angreifer an. Er muss sich also darauf verlassen, dass seine zugestellten Pakete bestimmte von ihm erwünschte Reaktionen auslösen. Um mit einer gefälschten IPAdresse in einen Rechner einzudringen, muss dieser zusätzlich ein bekanntes Sicherheitsloch haben, das sich ungesehen nutzen lässt. Dies könnte etwa eine Remote-Shell sein, über die der Angreifer dann beispielsweise Benutzeraccounts anlegt. Doch es existieren auch weniger blinde Attacken.

Die Mitte hört zu
Bekannt und gefürchtet ist der Angriff »Man-in-the-Middle«, auch »Non-Blind Spoofing« genannt. Diese Angriffsform heißt so, weil sich der Angreifer dabei im gleichen Subnetz wie das Opfer befindet. Dies erleichtert ihm den Angriff, denn er kann die Sequenz- und Acknowledge- Nummern einfach aus den Paketen mitschneiden. Dazu kommt ein Sniffer- Programm wie Ethereal (www.ethereal. com) zum Einsatz. Damit lässt sich der Datenverkehr an einer Schnittstelle im Netzwerk einfach protokollieren. Per Non-Blind Spoofing lässt sich eine Session relativ leicht übernehmen und dadurch zuvor etablierte Vertrauensstellungen mit der angegriffenen Maschine wiederverwenden. Effektiv umgeht der Übeltäter damit beispielsweise Passwortabfragen bei Zugriffen auf Ressourcen im lokalen Netzwerk.


Server überlasten

IP-Spoofing und Buffer Overflows verstehen

Vor allem aus dem Webbereich bekannt sind »Denial-of-Service-Attacken« (DoS), bei dem ein Server durch eine regelrechte Flut von Anfragen in die Knie geht. Hierbei kommt das IP-Spoofing nahezu immer zum Einsatz. Der Angreifer interessiert sich nicht dafür, dass ein Paket zu ihm zurückkommt. Er will den Angegriffenen einfach nur mit Paketen überfluten. Gleichzeitig soll der Angriff natürlich auch möglichst lange anhalten. Und das ist am effektivsten, wenn sich die angreifenden Rechner nicht ohne weiteres aufspüren lassen. Eine typische DoS-Angriffsform mit IP-Spoofing ist das Syn-Flooding. Wie Sie bereits erfahren haben, wird eine Kommunikations- Session mit drei Schritten eingeleitet: Der Client sendet SYN, der Empfänger antwortet mit SYN/ACK und der Client bestätigt schließlich mit ACK. Was aber, wenn der Client ein Angreifer ist und mit IP-Spoofing arbeitet? In diesem Fall erhält der angegriffene Rechner einfach kein ACK und die Verbindung bleibt halb geöffnet. Es kann aber nur eine gewisse Anzahl von halboffenen Verbindungen geben. Wird diese Anzahl überschritten, kann der Server keine weiteren Verbindungen mehr annehmen und ist effektiv für andere Hosts im Internet nicht mehr erreichbar. Der Server wurde mit »Syn«-Anforderungen überflutet. Derlei Angriffe sind allerdings nicht mehr neu. Moderne Firewalls und sogar Serverprogramme erkennen diese Attacke ganz ohne weitere Benutzermaßnahmen und blocken diese ab.

IP-Spoofing abwehren
Das einfachste Mittel gegen IP-Spoofing ist der Einsatz entsprechender Regeln in der Firewall. Die Brandmauer muss von außen eingehende Pakete daraufhin prüfen, ob deren IP-Header aus dem Adressbereich des internen Netzes stammen. Trifft dies zu, handelt es sich mit Sicherheit um eine gefälschte Adresse, denn die internen Adressen befinden sich ja auf der anderen Seite der Firewall. Der Administrator sollte aber auch ausgehende Pakete auf IP-Spoofing verdächtige Pakete filtern. Eventuell möchten Pakete das lokale Netzwerk verlassen, die nicht zum eigenen Vorrat öffentlicher IP-Adressen passen. Fängt die Firewall solche Pakete ab, dann fälscht jemand aus dem eigenen Netzwerk Adressen und verschickt derart getürkte Pakete ins Internet. Das ist unter allen Umständen zu unterbinden, da sich das Unternehmen sonst schnell mit Schadenersatzforderungen konfrontiert sieht.


Buffer Overflows im Schnelldurchgang

IP-Spoofing und Buffer Overflows verstehen

Bei Einbrüchen in Rechner gilt eine einfache Regel: Der Angriff war erfolgreich, also hat der geknackte Computer ein Sicherheitsloch. Besonders problematisch, da im Vorfeld für den Administrator praktisch nicht zu erkennen, sind die so genannten Pufferüberläufe (Buffer Overflows). Buffer Overflows sind hauptsächlich deshalb möglich, weil die extrem weit verbreitete Programmiersprache C/C++ den Speicher auf eine ganz bestimmte Art verwendet. Um die zugehörige Problematik zu verstehen, braucht es aber noch ein wenig mehr Hintergrundwissen, das Linux Professionell im weiteren Verlauf sehr verständlich vermittelt. Üblicherweise ist ein Programm in verschiedene Funktionen aufgeteilt, die in einer
bestimmten Reihenfolge aufgerufen werden. Bei einem Programm, das Adressen verwaltet, könnte es eine Funktion namens »DruckeName()« geben. Diese Funktion erhält mit dem auszudruckenden Namen noch einen Funktionsparameter. Die Funktion beherrscht allerdings noch mehr, als nur Namen zu drucken. Sie wandelt beispielsweise alle Namen vor dem Ausdrucken in Großbuchstaben um. Die Umwandlung geschieht jedoch nicht auf Basis der Originaldaten. Diese sollen unverändert bleiben. Stattdessen wandert der in der Funktion als Parameter übergebene Name in einen separaten Puffer (Buffer), bevor die Funktion die einzelnen Buchstaben anschließend in Großbuchstaben umwandelt.

Überschreiben des Speichers
Solche Puffer kommen nicht nur bei der Verarbeitung von Daten zum Zuge, sondern auch bei deren Erfassung. Angenommen das angesprochene Programm zur Adressverwaltung läuft auf einem Server und ist dort über eine Weboberfläche erreichbar. In diesem Fall gibt der Anwender die Namen in HTML-Formulare ein, die der Browser dann an den Server sendet. Dort kümmert sich dann wieder eine Funktion um die Namen und legt diese ebenfalls in einem spezielle Puffer ab. Hier sind die Daten (Namen) das Resultat einer Benutzereingabe. Und in diesem Zusammenhang stellt sich die Frage für den Programmierer: Wie lang darf ein Name sein oder wie viele Zeichen sind dafür vorgesehen? Den Puffer reserviert ein Programm ganz normal im Hauptspeicher. Im einfachsten Fall benötigt es für jeden Buchstaben ein Byte an Speicher. Der Puffer ist also in Byte gerechnet so lang, wie der Name Buchstaben hat. Sieht nun der Programmierer eine feste Menge an Speicher für den Puffer vor, hat aber der eingegebene Name mehr Buchstaben als der Puffer Speicherzellen, dann kommt es offensichtlich zum Konflikt. Entweder das Programm stellt hier sicher, dass ein Benutzer keine Namen eingibt, die mehr Buchstaben haben, als Speicherzellen zur Verfügung stehen. Oder der lange Name überschriebt ganz einfach Speicher, den das System bereits für andere Zwecke vorgesehen hat.


Löcher in Puffern ausnutzen

IP-Spoofing und Buffer Overflows verstehen

Ein Buffer Overflow ist letztlich immer ein Programmierfehler: Das Programm kann oder will nicht sicherstellen, dass der Puffer groß genug für die Eingangsdaten ist. Eventuell ist der Puffer für die meisten Namen auch ausreichend dimensioniert, so dass der Lapsus unter Umständen gar nicht auftritt. Wodurch wird die Sache also zu einem Sicherheitsproblem? An dieser Stelle kommt die eingangs erwähnte Sprache C/C++ ins Spiel. Genau wie die Daten, die ein Programm bearbeitet, liegt auch der Programmcode einfach im Hauptspeicher vor. Der Anfang jeder Funktion liegt an einer jeweils eindeutigen Speicherzelle im RAM. Die Nummer diese Zelle heißt einfach »Adresse«. Ruft ein Programm nun eine Funktion auf, dann führt das zur Laufzeit des Programms dazu, dass die Adresse der Funktion direkt angesprungen wird. Dabei übergibt das Programm Parameter an die Funktion. Aber woher weiß der Prozessor, an welcher Stelle (Adresse) es im Programm weitergeht, wenn die Funktion beendet ist? C/C++-Programme legen diese Informationen in einem speziellen Speicherbereich ab: dem Stack. Dieser befindet sich ebenfalls im RAM. Es handelt sich dabei um einen Speicherbereich, den C/C++-Programme für verschiedene Zwecke nutzen, nicht nur für die Übergabe von Parametern. Auch der Speicher für die Puffer aller Variablen, die das Programm innerhalb von Funktionen verwendet, stammt aus dem Stack. Die Verwaltung des Stacks ist dabei ganz einfach: Der Speicher, der zuletzt belegt wurde, muss als erster wieder freigegeben werden. Daher auch der Name Stack (Haufen).


Rücksprungadresse fehlerhaft

IP-Spoofing und Buffer Overflows verstehen

Ruft ein Programm die Beispielfunktion aus der Adressverwaltung auf, kopiert diese nicht nur den übergebenen Namen auf den Stack. Auch die Adresse der nächsten Operation, die nach dem Beenden der Funktion selbst aufgerufen werden soll, kommt auf den Stapel. So weiß die nachfolgende Funktion oder Operation, an welcher Stelle das Programm anschließend weiterlaufen soll. Außerdem befindet sich bereits ein Puffer auf dem Stack: In diesen kopiert die Funktion den übergebenen Namen. Und genau da setzt der ärgerliche Aspekt von C/C++ ein: Die übergebenen Parameter, die Rücksprungadresse und der vorhandene Puffer sind so zueinander organisiert, dass im Falle eines Pufferüberlaufs die Rücksprungadresse überschrieben wird. Ist der eingegebene Name zu lang für den vorgesehenen Puffer, dann überschreibt er die Rücksprungadresse. Somit kann das Programm nicht mehr zur »richtigen« Adresse zurückkehren und dort weiterlaufen. Dies fällt bis dahin nicht auf. Als Resultat liest das Programm irgendwann die (überschriebene) Rücksprungadresse aus und übergibt die Kontrolle an die nächste Instruktion, die diese Adresse anspringt.

Code einschmuggeln
Da die Rücksprungadresse jedoch mit den Zeichen eines Namens überschrieben wurde, gibt es nun zwei mögliche Fälle: Entweder die Zeichen ergeben (zufällig) einen für eine Adresse gültigen Wert oder eben nicht. Handelt es sich nicht um einen Wert, der sich als Adresse auswerten lässt, stürzt das Programm sofort ab. Ist der Wert wider Erwarten gültig, übergibt das Programm die Kontrolle an diese Adresse und das Programm läuft dort weiter. Mit Sicherheit geht dies jedoch nicht lange gut, schließlich war ein solcher Programmsprung nie vorgesehen. Wenige Instruktionen später verabschiedet sich das Programm ebenfalls mit einem Absturz. Nur ? es gibt auch noch einen dritten Fall. Angenommen, der Puffer wurde nicht überschrieben, weil zufällig jemand einen zu langen Namen eingegeben hat. Stattdessen hat ein Angreifer mit voller Absicht einen solchen langen Namen platziert. Mit ein wenig Glück kann der Angreifer sogar noch ein paar eigene Codezeilen mit in den langen Namen packen. Diese lässt er dann einfach ausführen, denn dazu muss er nur die Rücksprungadresse richtig setzen. Damit erhält der Angreifer ein Fenster für einen Buffer-Overflow-Angriff. Entweder der Cracker schmuggelt eigenen Code ein oder benutzt eigene Daten, die das Programm dann im Kontext eines anderen Benutzers als Eingangswerte für eine Funktionen verwendet. In beiden Fällen erhält der Angreifer Zugriff auf den attackierten Rechner.


Patchen, patchen, patchen

IP-Spoofing und Buffer Overflows verstehen

Einbrüche durch Buffer Overflows lassen sich nur verhindern, wenn der Administrator gewissenhaft arbeitet. Jedes Programm, das im Netzwerk läuft, muss ständig beobachtet und mit den aktuellsten Sicherheitspatches versorgt werden. Manche Distributoren wie Novell Suse versorgen ihre registrierten Anwender im Rahmen des Produktsupports automatisch mit Security-Patches oder weisen per E-Mail auf neu aufgetauchte Löcher hin. Wer hingegen Software einsetzt, die der Distributor nicht im Programm hat, abonniert am besten einen Newsletter wie Securityfocus (www.securityfocus.com) oder Crypto-Gram (www.schneier.com). Diese halten den Administrator nicht nur auf dem Laufenden, sondern verlinken meist gleich auf den entsprechenden Patch zum Download. Das System immer up to date zu halten ist mühsam. Vernachlässigt der Verwalter jedoch seine Pflichten, dauert es nicht lange, bis ein Einbruchsversuch erfolgreich ist. Und hat der Cracker erst einmal Zugriff auf ein System im lokalen Netzwerk, ist die gesamte IT im Unternehmen gefährdet.

Weitere Infos
Grundlage für IP-Spoofing sind zwei Aufsätze von Robert Moris (1985) und Stephen Bellovin (1989) – mit durchschlagendem Erfolg. Nachdem die Autoren diese Dokumente öffentlich machten, wurden Angriffe mit Hilfe von IP-Spoofing zum Tagesgeschehen.
Sequence Prediction, Robert Morris: www.pdos.lcs.mit.edu/~rtm
/papers/117.pdf