jeudi 21 avril 2011

Weak Host Model

Cela fait plusieurs fois que je suis confronté dans mon travail à un comportement déroutant de la pile TCP/IP de Linux.

Prenons un exemple concret : nous avons une machine linux qui a deux interfaces réseaux et qui sert de routeur. Cette machine n'effectue aucun filtrage.

# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:19:b9:0d:a1:6d  
          inet addr:10.0.0.1  Bcast:10.255.255.255  Mask:255.0.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          Interrupt:16 

eth1      Link encap:Ethernet  HWaddr 00:0e:2e:ee:ca:87  
          inet addr:192.168.0.1  Bcast:192.168.0.255  Mask:255.255.2255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:127551 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1300 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:12749638 (12.7 MB)  TX bytes:154130 (154.1 KB)
          Interrupt:18 Base address:0xdc00 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source
# sysctl -e net.ipv4.ip_forward
net.ipv4.ip_forward = 1
Maintenant prenons une machine cliente connectée sur le même réseau que eth0 de notre routeur. La passerelle par défaut de notre machine cliente est notre routeur.

# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:17:42:2e:7d:77  
          inet adr:10.0.0.2  Bcast:10.255.255.255  Masque:255.0.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Packets reçus:900572 erreurs:0 :0 overruns:0 frame:0
          TX packets:296685 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          Octets reçus:98442936 (98.4 MB) Octets transmis:50927297 (50.9 MB)
          Interruption:16 

lo        Link encap:Boucle locale  
          inet adr:127.0.0.1  Masque:255.0.0.0
          adr inet6: ::1/128 Scope:Hôte
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          Packets reçus:624989 erreurs:0 :0 overruns:0 frame:0
          TX packets:624989 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:0 
          Octets reçus:41130183 (41.1 MB) Octets transmis:41130183 (41.1 MB)
# route -n
Table de routage IP du noyau
Destination     Passerelle      Genmask         Indic Metric Ref    Use Iface
10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 eth0
0.0.0.0         10.0.0.1        0.0.0.0         UG    0      0        0 eth0


Faisons quelques tests de connectivité sur la machine cliente pour voir ce qu'il se passe.

time0ut_client# scapy
Welcome to Scapy (2.1.0)
>>>  srp1(Ether()/IP(dst="10.0.0.1")/ICMP())
Begin emission:
...Finished to send 1 packets.
*
Received 4 packets, got 1 answers, remaining 0 packets
<Ether  dst=00:17:42:2e:7d:77 src=00:19:b9:0d:a1:6d type=0x800 |
<IP  version=4L ihl=5L tos=0x0 len=28 id=25698 flags= frag=0L ttl=64 proto=icmp chksum=0x27d src=10.0.0.1 dst=10.0.0.2 options=[] |
<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |
<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>
>>>  srp1(Ether()/IP(dst="192.168.0.1")/ICMP())
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<Ether  dst=00:17:42:2e:7d:77 src=00:19:b9:0d:a1:6d type=0x800 |
<IP  version=4L ihl=5L tos=0x0 len=28 id=25699 flags= frag=0L ttl=64 proto=icmp chksum=0x4bd3 src=192.168.0.1 dst=10.0.0.2 options=[] |
<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |
<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>

Comme on peut le voir un ping passe du client vers 10.0.0.1 et aussi vers 192.168.0.1. On remarque aussi que l'adresse MAC utilisée dans la réponse de 192.168.0.1 est bien celle de l'interface eth0 du routeur soit celle qui a l'adresse IP 10.0.0.1. Notre routeur fonctionne bien.

Maintenant désactivons le routage sur notre routeur.

# sysctl -w net.ipv4.ip_forward=0
net.ipv4.ip_forward = 0
Et refaisons le même test de connectivité.

