
CVE-2025-60876(BusyBox wget における HTTP ヘッダインジェクション脆弱性)に対する動作を維持する修正、それを実証する概念実証、および修正を BusyBox にアップストリーム提出するための成果物です。
BusyBox wget は、URL のパスとクエリを HTTP リクエスト行にそのままコピーします。生の CR (0x0D)、LF (0x0A)、またはその他の制御バイトを含む URL は、リクエスト行を分割し、攻撃者が制御するヘッダを注入する可能性があります。スペース (0x20) も同様の効果を持ちます。METHOD SP request-target SP HTTP/1.1 のフレーミングを破壊します。
影響範囲:BusyBox 1.37.0 まで(すべてのサポート中の Alpine ブランチに同梱されている pkgver)。2025年8月に BusyBox メーリングリストで報告され、CVE-2025-60876 として CVE 割り当て(NVD CVSS 6.5、Medium)。
既に2つのパッチがアップストリームに提案されています(Takeuchi Yuma、2025年8月;Radoslav Kolev、2025年11月)。どちらも問題のある URL を 拒否 し、bb_error_msg_and_die を呼び出します。これによりインジェクションは阻止されますが、通常のスペースも拒否されるため、以前は動作していた http://example.org/foo bar のような URL がエラーになります。この後方互換性の欠如が、修正がダウンストリームに取り込まれていない理由です。Alpine はこれに関するバックポートを保留しています()。
この修正では、パスとホストを異なる方法で処理し、GNU wget および curl の動作に合わせています。
パス: networking/wget.c の percent_encode_target() が、リクエスト行が構築される前に、リクエストターゲット内の制御バイト(0x00 ~ 0x1f)、スペース (0x20)、および DEL (0x7f) をパーセントエンコードします。
http://example.org/foo bar は /foo%20bar として送信され、GNU wget および curl と一致します。後方互換性の問題はありません。% は変更されないため、既にエンコードされたパスが二重エンコードされることはありません。ホスト: URL のホスト内の同じバイトは拒否されます。ホスト名に正当に制御文字やスペースが含まれることはなく、権限コンポーネントに対してパーセントエンコードは定義されていません。これはプロキシモードで重要です。ホストは絶対形式のリクエストターゲット(GET http://host/path)および Host: ヘッダに配置されますが、ローカルでは解決されないため、ホスト内の生の CR や LF は、そうでなければ注入を引き起こします。GNU wget(CVE-2017-6508 以降)および curl もホスト内の制御文字を拒否します。
変更は networking/wget.c のみに限定され、1つのヘルパー関数が追加されます。
範囲外: FTP 制御チャネル(ftpcmd は target->path とユーザー/パスワードをそのまま送信します。アップストリームで認識された TODO)は別のインジェクション種別であり、CVE-2025-60876 の一部ではなく、ここでは対処しません。別途追跡されるべきです。
Alpine が使用するのと同じ設定(CONFIG_WERROR と CONFIG_TC をオフ)で BusyBox 1.37.0 に対してビルド。ローカルリスナーが 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 テスト4つはすべてパッチ適用後のビルドに対してパスします(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 | BusyBox networking/wget.c に対する git format-patch 形式の修正。提出準備済み |
commit-message.txt | コミットメッセージ(パッチヘッダも兼ねる) |
cover-letter.txt | 既存のスレッドを再開する [PATCH v3] メーリングリストカバーレター |
apply_fix.py | 変更を networking/wget.c に適用するジェネレーター(パッチの生成に使用) |
test/serve_once.py | 生のHTTPリクエストをエコーバックする単一接続リスナー |
test/driver.sh | バニラおよびパッチ適用後の BusyBox をビルドし、直接+プロキシの before/after PoC を実行 |
test/finalize.sh | 完全実行:before/after PoC、bloat-o-meter、testsuite/wget、パッチの再生成 |
test/poc-output.txt | キャプチャされた before/after リクエストバイト(直接およびプロキシ) |
test/testsuite-output.txt | パッチ適用後のビルドに対する BusyBox testsuite/wget の結果 |
test/finalize.sh は、Docker を使用して alpine:edge コンテナ内で実行され、すべて(PoC、サイズ、テストスイート、パッチ)を再現します。
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 はメーリングリストベースのプロジェクトです。修正は git send-email を介して [email protected] に送信され、Radoslav Kolev の2025年11月のスレッドに対する [PATCH v3] 返信として、以前の報告者やレビュアーに credit を与えます。cover-letter.txt を参照してください。修正は BusyBox と同じ GPLv2 ライセンスです。