FlawOpen

flawopen.com/Incidents/citrix-bleed-session-token-leak-october-2023

● Citrix · CVE-2023-4966 · October 2023 · CVSS 9.4 · 緊急
セキュリティ研究 · FlawOpen

Citrix Bleed:セッショントークン流出とMFA回避

CVE-2023-4966はセッショントークンを含むNetScalerのメモリを漏えいさせ、MFAを経ずにセッションを乗っ取ることを可能にしました。悪用は2023年8月下旬に始まりました。[1], [2]

💡 わかりやすい解説 (ELI5)

アシスタントに、メッセージを小さなカードに書き写して長さを教えてほしいと頼んだとします。カードには 50 文字しか入らないので 50 文字だけ書きますが、長さを尋ねると正直に「800 文字」と答えます。元の文がその長さだったからです。あなたはそのカードを別の人に渡し、800 文字読み上げるよう頼みます。その人はあなたの 50 文字を読み終えたあとも、紙の残りに書かれていることを読み続けてしまいます。[2]

主要な概念と専門用語

snprintf
固定長のバッファに文字列を整形して書き込む C の関数。収まらないときは切り詰めますが、戻り値は完全な文字列が持つはずだった長さです。[3]
NetScaler ADC / Gateway
ネットワークの境界でリモートアクセスと負荷分散を担う Citrix のアプライアンス。影響を受けたのは Gateway または AAA 仮想サーバーとして構成されたものだけです。[1]
バッファの範囲外読み取り(CWE-125)
バッファの終端を越えて読むこと。何も上書きされませんが、隣接するメモリにある内容が漏れます。 これは範囲外読み取りの具体的な仕組み(CWE-125)です。CitrixはCVE-2023-4966を広い分類のCWE-119としています。[1], [3]
セッショントークン
ユーザーがすでにログインしていることを証明する値。これを示した人は、パスワードや MFA を求められずにそのユーザーとして扱われます。[2]

フレームワーク対応

インシデントのタイムライン

2023-08

後のインシデント対応の報告によれば、この欠陥のゼロデイとしての悪用が始まる。[2]

2023-10-10

Citrix が修正版 14.1-8.50、13.1-49.15、13.0-92.19 を含むセキュリティ公告 CTX579459 を公開する。[1]

2023-10-18

CISA が CVE-2023-4966 を既知の悪用された脆弱性カタログに追加する。[4]

ステップ・バイ・ステップの攻撃フロー

ステップ 1

大きすぎるヘッダー

非常に長い Host ヘッダーを持つ認証なしのリクエストが、アプライアンス上で OpenID Connect のディスカバリー応答を組み立てるコードに届きます。[2], [3]

ステップ 2

長さが読み違えられる

snprintf はバッファに収まるよう文字列を切り詰めますが、本来の長さを返します。コードはそれを書き込んだバイト数として扱います。[3], [2]

ステップ 3

バッファの先のメモリが送られる

応答はその大きな長さで送信されるため、アプライアンスは切り詰められた JSON と一緒に隣接するメモリも返します。[2]

ステップ 4

セッションが乗っ取られる

漏れたメモリには有効なセッショントークンが含まれることがあります。それを再利用すれば、攻撃者はパスワードも MFA もなしにユーザーのセッションを得られます。[2], [1]

ソースコード比較:脆弱 vs 堅牢化

説明用の C コードであり、NetScaler のソースコードではありません。修正では snprintf の戻り値を確認します。この値は完全な出力に必要だった長さを示します。負の値、またはバッファーサイズ以上なら、書式化に失敗したか出力が切り詰められているため、関数は何も送信しません。完全な応答が buf に収まる場合に限り、その長さを send_response に渡します。[3], [2]

脆弱な実装
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    /* n は実際に書き込まれるはずだったバイト数で、sizeof(buf) を超える場合がある */
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    return send_response(buf, n);   /* n バイト送信するために buf の範囲外まで読み取る */
}
堅牢化されたセキュアパッチ
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    if (n < 0 || (size_t)n >= sizeof(buf))
        return -1;                  /* 切り詰められた出力を拒否し、バッファーを超えて送信しない */
    return send_response(buf, n);
}

エンジニアリング&システム堅牢化チェックリスト

参考資料