
Correctif préservant le comportement pour l'injection d'en-tête HTTP CVE-2025-60876 dans BusyBox wget, avec preuve de concept, correctif d'encodage en pourcentage et artefacts de soumission en amont.
Un correctif préservant le comportement pour CVE-2025-60876, une vulnérabilité d'injection d'en-tête HTTP dans BusyBox wget, ainsi que la preuve de concept qui la démontre et les artefacts pour soumettre le correctif en amont à BusyBox.
BusyBox wget copie le chemin et la requête de l'URL textuellement dans la ligne de requête HTTP. Une URL contenant un CR brut (0x0D), un LF (0x0A) ou un autre byte de contrôle peut diviser la ligne de requête et injecter des en-têtes contrôlés par l'attaquant. Un espace (0x20) a le même effet : il brise le formatage METHOD SP request-target SP HTTP/1.1.
Affecté : BusyBox jusqu'à la version 1.37.0 (le pkgver livré sur chaque branche Alpine prise en charge). Signalé sur la liste de diffusion BusyBox en août 2025 ; CVE attribuée sous le numéro CVE-2025-60876 (NVD CVSS 6.5, Moyen).
Deux correctifs ont déjà été proposés en amont (Takeuchi Yuma, 2025-08 ; Radoslav Kolev, 2025-11). Tous deux une URL offensante et appellent . Cela stoppe l'injection, mais rejette également un espace simple, donc une URL comme qui fonctionnait auparavant génère maintenant une erreur. Cette régression est la raison pour laquelle le correctif n'a pas été repris en aval. Alpine retient son backport ().
bb_error_msg_and_diehttp://example.org/foo barCe correctif traite le chemin et l'hôte différemment, comme le font GNU wget et curl.
Chemin : percent_encode_target() dans networking/wget.c encode en pourcentage les bytes de contrôle (0x00 à 0x1f), l'espace (0x20) et DEL (0x7f) dans le request-target avant que la ligne de requête ne soit construite :
http://example.org/foo bar est envoyé comme /foo%20bar, comme le font GNU wget et curl. Pas de régression.% existant est laissé inchangé, donc un chemin déjà encodé n'est pas doublement encodé.Hôte : les mêmes bytes sont rejetés dans l'hôte de l'URL. Un nom d'hôte ne peut légitimement contenir des caractères de contrôle ou un espace, et l'encodage en pourcentage n'est pas défini pour le composant autorité. Cela a de l'importance en mode proxy : l'hôte est placé dans le request-target de forme absolue (GET http://hôte/chemin) et l'en-tête Host: mais n'est pas résolu localement, donc un CR ou LF brut dans l'hôte injecterait autrement. GNU wget (depuis CVE-2017-6508) et curl rejettent également les caractères de contrôle dans l'hôte.
La modification est confinée à networking/wget.c et ajoute une fonction d'assistance.
Hors périmètre : le canal de contrôle FTP (ftpcmd envoie target->path et l'utilisateur/mot de passe bruts, un TODO reconnu en amont) est une classe d'injection distincte, ne fait pas partie de CVE-2025-60876, et n'est pas traité ici. Il devrait être suivi séparément.
Compilé contre BusyBox 1.37.0 avec la même configuration qu'utilise Alpine (CONFIG_WERROR et CONFIG_TC désactivés). Un écouteur local capture les bytes exacts que wget envoie sur le fil.
| URL d'entrée | Vanilla 1.37.0 | Corrigé |
|---|---|---|
/x + CRLF + Evil: injected | Evil: injected arrive comme un véritable en-tête (vulnérable) | GET /x%0D%0AEvil:%20injected HTTP/1.1, aucun en-tête injecté |
/foo bar | envoie un espace littéral | GET /foo%20bar HTTP/1.1 (pas de régression) |
/foo%20bar (déjà encodé) | inchangé | GET /foo%20bar HTTP/1.1 (pas %2520) |
/normal | inchangé | GET /normal HTTP/1.1 (inchangé) |
proxy, host = h + CRLF + PInjected: 1 | PInjected: 1 arrive comme un véritable en-tête (vulnérable) | se termine avec bad character in URL host, rien envoyé |
proxy, host = example.test | normal | GET http://example.test/p HTTP/1.1 (inchangé) |
Capture complète : test/poc-output.txt. Les quatre tests testsuite/wget de BusyBox passent tous contre la compilation corrigée (test/testsuite-output.txt); les autres échecs de la suite complète notés là sont sans rapport (mount/taskset nécessitent root, et quelques bizarreries connues de conteneurs restreints). L'impact en taille sur aarch64 defconfig est de +185 octets (wget_main +142, .rodata +43, aucun symbole ajouté ou supprimé), mesuré avec scripts/bloat-o-meter et enregistré dans cover-letter.txt.
| Fichier | Description |
|---|---|
wget-cve-2025-60876.patch | Le correctif en tant que git format-patch contre BusyBox networking/wget.c, prêt à être envoyé |
commit-message.txt | Le message de commit (également l'en-tête du correctif) |
cover-letter.txt | La lettre d'accompagnement [PATCH v3] de la liste de diffusion qui relance le fil existant |
apply_fix.py | Générateur qui applique la modification à un networking/wget.c (utilisé pour produire le correctif) |
test/serve_once.py | Écouteur à connexion unique qui renvoie la requête HTTP brute |
test/driver.sh | Construit BusyBox vanilla et corrigé et exécute le PoC direct + proxy avant/après |
test/finalize.sh | Exécution complète : PoC avant/après, bloat-o-meter, testsuite/wget, et régénère le correctif |
test/poc-output.txt | Bytes de requête avant/après capturés (direct et proxy) |
test/testsuite-output.txt | Résultats de testsuite/wget de BusyBox contre la compilation corrigée |
test/finalize.sh s'exécute dans un conteneur alpine:edge avec Docker et reproduit tout (PoC, taille, suite de tests, correctif) :
docker run --rm -v "$PWD:/work" alpine:edge sh /work/test/finalize.sh
Il télécharge BusyBox 1.37.0, le compile en version vanilla (montrant l'injection directement et via un proxy), applique le correctif, recompile, rejoue les cas de requête ci-dessus, exécute bloat-o-meter et la suite de tests wget, et écrit le correctif. test/driver.sh est une variante plus légère uniquement PoC.
BusyBox est un projet basé sur une liste de diffusion. Le correctif est envoyé à [email protected] via git send-email, en tant que réponse [PATCH v3] sur le fil de Radoslav Kolev de novembre 2025, en créditant les rapporteurs et relecteurs précédents. Voir cover-letter.txt. Le correctif est sous GPLv2, la même licence que BusyBox.