SYN-Flood kurz erklärt

SYN-Flood bezeichnet einen einfachen und ohne Vorkehrungen sehr effektiven Denial of Service (DoS) Angriff auf Internetdienste. Insbesonders zusammen mit IP-Spoofing eingesetzt, ist es nicht einfach diesen abzuwehren. Allerdings haben sich einige Methoden etabliert, die SYN-Attacken wenn auch nicht verhindern, so zumindest relativ effektiv bekämpfen.

Grundlagen

SYN-Attacken beruhen auf einer ganz gewöhnlichen Eigenschaft des TCP: dem Verbindungsaufbau. Das TCP ist im Internet immanent präsent, beinahe jeder nennenswerte Dienst im WWW basiert darauf, insbesondere allgegenwärtige Protokolle wie HTTP (Web-Browsing), SMTP, POP3 und IMAP (alle E-Mail) setzen darauf auf. Das “Transmission Control Protocol” gehört zu den verbindungsorientierten Protokollen, weil eben formal eine Verbindung erst aufgebaut und nach Ende der Übertragung wieder regulär beendet werden muss. Das unterscheidet TCP zum Beispiel von UDP (was hauptsächlich für Multimediastreaming, etwa Online Spiele eingesetzt wird).

Ein aktiver Klient verbindet sich bei TCP mit einem passiven Dienst, die beiden tauschen ein paar Informationen aus und dann gilt die Verbindung als hergestellt. Von da an können dann beidseitig Daten ausgetauscht werden, vereinfacht die Payload, der Inhalt des TCP-Pakets, den es zu transportieren gilt (Webseiten, E-Mails, …).

Bei SYN-Attacken spielt der Verbindungsaufbau die erwähnte ganz besondere Rolle. Dieser nennt sich bei TCP “Drei-Wege-Handshake”, da er über drei Pakete zwischen den beiden beteiligten Parteien (Klient und Server) erfolgt. Bildlich funktioniert das so, dass der Klient beim Server seine Ankunft ankündigt (1). Der Server antwortet dem Klienten über den Rückkanal, dass er die Ankuft des Klienten erwarte (2), worauf der Klient tatsächlich eintrifft (3). Technisch funktioniert das so, dass diverse Flags im TCP-Header gesetzt werden, namentlich “SYN” und “ACK”, daher auch der Name SYN-Flood, da bei diesem Angriff der Klient nur Schritt 1 initialisiert. Die folgende Grafik veranschaulicht den Paketfluss mit jeweils gesetzten Flags:

TCP-Handshake vs. SYN-Flood


Bei der Implementierung von TCP im Betriebssystem werden bereits nach dem ersten “SYN” des Klienten dafür beim Server Ressourcen reserviert um zukünftige Anfragen beantworten zu können. Damit gibt es eine endliche Größe an Verbindungen die ein Server gleichzeitig bearbeiten kann, bis die Verbindungsliste im Server voll ist, also keine weiteren Verbindungen akzeptiert werden. Für einen regulären Klienten, der dann diesen Dienst in Anspruch nehmen will, stellt es sich dann so dar, als wäre der Server nicht erreichbar (Timeout). Ein Angreifer nutzt diese Tatsache aus und erzeugt sequentiell eine sehr große Anzahl an halboffenen Verbindungen (weil er seinerseits die Verbindung nie bestätigt), bis er diesen Zustand auf dem Server erreicht.

Der Server ist gezwungen für jede einzelne Verbindung Ressourcen bereit zu halten, um auf Reaktionen zu warten, da sich derartige Angriffe nicht von regulären Klienten unterscheiden lassen. Der angreifende Klient hingegen interessiert sich nicht weiter für das gesendete Paket und schließt seine Verbindung, die Antwort (SYN+ACK) vom Server interessiert ihn gar nicht erst. Daher ist es auch möglich und gängige Praxis die absendende IP-Adresse zu spoofen, zu fälschen.

