Récemment j’ai expliqué comment j’en étais arrivé à développer Sanca , un scanner de vulnérabilités écrit en Rust. Après une mise à jour des dépendances, Sanca s’est mis à planter. Pas juste un crash, un SIGSEGV (segmentation violation, aussi appelé segmentation fault), une erreur mémoire qui fait qu’il a été tué par le système d’exploitation. Rust est censé empêcher ce genre d’erreurs.
Le problème ne survenait que sur OpenBSD, pas sur Linux. OpenBSD a des règles
de sécurité plus strictes en ce qui concerne la mémoire (par exemple W^X), donc
c’est cohérent.
J’utilise Sanca quotidiennement sur OpenBSD, il fallait donc que je corrige ça.
À la recherche du responsable

L’image ci-dessus montre l’erreur qui se produisait lorsque j’exécutais Sanca,
on ne peut pas dire que ce soit détaillé.
Heureusement, OpenBSD sauvegarde l’état de la mémoire au moment du crash dans
un fichier .core, permettant d’analyser la mémoire, les registres, tout ce
qu’il faut pour identifier quelle partie du code fait planter le programme.
J’ai ouvert sanca_software.core dans gdb (GNU Debugger), qui m’a rapidement mis sur une piste.

À peine ouvert, GDB m’a indiqué qu’il y avait un problème avec aws-lc-sys, le wrapper Rust autour de la librairie aws-lc, écrite en C. En faisant quelques recherches j’ai découvert que aws-lc était une librairie cryptographique basée sur BoringSSL, un fork de OpenSSL. Le problème semblait donc lié à SSL / TLS, mais je voulais être sûr de l’origine du problème.
Sanca ne dépend pas directement de cette librairie, alors j’ai utilisé la
commande cargo tree -i aws-lc-sys pour découvrir quelle dépendance de Sanca
utilisait aws-lc-sys. Verdict, il s’agissait de reqwest.
Sanca utilise une librairie appelée “reqwest” pour envoyer des requêtes HTTP aux serveurs Web, reqwest utilise “rustls” pour gérer le protocole TLS, et rustls utilise “aws-lc-rs”, qui est un wrapper autour de “aws-lc-sys”, pour gérer la partie cryptographie.
Recherche et mise en place de la solution
Après avoir trouvé la source du problème, il me fallait une solution.
En faisant des recherches sur rustls, j’ai remarqué que la librairie
proposait plusieurs “providers” de cryptographie, aws-lc était le nouveau
provider par défaut, l’ancien était ring. Mais il était toujours possible
d’installer rustls avec ring au lieu d’aws-lc, grâce à une fonctionnalité de
Rust / Cargo appelée feature flags. Il restait à découvrir si reqwest était
suffisamment flexible pour me laisser choisir le provider de rustls.
Heureusement, oui. Encore grâce aux feature flags, j’ai pu
installer reqwest en lui précisant de n’installer aucun provider pour rustls,
et en parallèle j’ai installé et initialisé moi-même rustls avec ring comme
provider. Cette fois ça fonctionnait, je pouvais compiler et utiliser Sanca
sans problème.
J’aurais pu m’en contenter, mais ça m’embêtait de juste retirer aws-lc de Sanca
alors que c’était le choix par défaut de rustls, il devait bien y avoir une
raison.
J’ai décidé d’utiliser moi aussi les feature flags dans Sanca, pour permettre à l’utilisateur d’utiliser soit aws-lc soit ring, au choix. Aujourd’hui il y a deux façons de compiler Sanca :
cargo build --release
pour compiler avec aws-lc, ce qui convient probablement à la plupart des utilisateurs FreeBSD, Linux, macOS et Windows. Ou
cargo build --release --no-default-features --features ring-provider
pour compiler Sanca avec ring, ce qui convient mieux aux utilisateurs OpenBSD
pour le moment.
J’ai commenté la différence dans le fichier README, pour que chaque utilisateur
puisse choisir la méthode qui lui convient.
Ce que je retiens
Les développements modernes dépendent beaucoup de librairies externes, et ce quel que soit le langage. Si JavaScript avec npm est le champion des supply chain attacks, ces compromissions de librairies externes qui impactent les projets qui s’en servent, Rust avec Cargo n’est pas à l’abri de problèmes non plus.
Ici ce n’est pas une attaque, c’est un simple bug. Mais un bug dans une dépendance affecte les projets qui s’en servent. Il faut savoir déboguer et trouver des solutions adaptées, qui fonctionnent aujourd’hui sans handicaper le projet demain. La librairie aws-lc supporte des algorithmes “post-quantiques”, ce qui n’est pas encore le cas de ring, donc ne pas faire de choix bloquant était important.
En Rust on retrouve souvent des dépendances à des librairies écrites en C. Ça
s’applique également à d’autres langages comme Python ou PHP, mais Rust
fait une promesse forte, celle que les accès mémoires sont sûrs. Ce n’est pas
son seul avantage, mais c’est un point différenciant très important.
Le problème c’est qu’aujourd’hui beaucoup de programmes importants sont écrits en
C ou en C++, que ce soient des librairies cryptographiques comme LibreSSL, OpenSSL
ou BoringSSL, ou des systèmes d’exploitation comme OpenBSD, Linux, macOS ou
Windows. C est un super langage, il tourne sur toutes les architectures et
compile très rapidement, mais ses accès mémoires ne sont pas sûrs. Encore
aujourd’hui, énormément de vulnérabilités sont liées à la gestion de la mémoire.
Aussi longtemps que Rust dépendra de librairies écrites en C, ce qui n’est pas prêt de changer puisque C est universel, on sera confrontés à des problèmes de mémoire. Une librairie cryptographique 100% écrite Rust est en cours de développement, mais il se passera plusieurs années avant qu’elle soit aussi complète et auditée que peuvent l’être OpenSSL ou aws-lc.
Une dépendance qui plante, c’est une vulnérabilité de plus dans votre chaîne
d’approvisionnement.
Si vous voulez auditer vos propres outils ou migrer vers une stack plus
maîtrisée, parlons-en
.