
Verhaltensbewahrender Fix für CVE-2025-60876 HTTP-Header-Injection in BusyBox wget, mit Proof-of-Concept, Percent-Encoding-Patch und Upstream-Einreichungsartefakten.
Eine verhaltensbewahrende Korrektur für CVE-2025-60876, eine HTTP-Header-Injection-Schwachstelle in BusyBox wget, sowie der Proof-of-Concept, der sie demonstriert, und die Artefakte zur Einreichung der Korrektur bei BusyBox.
BusyBox wget kopiert den URL-Pfad und die Query unverändert in die HTTP-Anfragezeile. Eine URL, die ein rohes CR (0x0D), LF (0x0A) oder ein anderes Steuerbyte enthält, kann die Anfragezeile spalten und vom Angreifer kontrollierte Header injizieren. Ein Leerzeichen (0x20) hat den gleichen Effekt: Es unterbricht die METHOD SP request-target SP HTTP/1.1-Rahmung.
Betroffen: BusyBox bis einschließlich 1.37.0 (die pkgver, die auf jedem unterstützten Alpine-Zweig ausgeliefert wird). Gemeldet auf der BusyBox-Mailingliste im August 2025; CVE zugewiesen als CVE-2025-60876 (NVD CVSS 6.5, Mittel).
Bereits vorgeschlagen wurden zwei Patches (Takeuchi Yuma, 2025-08; Radoslav Kolev, 2025-11). Beide weisen eine anstößige URL zurück und rufen bb_error_msg_and_die auf. Das stoppt die Injection, lehnt aber auch ein einfaches Leerzeichen ab, sodass eine URL wie http://example.org/foo bar, die bisher funktionierte, nun einen Fehler verursacht. Diese Regression ist der Grund, warum die Korrektur nicht von Downstream übernommen wurde. Alpine hält den Backport zurück (Arbeitspaket 17872).
Diese Korrektur behandelt Pfad und Host unterschiedlich, entsprechend dem Verhalten von GNU wget und curl.
Pfad: percent_encode_target() in networking/wget.c kodiert Steuerbytes (0x00 bis 0x1f), Leerzeichen (0x20) und DEL (0x7f) im request-target percent-encodiert, bevor die Anfragezeile erstellt wird:
http://example.org/foo bar wird als /foo%20bar gesendet, wie bei GNU wget und curl. Keine Regression.% bleibt unverändert, sodass ein bereits kodierter Pfad nicht doppelt kodiert wird.Host: dieselben Bytes werden im URL-Host zurückgewiesen. Ein Hostname kann legitimerweise keine Steuerzeichen oder ein Leerzeichen enthalten, und die Prozentkodierung ist für die Authority-Komponente nicht definiert. Dies ist im Proxy-Modus von Bedeutung: Der Host wird im request-target der absoluten Form (GET http://host/pfad) und im Host:-Header platziert, aber nicht lokal aufgelöst, sodass ein rohes CR oder LF im Host andernfalls injizieren würde. GNU wget (seit CVE-2017-6508) und curl weisen ebenfalls Steuerzeichen im Host zurück.
Die Änderung beschränkt sich auf networking/wget.c und fügt eine Hilfsfunktion hinzu.
Außerhalb des Umfangs: Der FTP-Kontrollkanal (ftpcmd sendet target->path sowie Benutzer/Passwort roh, ein von den Upstream-Entwicklern anerkanntes TODO) ist eine separate Injection-Klasse, nicht Teil von CVE-2025-60876, und wird hier nicht behandelt. Es sollte separat verfolgt werden.
Erstellt gegen BusyBox 1.37.0 mit derselben Konfiguration, die Alpine verwendet (CONFIG_WERROR und CONFIG_TC aus). Ein lokaler Lauscher erfasst die genauen Bytes, die wget auf die Leitung legt.
Voller Mitschnitt: test/poc-output.txt. Die vier BusyBox testsuite/wget-Tests bestehen alle gegen den gepatchten Build (test/testsuite-output.txt); die dort vermerkten anderen Fehlschläge der gesamten Testsuite sind nicht verwandt (mount/taskset benötigen root und ein paar bekannte Eigenheiten eingeschränkter Container). Der Größenaufwuchs auf aarch64 defconfig beträgt +185 Bytes (wget_main +142, .rodata +43, keine Symbole hinzugefügt oder entfernt), gemessen mit scripts/bloat-o-meter und dokumentiert in cover-letter.txt.
test/finalize.sh läuft innerhalb eines alpine:edge-Containers mit Docker und reproduziert alles (PoC, Größe, Testsuite, Patch):
docker run --rm -v "$PWD:/work" alpine:edge sh /work/test/finalize.sh
Es lädt BusyBox 1.37.0 herunter, erstellt es als Vanilla (zeigt die Injection direkt und über einen Proxy), wendet den Fix an, erstellt neu, spielt die obigen Anfragefälle erneut ab, führt bloat-o-meter und die wget-Testsuite aus und schreibt den Patch. test/driver.sh ist eine leichtere, nur auf den PoC beschränkte Variante.
BusyBox ist ein Mailinglisten-Projekt. Der Fix geht per git send-email an [email protected], als [PATCH v3]-Antwort auf Radoslav Kolevs Thread vom November 2025, unter Nennung der vorherigen Melder und Prüfer. Siehe cover-letter.txt. Der Fix steht unter GPLv2, derselben Lizenz wie BusyBox.
| Input URL | Vanilla 1.37.0 | Gepatcht |
|---|
/x + CRLF + Evil: injected | Evil: injected kommt als echter Header an (anfällig) | GET /x%0D%0AEvil:%20injected HTTP/1.1, kein injizierter Header |
/foo bar | sendet ein wörtliches Leerzeichen | GET /foo%20bar HTTP/1.1 (keine Regression) |
/foo%20bar (bereits kodiert) | unverändert | GET /foo%20bar HTTP/1.1 (nicht %2520) |
/normal | unverändert | GET /normal HTTP/1.1 (unverändert) |
proxy, host = h + CRLF + PInjected: 1 | PInjected: 1 kommt als echter Header an (anfällig) | bricht mit bad character in URL host ab, nichts gesendet |
proxy, host = example.test | normal | GET http://example.test/p HTTP/1.1 (unverändert) |
| Datei | Beschreibung |
|---|
wget-cve-2025-60876.patch | Der Fix als git format-patch gegen BusyBox networking/wget.c, versandbereit |
commit-message.txt | Die Commit-Nachricht (auch der Patch-Header) |
cover-letter.txt | Das [PATCH v3]-Anschreiben für die Mailingliste, das den bestehenden Thread wiederbelebt |
apply_fix.py | Generator, der die Änderung auf networking/wget.c anwendet (zur Erstellung des Patches) |
test/serve_once.py | Einzelverbindungs-Lauscher, der die rohe HTTP-Anfrage zurücksendet |
test/driver.sh | Erstellt die Vanilla- und die gepatchte BusyBox und führt den direkten + Proxy-vorher/nachher-PoC aus |
test/finalize.sh | Vollständiger Durchlauf: Vorher/Nachher-PoC, bloat-o-meter, testsuite/wget und Neuerstellung des Patches |
test/poc-output.txt | Erfasste Vorher/Nachher-Anfragebytes (direkt und Proxy) |
test/testsuite-output.txt | BusyBox testsuite/wget-Ergebnisse gegen den gepatchten Build |