
Исправление, сохраняющее поведение, для CVE-2025-60876 — инъекции HTTP-заголовков в BusyBox wget, с доказательством концепции, патчем процентного кодирования и артефактами отправки в апстрим.
Исправление без изменения поведения для CVE-2025-60876, уязвимости внедрения HTTP-заголовков в BusyBox wget, а также proof-of-concept, демонстрирующий её, и артефакты для отправки исправления в апстрим BusyBox.
BusyBox wget копирует путь и query URL в строку HTTP-запроса дословно. URL, содержащий сырой CR (0x0D), LF (0x0A) или другой управляющий байт, может разорвать строку запроса и внедрить управляемые атакующим заголовки. Пробел (0x20) даёт тот же эффект: он ломает структуру METHOD SP request-target SP HTTP/1.1.
Затронуто: BusyBox до 1.37.0 включительно (pkgver, поставляемый во всех поддерживаемых ветках Alpine). Сообщено в списке рассылки BusyBox в августе 2025 года; присвоен CVE-2025-60876 (NVD CVSS 6.5, средний).
Уже были предложены два патча в апстриме (Takeuchi Yuma, 2025-08; Radoslav Kolev, 2025-11). Оба отклоняют проблемный URL и вызывают bb_error_msg_and_die. Это останавливает инъекцию, но также отклоняет обычный пробел, поэтому URL вида , который раньше работал, теперь вызывает ошибку. Именно из-за этой регрессии исправление не было принято downstream. Alpine приостановила свой бэкпорт из-за этого ().
http://example.org/foo barЭто исправление по-разному обрабатывает путь и хост, как это делают GNU wget и curl.
Путь: функция percent_encode_target() в networking/wget.c процентно-кодирует управляющие байты (от 0x00 до 0x1f), пробел (0x20) и DEL (0x7f) в request-target перед построением строки запроса:
http://example.org/foo bar отправляется как /foo%20bar, как в GNU wget и curl. Регрессии нет.% остаётся без изменений, поэтому уже закодированный путь не кодируется повторно.Хост: те же байты отклоняются в хосте URL. Имя хоста не может легально содержать управляющие символы или пробел, а процентное кодирование не определено для компонента authority. Это важно в режиме прокси: хост помещается в request-target в абсолютной форме (GET http://host/path) и в заголовок Host:, но локально не разрешается, поэтому сырые CR или LF в хосте иначе позволили бы провести инъекцию. GNU wget (начиная с CVE-2017-6508) и curl также отклоняют управляющие символы в хосте.
Изменение ограничено файлом networking/wget.c и добавляет одну вспомогательную функцию.
Вне области действия: управляющий канал FTP (ftpcmd отправляет target->path, а также пользователя и пароль в сыром виде — это признанный апстримом TODO) относится к отдельному классу инъекций, не является частью CVE-2025-60876 и здесь не рассматривается. Его следует отслеживать отдельно.
Собрано с BusyBox 1.37.0 с той же конфигурацией, которую использует Alpine (CONFIG_WERROR и CONFIG_TC выключены). Локальный слушатель перехватывает точные байты, которые wget отправляет по сети.
| Входной URL | Исходный 1.37.0 | С патчем |
|---|---|---|
/x + CRLF + Evil: injected | Evil: injected приходит как настоящий заголовок (уязвимо) | GET /x%0D%0AEvil:%20injected HTTP/1.1, инъекции заголовка нет |
/foo bar | отправляет обычный пробел | GET /foo%20bar HTTP/1.1 (без регрессии) |
/foo%20bar (уже закодирован) | без изменений | GET /foo%20bar HTTP/1.1 (не %2520) |
/normal | без изменений | GET /normal HTTP/1.1 (без изменений) |
прокси, хост = h + CRLF + PInjected: 1 | PInjected: 1 приходит как настоящий заголовок (уязвимо) | завершается с ошибкой bad character in URL host, ничего не отправлено |
прокси, хост = example.test | обычный запрос | GET http://example.test/p HTTP/1.1 (без изменений) |
Полный захват: test/poc-output.txt. Все четыре теста BusyBox testsuite/wget проходят при сборке с патчем (test/testsuite-output.txt); остальные сбои полного набора тестов, отмеченные там, не связаны с этим (mount/taskset требуют root, а также есть несколько известных особенностей ограниченных контейнеров). Влияние на размер для aarch64 defconfig составляет +185 байт (wget_main +142, .rodata +43, символы не добавлялись и не удалялись), измерено с помощью scripts/bloat-o-meter и записано в cover-letter.txt.
| Файл | Назначение |
|---|---|
wget-cve-2025-60876.patch | Исправление в виде git format-patch для BusyBox networking/wget.c, готовое к отправке |
commit-message.txt | Сообщение коммита (оно же заголовок патча) |
cover-letter.txt | Сопроводительное письмо [PATCH v3] для списка рассылки, возобновляющее существующую ветку обсуждения |
apply_fix.py | Генератор, применяющий изменение к networking/wget.c (используется для создания патча) |
test/serve_once.py | Слушатель на одно соединение, возвращающий сырой HTTP-запрос |
test/driver.sh | Собирает чистый и пропатченный BusyBox и запускает прямой и прокси-сценарии до/после PoC |
test/finalize.sh | Полный запуск: до/после PoC, bloat-o-meter, testsuite/wget и пересоздание патча |
test/poc-output.txt | Захваченные байты запросов до/после (прямой и через прокси) |
test/testsuite-output.txt | Результаты BusyBox testsuite/wget для сборки с патчем |
test/finalize.sh запускается внутри контейнера alpine:edge с Docker и воспроизводит всё (PoC, размер, testsuite, патч):
docker run --rm -v "$PWD:/work" alpine:edge sh /work/test/finalize.sh
Он загружает BusyBox 1.37.0, собирает его без изменений (показывая инъекцию напрямую и через прокси), применяет исправление, пересобирает, повторяет описанные выше случаи запросов, запускает bloat-o-meter и тестовый набор wget, а затем записывает патч. test/driver.sh — более лёгкий вариант, содержащий только PoC.
BusyBox — проект, управляемый через список рассылки. Исправление отправляется на [email protected] через git send-email как ответ [PATCH v3] в ветке Radoslava Koleva от ноября 2025 года, с упоминанием предыдущих репортёров и рецензентов. См. cover-letter.txt. Исправление распространяется под лицензией GPLv2, той же, что и BusyBox.