Der SYN-Angriff ist also ein grob unausgewogener Angriff, da der Server ungleich mehr Ressourcen für die Behandlung des selbigen benötigt, als der Klient, der nur ein einzelnes Paket wenige Byte großes Paket erzeugen muss. So ist es auch mit einer geringen Bandbreite und einem relativ schwachen Rechner möglich mehrere Tausend Pakete pro Sekunde zu generieren, die auf den anzugreifenden Rechner abgefeuert werden, während der entfernte Rechner ungleich mehr Ressourcen benötigt diese zu verwalten, ehe er überhaupt keine weiteren Verbindungen mehr akzeptieren kann.

Die Angreifersicht

Am besten kann man Angriffe jeder Art abwehren, wenn man die Techniken und Konzepte dahinter versteht und umsetzen kann. In der Folge daher ein naiver (aber funktionierender) Ansatz an Code für den Angreifer.

Für diese Art von Attacke muss man auf Angreiferseite sehr tief in den TCP-Stack des Betriebssystems eingreifen. Normalerweise kümmert sich dieses nämlich vollständig um alle Angelegenheiten auf dieser Ebene. So ist das Herstellen, Beenden und Aufrechterhalten der Verbindung zwischen zwei Peers Aufgabe des TCP-Stacks im Betriebssystem. Bei der normalen Socket-Programmierung wie sie jede gängige Hochsprache (PHP, Java, Python …) bereitstellt hat man jedoch keinen Zugriff auf rohe Sockets, die benötigt werden, wenn man selbst TCP/IP-Pakete bauen möchte. Genau das ist aber nötig, wenn man gezielt Flags im Header setzen möchte. Portabel ist diese Art der Programmierung praktisch unmöglich, da jede Umsetzung sehr tief in die Systemschnittstellen eingreift. Das folgende Perl-Skript verwendet Net::RawIP, das seinerseits eine Schnittstelle zu den Raw-Socket Syscalls im Linux-Kernel darstellt. Unnötig daher zu erwähnen, dass etwa Windows Benutzer damit also keinen Spaß haben werden.

#! /usr/bin/perl use Net::RawIP;
use strict;

my $packet = Net::RawIP->new;
my $saddr = '192.168.0.50';
my $daddr = '192.168.0.2';
my $src = 1337;
my $dst = 80;

$packet->set({
ip => {
saddr => $saddr,
daddr => $daddr,
},
tcp => {
source => $src,
dest => $dst,
syn => 1,
seq => 31415
}
});
$packet->send(10);

Dieses Beispiel ist leicht gekürzt und verändert, sodass Skript-Kiddies damit wenig Freude haben werden. Es zeigt jedoch die grundsätzliche Vorgehensweise. Durch die verwendeten Low-Level Schnittstellen benötigt dieses Skript außerdem root-Rechte und passende Voraussetzungen. Dieser Proof-of-Concept Code ist nämlich zwar funktional, hat aber zwei Probleme:

  • Gespoofte IP-Adressen, wie sie hier verwendet werden, werden bei jedem vernünftigen Uplink-ISP (zum Glück!) von der Firewall verworfen.
  • Der reguläre TCP-Stack des vermeintlichen Absenders weiß nichts von dieser Verbindung (weil dieser ja umgangen wird, oder gar nicht beteiligt ist), so wird er auf die SYN+ACK-Antwort vom Zielrechner mit RST antworten, die Verbindung also zurücksetzen. Umgangen werden, kann das bestenfalls mit geeigneten ARP-Tabellen auf dem Zielrechner.

Führt man obigen Code in einer Schleife aus, wobei man entweder Source-IP (den Absender) oder den Source-Port mit jeder Iteration ändert und die angesprochenen Probleme umgehen kann, erzeugt dieses Script innerhalb von einer Sekunde mehrere Tausend Verbindungen. Alternativ kann man auch direkt auf rohe C-Sockets zurückgreifen.

Kernfunktionen und Strukturen mit C-Raw Sockets:

  • struct iphdr
  • struct tcphdr
  • socket(AF_INET, SOCK_RAW, IPPROTO_TCP)
  • setsockopt(sock, IPPROTO_IP, IP_HDRINCL, …)
  • sendto(…)

SYN-Flood Abwehr

Obiges Skript erzeugt (richtig eingesetzt) genau das typische SYN-Flood Verhalten. Eine Sicht auf den Netzwerkverkehr mit tcpdump zeigt das:

