Mauvaise initialisation de la graine

En 1996, le navigateur web Netscape était encore un logiciel propriétaire. Ce qui n'a pas empêché Ian Goldberg et de David Wagner de décoder l'algorithme de génération de nombres pseudo-aléatoires (janvier 1996) utilisé dans la négociation des clés SSL (HTTPS). La graine était choisie à partir du numéro du processus, du numéro du processus parent et de l'heure actuelle (epoch UNIX). Or ces trois informations sont prédictibles et donc peu fiables.

Autres exemples :

Ne jamais écrire rand() % max

Kevin Amorin et Timothy Morgan ont relevé deux erreurs concernant la génération de nombres pseudo-aléatoires de ClamAV en auditant son code source (2005). La graine était réinitialisée à chaque génération de nombre à partir de gettimeofday() et clock(). Or ces deux informations sont prédictibles, en particulier pour un attaquant ayant un accès local à la machine. Ce n'est pas non plus une bonne idée de réinitialiser la graine à chaque fois.

De plus, ClamAV utilisait « rand() % max » bien que la page de manuel indique que les bits de poids faibles sont prédictibles. Lire l'article générateur congruentiel linéaire pour en comprendre la raison. Autre article intéressant : Pseudo Random Coins Show More Heads Than Tails (Juillet 2003) d'Heiko Bauke et de Stephan Mertens qui se conclue par « Do not produce pseudo random numbers using arithmetic in Z2 , use arithmetic in Zm instead, with m being a large prime ».

X11 et NFS

Histoire d'en rajoutant une couche, le papier Murphy's law and computer security (1996) de Wietse Venema rapporte des problèmes similaires.

Le protocole d'authentification Kerberos version 4 utilise un ticket délivré au client par le serveur. Ce ticket sert de sésame une fois l'authentification réussie. Or le serveur Kerberos initialisait mal la graine du générateur de nombres pseudo-aléatoires. Il utilisait l'heure (gettimeofday, epoch UNIX), le numéro de processus (getpid) et l'identifiant (gethostid) du serveur, ainsi qu'un simple compteur (s'incrémente à chaque génération).

De manière similaire, un serveur NFS génère un ticket pour un client authentifié. Mais le serveur n'utilisait que le numéro de processus du serveur et l'heure (epoch UNIX) pour initialiser la graine. Pire encore, les premières implémentations NFS de SunOS n'initialisaient même pas la variable de l'heure !

L'article détaille également la génération de ticket X11, mais celle-ci semble conforme.

Numéro de séquence TCP

Le protocole réseau TCP date de 1981, et depuis sa création sa sécurité est régulièrement remise en cause. Un des gros problèmes de TCP est la prédiction du numéro de séquence : c'est un peu la clé qui empêche l'injection de paquet ou de pouvoir couper une connexion. Il faut donc que ce numéro soit difficile à prédire... Ce qui était loin d'être le cas en 2001 comme en atteste les rapports d'Xforce (TCP sequence prediction) et du CERT (Statistical Weaknesses in TCP/IP Initial Sequence Numbers).

Le bulletin de FreeBSD (TCP Sequence Number Vulnerability) (octobre 2000) est fort instructif : le patch présenté montre qu'il suffit de changer deux instructions pour utiliser arc4random() au lieu de random() ! On en comprend que la fonction random() n'est pas sûre, alors que arc4random() semble plus robuste face aux attaques.

Recommandations de l'IETF sur la sécurité :

OpenBSD utilise des variables aléatoires pour le numéro de session, mais également pour le champ « timestamp ». Ce qui assure une meilleure sécurité, mais peut poser des problèmes de compatibilité. Enfin, l'outil nmap permet d'estimer la Prédictabilité de Séquence TCP avec l'option -O (détection du système d'exploitation). Exemple avec une Freebox v5 :

$ nmap -v -O 192.168.0.254
(...)
TCP Sequence Prediction: Difficulty=0 (Trivial joke)

Gare aux fuites d'information

Les algorithmes des générateurs de nombre pseudo-aléatoires sont connus. Si on arrive à en connaître l'état interne, on peut prédire les prochains nombres générés, voir même retrouver les nombres précédents.

Justement, une faille a été trouvée dans une implémentation de SSH2 : LSH leaks file descriptors of the random generator seed file to user shells. Le générateur était directement accessible pour l'utilisateur !

De même, FreeBSD a aussi eu son bug il y a quelques jours : FreeBSD sys_dev_random Random Data Replay Vulnerability (CVE-2007-6150, 30 novembre 2007). La vulnérabilité peut être utilisée pour déterminer des fragments de données aléatoires précédemment générées par le périphérique /dev/urandom.

Conclusion

Si la sécurité de votre application est critique, n'utilisez pas ni l'heure, ni le numéro de processus pour initialiser votre graine, mais un valeur générée par du matériel (utilisez /dev/random sous Linux). De plus, n'utilisez pas la fonction random() qui est insuffisante mais une fonction de qualité cryptographique qui a été testée par plusieurs personnes de confiance.