
Correzione che preserva il comportamento per l'iniezione di header HTTP CVE-2025-60876 in BusyBox wget, con proof-of-concept, patch di codifica percentuale e artefatti di invio a monte.
Una correzione che preserva il comportamento per CVE-2025-60876, una vulnerabilità di iniezione di intestazioni HTTP in BusyBox wget, più la proof-of-concept che la dimostra e gli artefatti per inviare la correzione upstream a BusyBox.
BusyBox wget copia il percorso e la query dell'URL nella riga di richiesta HTTP così come sono. Un URL che contiene un CR (0x0D), un LF (0x0A) o un altro byte di controllo grezzo può spezzare la riga di richiesta e iniettare intestazioni controllate dall'attaccante. Anche uno spazio (0x20) ha lo stesso effetto: rompe la struttura METHOD SP request-target SP HTTP/1.1.
Versioni interessate: BusyBox fino alla 1.37.0 (la pkgver distribuita su tutti i branch Alpine supportati). Segnalata sulla mailing list di BusyBox nell'agosto 2025; CVE assegnato come CVE-2025-60876 (NVD CVSS 6.5, Medio).
Due patch erano già state proposte upstream (Takeuchi Yuma, 2025-08; Radoslav Kolev, 2025-11). Entrambe un URL offensivo e chiamano . Questo ferma l'iniezione, ma rifiuta anche un semplice spazio, quindi un URL come che prima funzionava ora genera un errore. È questa regressione il motivo per cui la correzione non è stata adottata downstream. Alpine sta sospendendo il suo backport a causa di questo ().
bb_error_msg_and_diehttp://example.org/foo barQuesta correzione gestisce il percorso e l'host in modo diverso, in linea con il comportamento di GNU wget e curl.
Percorso: percent_encode_target() in networking/wget.c applica il percent-encoding ai byte di controllo (da 0x00 a 0x1f), allo spazio (0x20) e a DEL (0x7f) nel request-target prima che venga costruita la riga di richiesta:
http://example.org/foo bar viene inviato come /foo%20bar, come fanno GNU wget e curl. Nessuna regressione.% esistente viene lasciato invariato, quindi un percorso già codificato non viene codificato due volte.Host: gli stessi byte vengono rifiutati nell'host dell'URL. Un nome host non può legittimamente contenere caratteri di controllo o uno spazio, e il percent-encoding non è definito per il componente authority. Questo è rilevante in modalità proxy: l'host viene inserito nel request-target in forma assoluta (GET http://host/path) e nell'intestazione Host:, ma non viene risolto localmente, quindi un CR o LF grezzo nell'host potrebbe altrimenti iniettare. Anche GNU wget (da CVE-2017-6508) e curl rifiutano i caratteri di controllo nell'host.
La modifica è limitata a networking/wget.c e aggiunge una funzione di supporto.
Fuori ambito: il canale di controllo FTP (ftpcmd invia target->path e utente/password grezzi, un TODO riconosciuto upstream) è una classe di iniezione separata, non fa parte di CVE-2025-60876 e non viene affrontato qui. Dovrebbe essere tracciato separatamente.
Compilato contro BusyBox 1.37.0 con la stessa configurazione usata da Alpine (CONFIG_WERROR e CONFIG_TC disattivati). Un listener locale cattura i byte esatti che wget invia sulla rete.
| URL di input | Vanilla 1.37.0 | Con patch |
|---|---|---|
/x + CRLF + Evil: injected | Evil: injected arriva come intestazione reale (vulnerabile) | GET /x%0D%0AEvil:%20injected HTTP/1.1, nessuna intestazione iniettata |
/foo bar | invia uno spazio letterale | GET /foo%20bar HTTP/1.1 (nessuna regressione) |
/foo%20bar (già codificato) | invariato | GET /foo%20bar HTTP/1.1 (non %2520) |
/normal | invariato | GET /normal HTTP/1.1 (invariato) |
proxy, host = h + CRLF + PInjected: 1 | PInjected: 1 arriva come intestazione reale (vulnerabile) | termina con bad character in URL host, non viene inviato nulla |
proxy, host = example.test | normale | GET http://example.test/p HTTP/1.1 (invariato) |
Cattura completa: test/poc-output.txt. I quattro test BusyBox testsuite/wget passano tutti con la build patchata (test/testsuite-output.txt); gli altri errori dell'intera suite indicati lì non sono correlati (mount/taskset richiedono root e ci sono alcune note peculiarità dei container ristretti). L'impatto sulle dimensioni con defconfig aarch64 è di +185 byte (wget_main +142, .rodata +43, nessun simbolo aggiunto o rimosso), misurato con scripts/bloat-o-meter e registrato in cover-letter.txt.
| File | Descrizione |
|---|---|
wget-cve-2025-60876.patch | La correzione come git format-patch per BusyBox networking/wget.c, pronta da inviare |
commit-message.txt | Il messaggio di commit (anche l'intestazione della patch) |
cover-letter.txt | La lettera di accompagnamento [PATCH v3] per la mailing list che riattiva il thread esistente |
apply_fix.py | Generatore che applica la modifica a un networking/wget.c (usato per produrre la patch) |
test/serve_once.py | Listener a connessione singola che restituisce la richiesta HTTP grezza |
test/driver.sh | Compila la BusyBox vanilla e quella patchata ed esegue il PoC prima/dopo, sia diretto che via proxy |
test/finalize.sh | Esecuzione completa: PoC prima/dopo, bloat-o-meter, testsuite/wget e rigenerazione della patch |
test/poc-output.txt | Byte delle richieste catturati prima/dopo (diretto e proxy) |
test/testsuite-output.txt | Risultati di BusyBox testsuite/wget con la build patchata |
test/finalize.sh viene eseguito all'interno di un container alpine:edge con Docker e riproduce tutto (PoC, dimensioni, testsuite, patch):
docker run --rm -v "$PWD:/work" alpine:edge sh /work/test/finalize.sh
Scarica BusyBox 1.37.0, la compila vanilla (mostrando l'iniezione sia direttamente che tramite proxy), applica la correzione, ricompila, riesegue i casi di richiesta sopra descritti, esegue bloat-o-meter e la testsuite di wget e scrive la patch. test/driver.sh è una variante più leggera, solo PoC.
BusyBox è un progetto basato su mailing list. La correzione viene inviata a [email protected] tramite git send-email, come risposta [PATCH v3] al thread di Radoslav Kolev del novembre 2025, dando credito ai precedenti segnalatori e revisori. Vedi cover-letter.txt. La correzione è GPLv2, la stessa licenza di BusyBox.