Der Linux Kernel hat Einstellungen für Netzwerk Schnittstellen die es
erlauben, das lokale Netz (127.0.0.0/8 für IP Version 4, auch oft
Loopback Adresse bezeichnet weil sie typischerweise der lo
Loopback
Schnittstelle zugewiesen ist) zu routen. Diese Einstellung kann man über
die Datei
Was kann man mit dieser Einstellung tun?
Im Folgenden verwende ich Beispiele für die IP Version 4, das gleiche
kann man aber für die IP Version 6 local host Adresse ::1 erreichen.
Im Linux Netzwerk-Stack werden IP Pakete die entweder von einer Adresse
aus dem lokalen Netz 127.0.0.0/8
kommen oder eine Zieladresse dort
haben verworfen, wenn sie über eine Schnittstelle kommen die nicht für diesen
Adressbereich konfiguriert ist. Wir verifizieren das im Folgenden zuerst
mal.
Für einen einfachen UDP Server in Python der UDP Pakete empfängt und
deren IP Adresse und Port (und Länge) ausdruckt können wir das folgende
(gekürzte) Script verwenden:
import socket
[...]
sock = socket.socket (socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
sock.bind ((args.address, args.port))
while True:
buf, adr = sock.recvfrom (1024)
l = len (buf)
print ('Received %(l)s bytes from %(adr)s' % locals ())
Alle Werte in der Variable args
kommen von der Kommandozeile mit der
Klasse ArgumentParser
. Die Adresse auf die gebunden wird (bind) ist
im folgenden standardmässig 0.0.0.0
was bedeutet dass Pakete von
allen Netzwerk-Schnittstellen empfangen werden.
Wir testen das erstmal mit einem einfachen Client-Programm. Im folgenden
haben wir zwei Maschinen, .9 sendet und .5 empfängt. Zum Versenden von
UDP Paketen können wir den folgenden einfachen Code verwenden:
import socket
sock = socket.socket (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.sendto (b'This is a test', (args.address, args.port))
Auch hier werden die Parameter auf der Kommandozeile angegeben. Wenn wir von
.9 an .5 senden bekommen wir vom obigen Server
Received 14 bytes from ('XXXXXXX.9', 38232)
wobei der der Quell-Port 38232 eine zufällige vom Linux Kernel erzeugte
Portnummer ist und XXXXXXX
die oberen Bytes der IP-Adresse
des Senders repräsentiert. Wenn wir auf der empfangenden Maschine an
127.0.0.1
senden bekommen wir:
Received 14 bytes from ('127.0.0.1', 58196)
Wir sehen dass der Server sowohl auf der Ethernet Schnittstelle als auch
auf der Loopback Schnittstelle empfängt.
Soweit nichts neues. Schauen wir mal was passiert wenn wir Adressen
fälschen. Um beliebige IP Pakete (oder sogar Nicht-IP Pakete) zu
erzeugen verwende ich im Folgenden das Python Paket Scapy.
Ein Ausschnitt aus dem Script:
p = Ether (dst = args.mac_address, src = args.mac_address) \
/ IP (dst = args.address, src = args.source_address) \
/ UDP (dport = args.port, sport = args.source_port) \
/ Raw (b'This is a test')
sendp (p, iface = args.interface)
Das baut ein Ethernet Paket (Ether)
mit IP
Inhalt. Das IP Paket
wiederum enthält ein UDP
Paket was wiederum einen Raw
Zeichenfolge enthält. Das Paket wird über einen Netzwerk Layer 2 Socket
versendet. Wiederum kommen die Parameter von der Kommandozeile.
Standard für alle Ports ist 22222
, die Quell-IP ist XXXXXXX.9
und die Ziel-IP ist XXXXXXX.5
. Ziel- und Quell MAC Adresse werden
beide auf die MAC-Adresse der Zielmaschine gesetzt. Wir wiederholen
was wir mit dem einfachen Client ausprobiert haben:
sudo scapyclient.py
Was in der folgenden Ausgabe resultiert:
Received 14 bytes from ('XXXXXXX.9', 22222)
Hier wird standardmässig immer Port 22222 verwendet es sei denn wir
setzen den Port über eine Kommandozeilen-Option des Scripts. Wir müssen
sudo
verwenden weil nur der Superuser beliebige Pakete auf Layer-2
des Netzwerk-Stacks senden darf.
Was passiert nun wenn wir Pakete fälschen? Damit wir sehen was gesendet
(und empfangen) wird starten wir tcpdump
wie folgt:
sudo tcpdump -n -i eth0 udp port 22222
Natürlich muss das Argument der -i
Option geändert werden wenn die
Netzwerk-Schnittstelle anders heisst als eth0
. Die Option -n
sagt
tcpdump
dass keine DNS-Auflösung von IP Adressen erfolgen soll.
Wir senden ein Paket mit einer localhost Adresse als Quell-Adresse:
sudo scapyclient.py --source-address=127.0.0.1
Auf beiden Maschinen sehen wir in der tcpdump
Ausgabe
TT:TT:TT.TTTTTT IP 127.0.0.1.22222 > 10.23.5.5.22222: UDP, length 14
wobei TT:TT:TT.TTTTTT
ein Zeitstempel ist. Das Paket geht also auf
der Ethernet-Schittstelle der sendenden Maschine raus und wird von der
empfangenden Maschine empfangen (weil die MAC-Adresse richtig ist) aber
von unserem Server wird nichts ausgegeben.
Das gleich passiert wenn wir die Zieladresse ändern:
sudo scapyclient.py --address=127.0.0.1
Die tcpdump
Ausgabe ist jetzt:
TT:TT:TT.TTTTTT IP 10.23.5.9.22222 > 127.0.0.1.22222: UDP, length 14
Wieder wird das von beiden Maschinen von tcpdump
ausgegeben aber
nichts wird von unserem Server ausgegeben.
Schauen wir nun was passiert wenn wir route_localnet
für unsere
Ethernet Schnittstelle einschalten (das folgende funktioniert natürlich
nur als root
):
echo 1 > /proc/sys/net/ipv4/conf/eth0/route_localnet
Wir fälschen wieder die Quelladresse:
sudo python3.11 scapyclient.py --source-address=127.0.0.1
Wieder wird das Paket von beiden laufenden tcpdump
Prozessen
ausgegeben aber wieder nicht von unserem Server. Aber wenn wir die
Zieladresse fälschen:
sudo python3.11 scapyclient.py --address=127.0.0.1
Bekommen wir zusätzlich zur tcpdump
Ausgabe auch eine Antwort von
unserem Server:
Received 14 bytes from ('10.23.5.9', 22222)
Und bemerkenswert ist, dass das auch funktioniert wenn wir unserem
Server sagen dass er nur auf der localhost Adresse hören soll:
python3 server.py --address=127.0.0.1
Der Server empfängt trotzdem das gefälschte Paket obwohl es nicht über
die Loopback Schnittstelle empfangen wurde:
Received 14 bytes from ('10.23.5.9', 22222)