snowball:/home/arno# tcpdump -ni eth0
02:40:07.943862 IP 192.168.0.50.1337 > 192.168.0.2.www: S 31415:31415(0) win 65535
02:40:07.943910 IP 192.168.0.2.80 > 192.168.0.50.1337: S 2394706999:2394706999(0) ack 31416 win 32767 <mss 16396>

Nicht unterscheidbar von regulären Paketen erreicht den Daemon (hier HTTP auf Port 192.168.0.2:80) eine Verbindungsanfrage vom (gespooften) Klienten 192.168.0.50, zu erkennen am “SYN”-Flag, hier von tcpdump mit “S” dargestellt (Sequenznummer 31415). Regulär antwortet der TCP-Stack im Kernel dem Klienten, dass die Verbindung weiter hergestellt werden kann (SYN und ACK mit der Sequenznummer 31416). Gleichzeitig öffnet er einen Kanal dediziert für diesen Klienten, worüber jeder weitere Datenverkehr abgehandelt würde.

Überprüfen kann man diese offene Verbindung mit netstat:


snowball:/home/arno# netstat -ant | grep "192.168.0.50"
tcp 0 0 192.168.0.2:80 192.168.0.50:1337 SYN_RECV

Der Kernel hat also eine Verbindung geöffnet, diese auf den Status SYN_RECV (SYN erhalten) gesetzt, Ressourcen reserviert und wartet nun auf weitere Aktionen vom Klienten, die aber ausbleiben. Diesen Kanal hält der Kernel dennoch weiter offen, bis sie nach einer bestimmten Zeit inaktiv ausläuft (Timeout). Normalerweise sind dies 60 Sekunden, überprüfen und ändern kann man diesen Wert über “/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_syn_recv“. Schlimmer noch, standardmäßig versucht der TCP-Stack im Kernel fünf Mal erneut eine Verbindung mittels SYN+ACK herzustellen. Aber auch dieses Verhalten lässt sich regeln “/proc/sys/net/ipv4/tcp_synack_retries” ist hierfür zuständig.
Wie beim Klientskript erwähnt, ist dieses in der Lage innerhalb einer Sekunde mehrere Tausend Verbindungen auf diese Art und Weise zu erzeugen, während jede einzelne davon eine Minute lang Ressourcen blockiert und schon nach kurzer Zeit jede weitere Verbindung abweist.
So wird verständlich, dass SYN-Floods sehr effektiv sind, trifft man auf ein unvorbereitetes System.

Es gibt mehre verschiedene Möglichkeiten, SYN-Attacken beizukommen. Wie wirkungsvoll die einzelne Methode ist, hängt vom konkreten Einzelfall ab. Es ist jedoch auch möglich, eine oder mehrere Methoden zu kombinieren. Wie erwähnt werden dabei auch gern IP-Adressen gefälscht, daher macht es wenig Sinn eine spezifische IP-Adresse zu sperren. Der tatsächliche Absender lässt sich auf diesem Level unmöglich feststellen. Linux bietet jedoch die Möglichkeit IP-Adressen zu verifizieren. Das geschieht derart, dass die Rückroute überprüft wird. Der Sinn oder Unsinn dieser Methode ist bei SYN-Flood umstritten, sie hilft in erster Linie gefälschte Absenderadressen zu erkennen.
Aktiviert werden kann die “Reverse Path” Überprüfung gemäß RFC 1812 ebenfalls über /proc:


brain:/home/arno# echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
brain:/home/arno# echo 1 > /proc/sys/net/ipv4/conf/eth0/rp_filter

Deutlich nützlicher ist im konkreten Fall jedoch zweifellos das sogenannte SYN-Cookie. Dabei handelt es sich um eine Erweiterung im TCP-Stack, die theoretisch[tm] RFC-konform ist, also mit jedem Klienten kompatibel sein sollte. Im Unterschied zum normalen Verbindungsaufbau, verhält sich der Server jedoch intelligenter, da er keinerlei Informationen über eine halboffene Verbindung dauerhaft speichert. Vielmehr sind die halboffenen Verbindungen mit SYN-Cookies als Ringpuffer implementiert, die bei Bedarf jederzeit überschrieben werden können, wenn sich der Klient nicht mehr regulär meldet und die Verbindung validiert. Damit lösen SYN-Cookies das Problem der endlich vorhandenen Ressourcen, indem er alte, inaktive Verbindungsansätze verwirft, im Falle von SYN-Flood wahrscheinlich sehr viel schneller als nach einem regulären Timeout.

