はじめに
― CVE-2023-38501 を引き起こした“内部パラメータ”の正体 ―
Web アプリの脆弱性というのは、往々にして「本来ユーザーに触らせるつもりがなかった機能」が外に漏れたときに生まれます。今回の主人公 k304 もまさにその典型例。
CVE-2023-38501 で悪用されたこのパラメータは、一体何をするために存在していたのでしょうか?
1. k304 は「304 Not Modified」制御のための内部キー
Copyparty はアップロード機能のあるファイルサーバーで、ブラウザからのアクセスに対して、
キャッシュが使える場合は 304 Not Modified を返す 仕組みを持っています。
その内部判定の一部として利用されているのが k304。
本来の目的はひと言でいうと:
「ブラウザとのキャッシュ比較のためのタグ値(ETag 相当)」をやり取りするためのキー
つまり、ユーザーが触る予定など一切なかった「サーバー内部の調整用パラメータ」です。
2. なぜ外部に露出していたのか?
Copyparty は GET パラメータをそのまま受け取り、
「k304=xxx が送られてきたら、その値をヘッダの ETag 相当に書き込む」
という実装をしていました。
問題はその 値をサニタイズしないまま、HTTP レスポンスヘッダに出力していた こと。
パッチ前(v1.8.6 以前)の set_k304 は、ほぼ 1 行だけでした:GitHub
ざっくり書き直すと、イメージはこんな感じです:
def set_k304(self) -> bool:
ck = gencookie("k304", self.uparam["k304"], self.args.R, False, 86400 * 299)
self.out_headerlist.append(("Set-Cookie", ck))
self.redirect("", "?h#cc")
return True
ポイント:
-
self.uparam["k304"]
→ クエリパラメータ?k304=...がそのまま入る - それを
gencookie()に渡してSet-Cookieヘッダを生成 -
out_headerlistに追加され、send_headers()でレスポンスヘッダとして送信
つまり 「ユーザーが指定した文字列が header の一部(Set-Cookie 値)としてそのまま出力される」 状態です。
結果──
攻撃者が k304 に CRLF (%0D%0A) を注入すると、
レスポンスヘッダが意図せず途中で終了し、
続きが 攻撃者指定の HTML としてブラウザへ到達 します。
これがまさに HTTP Response Splitting → Reflected XSS のルート。
3. k304 を悪用した攻撃の流れ
攻撃者が以下の URL を踏ませるだけで:
?k304=y%0D%0A%0D%0A<img src=copyparty onerror=alert(1)>
サーバーは次のようなレスポンスを返します:
HTTP/1.1 200 OK
Content-Type: text/html
ETag: y
<img src=copyparty onerror=alert(1)>
ETag: の直後でレスポンスヘッダが強制的に終了し、
攻撃者が差し込んだ <img ...> が レスポンスボディとして解釈され、XSS が実行 されます。
もはや「304 のための ETag」などどこかへ吹き飛び、
ただの 攻撃者の自由入力欄 と化してしまったわけですね。
4. 脆弱性の本質:内部機能の“漏洩”
この脆弱性が示すものはとてもシンプルで、しかし非常に重要です。
内部用パラメータを URL クエリから直接変更できるようにすると、攻撃者に「サーバーの内部スイッチ」をプレゼントしてしまう。
- サーバー側が「ここはユーザーに触られないはず」と思っていた
- しかし実際には GET パラメータとして誰でも操作できた
- しかも出力が無検証だった
→ 当然 XSS となる
まさに 信頼境界の崩壊 が起きた例です。
5. どうやって防ぐべきだったのか?
防御策は3つの観点で語れます。
① 内部パラメータを公開しない
そもそも GET パラメータとして受け取る必要がない。
内部ロジック専用の値はユーザー入力と同じチャンネルに置かない。
② HTTP ヘッダに出力する値は必ずサニタイズする
CRLF(改行)を含めない検証は最低限必要。
Header Injection は古典的だがいまだに強烈。
③ 設計段階で「外部から触れない前提」を作らない
「ユーザーが使わないはず」という前提は攻撃者には通じません。
6. 攻撃者から見た k304 の魅力
攻撃者視点から見ると、k304 はこう見えていたはず。
- 出力位置が HTTP レスポンスヘッダ直後(これは超いいポジション)
- 値が一切フィルタされない(CRLF 入れ放題)
- HTML を自由に出せる(即 XSS)
「反射型 XSS 自動販売機」のような状態です。
まとめ
| 項目 | 説明 |
|---|---|
| k304 の正体 | 304 判定のための内部キャッシュパラメータ |
| なぜ危険? | ヘッダにそのまま出力され、CRLF で改行注入される |
| 結果 | HTTP Response Splitting → Reflected XSS |
| 本質 | 内部ロジックが外部入力に漏れた典型例 |
アプリ開発では「外部から触られないはず」という思い込みが最も恐ろしい。
今回の事件は、そのことを静かに、でも強烈に教えてくれます。
リンク
https://www.exploit-db.com/exploits/51635
https://github.com/9001/copyparty