time0ut_client# scapy
Welcome to Scapy (2.1.0)
>>>  srp1(Ether()/IP(dst="10.0.0.1")/ICMP())
Begin emission:
...Finished to send 1 packets.
*
Received 4 packets, got 1 answers, remaining 0 packets
<Ether  dst=00:17:42:2e:7d:77 src=00:19:b9:0d:a1:6d type=0x800 |
<IP  version=4L ihl=5L tos=0x0 len=28 id=25698 flags= frag=0L ttl=64 proto=icmp chksum=0x27d src=10.0.0.1 dst=10.0.0.2 options=[] |
<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |
<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>
>>>  srp1(Ether()/IP(dst="192.168.0.1")/ICMP())
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<Ether  dst=00:17:42:2e:7d:77 src=00:19:b9:0d:a1:6d type=0x800 |
<IP  version=4L ihl=5L tos=0x0 len=28 id=25699 flags= frag=0L ttl=64 proto=icmp chksum=0x4bd3 src=192.168.0.1 dst=10.0.0.2 options=[] |
<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |
<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>>

Même résultat, les deux adresses IP fonctionnent parfaitement. Bien que le routage ne soit pas activé, l'adresse 192.168.0.1 reste toujours accessible, chose qui peut paraître surprenante.

La raison pour cela est l'implémentation de la pile IPv4 (l'implémentation IPv6 est différente) de Linux qui fonctionne dans le mode Weak Host Model. Dans ce mode là, les adresses IP sont associées à la machine alors que dans le Strong Host Model, les adresses IP sont associées à l'interface (chez Solaris, ou encore chez microsoft à partir de windows Vista mais c'est configurable).
Dit autrement, ça donne que dans le fonctionnement Strong Host Model un paquet est autorisé si seulement sa destination correspond à l'adresse IP de l'interface sur laquelle il arrive, alors que dans le Weak Host Model un paquet est autorisé si sa destination correspond à au moins une adresse IP des interfaces de la machine.

Remarque: L'interface lo est gérée de façon totalement différente.

De ce fait, routage ou non l'adresse IP 192.168.0.1 reste toujours accessible.

Il faut donc faire très attention quand nous avons un service qui écoute sur une interface bien précise, comme par exemple un service d'administration. Prenons Apache par exemple et limitons son adresse d'écoute sur l'interface eth1.

...
Listen 192.168.0.1:80
...

Vérifions que c'est bien le cas :

# netstat -atn | grep 80
tcp        0      0 192.168.0.1:80          0.0.0.0:*               LISTEN
Apache n'écoute bien que sur notre adresse IP 192.168.0.1. Regardons que ce service est accessible malgrè que le routage soit désactivé :

time0ut_client# scapy
Welcome to Scapy (2.1.0)
>>>  sr1(IP(dst="192.168.0.1",src="10.0.0.2")/TCP(flags="S",dport=80))
Begin emission:
..Finished to send 1 packets.
*
Received 3 packets, got 1 answers, remaining 0 packets
<IP  version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=64 proto=tcp chksum=0x7021 src=192.168.0.1 dst=10.0.0.2 options=[] |
<TCP sport=www dport=ftp_data seq=3809183495L ack=1 dataofs=6L reserved=0L flags=SA window=5840 chksum=0x4c23 urgptr=0 options=[('MSS', 1460)] |
<Padding  load='\x00\x00' |>>>

On peut voir le résultat dans le flag de la réponse TCP ici c'est un SA pour Syn-Ack. Un service est donc en écoute sur notre port 80, même si le routage n'est pas actif.

# curl http://192.168.0.1
Administration Page

Moralité, si vous voulez protéger vos services d'administration, utilisez plutôt un bon filtrage.

3 commentaires:

  1. Très intéressante cette petite réflexion sur le weak host model. Faut toujours se méfier.

    RépondreSupprimer
  2. C'est très pratique pour faire de la découverte d'adresses quand on fait du mapping réseau distant à grands coups de traceroute...
    ;)

    RépondreSupprimer
  3. Ce qui légitime la mise en place de règles de FW ...

    RépondreSupprimer