Möglich wird das, weil der TCP-Stack mit SYN-Cookies die Verbindungsinformationen aus der Sequenznummer der Antwort des Klienten ableitet. Um Verbindungen chronologisch richtig sortieren zu können, spezifiziert das TCP-Protokoll, dass jedes Paket über eine Sequenznummer (SEQ) verfügen muss. Die jeweilige Antwort darauf muss jene Sequenznummer um eins inkrementiert enthalten. SYN-Cookies machen sich diese Eigenschaft zu nutze, indem sie eine speziell codierte Nummer mit der SYN+ACK Antwort senden, die ein kryptografisches Geheimnis enthält. Ein korrekter Klient wird diese Nummer + 1 als Antwort zurücksenden. Daraus kann man legitime Verbindungen ohne Kenntnis der ursprünglichen Paketverlaufshistorie identifizieren.

Aktiviert werden SYN-Cookies erneut über /proc:

brain:/home/arno# echo 1 > /proc/sys/net/ipv4/tcp_syncookies

Laufen die Verbindungen über und der Kernel beginnt die Verbindungen zu rotieren, meldet er dies dem Administrator:

Nov 2 03:07:05 brain kernel: possible SYN flooding on port 80. Sending cookies.

Tatsächlich gibt es so eine festgelegte Anzahl an Verbindungen, die nach Bedarf immer wieder überschrieben werden:


brain:/home/arno# netstat -ant | grep "192.168.0.50" | wc -l
1024

Außreichend dimensionierte Hardware, sollte reine SYN-Angriffe damit effizient abwehren können. Hilft jedoch auch das nicht mehr, kann man als letzte Methode die Anzahl der Verbindungen pro Intervall regulieren. Die Idee dahinter ist, dass reguläre Verbindungen wesentlich resistenter gegen Ausfälle sind, als die “fire and forget” Ansätze von Floodskripten. So wird ein regulärer Klient, der eine Verbindung zum Server herstellen möchte, dies im Fehlerfall öfter probieren und (hoffentlich) bei einem dieser Versuche Erfolg haben. Linux zum Beispiel versucht standardmäßig fünf Mal eine Verbindung herzustellen (steuerbar über “/proc/sys/net/ipv4/tcp_syn_retries“). Klappt dies dann noch immer nicht, schlägt der Versuch als Kollateralschaden fehl. Das ist keine intelligente Dauerlösung wie das SYN-Cookie, kann aber während einer Angriffswelle zu einem sehr akzeptablen Kompromiss führen, wo alles andere bereits versagt hat.

Zur Umsetzung benötigt man einen Paketfilter, iptables unter Linux. Die Idee an folgender Chain ist, dass sämtliche SYN-Pakete, gut wie böse in eine spezielle Regelkette verschoben werden, die maximal 10 globale Verbindungsversuche pro Sekunde erlaubt, und jeden weiteren in jener Zeiteinheit abweist. Da dies auch reguläre Klienten betreffen kann, wird die Verbindung richtig beendet, nicht einfach verworfen.


brain:/home/arno# iptables -N synflood
brain:/home/arno# iptables -A synflood -m limit --limit 10/second --limit-burst 24 -j RETURN
brain:/home/arno# iptables -A synflood -j REJECT
brain:/home/arno# iptables -A INPUT -p tcp --syn -j synflood

Am Limit und Burst kann und sollte man je nach regulären Traffic des Servers drehen.

Kategorien

Eingeordnet unter: , und

Verwandte Artikel

Eine Antwort auf »SYN-Flood kurz erklärt«

  1. Hallo,

    ich möchte das gerne auf meinen eigenen Server mal testen. Wie erstelle ich z.B. eine Schleife, oder besser eine Endlos-Schleife?

    Webagentur - 17. November 2008 um 15:22

Einen Kommentar schreiben