SwordArMor

Mettre en œuvre BCP38 avec un routeur FreeBSD en utilisant pf

BCP38 (pour Best Current Practice numéro 38) est un document de l’IETF qui présente les bonnes pratiques en matière de filtrage de trafic, notamment vis à vis de l’IP source. En effet, par défaut un routeur ne regarde que les IPs de destinations afin de savoir vers où envoyer le paquet, sans se soucier de savoir si l’IP source fait partie des blocs d’IP qu’il route ou pas.
Ceci a pour effet de pouvoir usurper l’IP source et de pouvoir mener des attaques sans révéler sa propre IP. Cela participe également aux attaques par amplification.
Exemple :

 # scapy
>>> p=IP(dst="86.229.168.245", src="89.234.141.6")/ICMP()
>>> send(p)
WARNING: No broadcast address found for iface eth0

.
Sent 1 packets.

root@drscott:~# tcpdump -i eth0.832 host 89.234.141.6 and '(icmp or icmp6)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.832, link-type EN10MB (Ethernet), capture size 262144 bytes
22:45:17.556468 IP 89.234.141.6 > LFbn-1-3662-245.w86-229.abo.wanadoo.fr: ICMP echo request, id 0, seq 0, length 8
22:45:17.556632 IP LFbn-1-3662-245.w86-229.abo.wanadoo.fr > 89.234.141.6: ICMP echo reply, id 0, seq 0, length 8
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel
Je viens donc de faire sortir par l’AS204092 une IP de l’AS60630. Ce qui n’aurait jamais dû arriver en temps normal, étant donné que nous ne routons pas les mêmes blocs d’IP. Notons que ça fonctionne aussi bien en IPv6.

Packet Filter (ou pf) possède un module qui vérifie que le paquet envoyé aurait bien dû prendre cette route s’il avait été reçu. Si ce n’est pas le cas, le paquet est bloqué. Attention, cela veut dire que ça ne marche que dans le cas de routage symétrique ; dès que le routage est asymétrique, les paquets seront bloqués même s’ils sont légitimes. Ce module se nomme urpf-failed pour Unicast Reverse Path Forwarding.

Prenons un exemple, j’ai un routeur avec d’un côté internet (sur em0), de l’autre mon réseau (sur em1).
Sur em0, le routage n’est pas forcément symétrique en raison de BGP. Mon AS n’a pas forcément les même préférences que celui de celui à qui je cause. Par exemple chez grifon, nous allons voir orange par cogent mais ils nous répondent par interoute.
Par contre, em1 ne contient qu’une seule route, celle de mon réseau local. C’est donc sur cette interface qu’il faut activer la vérification uRPF. Je vous conseille de toutes façons de préférer appliquer les règles de filtrage sur vos interfaces internes plutôt que vos interfaces externes. Si vous vous trompez, vous pourrez au moins reprendre la main sur vos équipement via les IPs d’interco (pour peu qu’elles soient annoncées en BGP).
Si l’on suit la doc de pf, il faut donc ajouter

block in quick on em1 from urpf-failed label uRPF
dans /etc/pf.conf et recharger la configuration de pf avec service pf reload.
Si nous reprenons notre exemple plus haut, ça donne ça :
morvan ~ # scapy 
INFO: Can't import python gnuplot wrapper . Won't be able to plot.
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
Welcome to Scapy (2.2.0)
>>> p=IP(dst="86.229.168.245", src="20.23.31.25")/ICMP()
>>> send(p)
WARNING: No broadcast address found for iface eth0

.
Sent 1 packets.
>>> 


root@nominoe:~ # tcpdump -i em1 host rennes.swordarmor.fr and '(icmp or icmp6)' 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on em1, link-type EN10MB (Ethernet), capture size 65535 bytes
18:14:11.730868 IP 20.23.31.25 > LFbn-1-3662-245.w86-229.abo.wanadoo.fr: ICMP echo request, id 0, seq 0, length 8
^C
root@nominoe:~ # tcpdump -i em0 host rennes.swordarmor.fr and '(icmp or icmp6)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on em0.20, link-type EN10MB (Ethernet), capture size 65535 bytes
^C
0 packets captured
8393 packets received by filter
0 packets dropped by kernel
Nous voyons donc bien que le paquet arrive sur l’interface interne, mais n’est pas transférée sur l’interface externe, et n’est donc pas envoyé sur Internet.

De cette façon, tout paquet ayant une IP source qui n’est pas accessible par votre interface interne — et qui ne fait donc pas partie de votre réseau — ne sera pas envoyé sur Internet. Et c’est bien le but de BCP38, n’autoriser que ses IPs à sortir depuis son réseau.
L’équivalent de cette fonctionnalité avec Netfilter s’appelle rpfilter. Vous pouvez trouver un exemple à ce propos sur la la doc d’ARN.