
Bloomberg Memray のエスケープされていないコマンドラインメタデータによる保存型XSS
Bloomberg Memray のエスケープされていないコマンドライン・メタデータを介した保存型 XSS
Memray(Bloomberg の Python メモリプロファイラ)をレビューしていた際、私はシンプルな疑問を胸にこの問題を発見しました。
攻撃者が制御するランタイム・メタデータは、ブラウザでレンダリングされるレポート出力に、安全でない形で混入し得るのか?
このケースでは、答えはイエスでした。
バグは Memray の HTML レポート生成経路にあり、コマンドライン・メタデータがエスケープされることなく、ブラウザで開かれるレポートにレンダリングされていました。これにより、運用上のフィールドが実行可能な HTML シンクへと変わり、最終的に CVE-2026-32722 になりました。
プロジェクト: Memray on GitHub
アドバイザリ: GHSA-r5pr-887v-m2w9 /CVE-2026-32722
attacker-controlled argv → metadata.command_line → HTML template sink without escaping → raw markup in generated report → browser-side JavaScript execution
Memray は Python メモリプロファイラです。
Python プロセスを計測し、アロケーションの挙動を記録して、開発者が以下を理解するのに役立つレポートを生成します:
これらのレポートの一部は HTML として出力され、ブラウザで開かれます。
そのため、レポート生成は現実のセキュリティ境界となります。
重要な問いは、Memray が「ローカルツール」であるかどうかではありません。
重要な問いは、攻撃者が制御するデータがブラウザでレンダリングされる出力に、安全でない形で混入し得るか です。
このケースでは、それは可能でした。
多くの人々は開発者向けツールを過小評価しています。
それは誤りです。
ツールが一度でも:
そのツールは Web アプリケーションと同じ出力エンコーディングのリスクを引き継ぎます。
これが今回の核心的な問題でした。
このバグはプロファイリングロジックにあったのではありません。 アロケーション追跡にあったのでもありません。 ネイティブメモリ処理にあったのでもありません。
これは古典的な信頼境界の失敗でした:
それだけで、現実の脆弱性を生み出すのに十分です。
私は Memray に対して、ランダムな CLI オプションをファジングしたり、クラッシュを追いかけたりするアプローチは取りませんでした。
より強力なアプローチは、まず最初に最も可能性の高いセキュリティ面を特定することでした。
Memray にとって、それは HTML レポート生成 でした。
なぜでしょうか?
なぜなら HTML 出力はブラウザシンクを導入し、ブラウザシンクは以下の条件が揃うと、ありふれたメタデータのバグをセキュリティ問題へと変えるからです:
今回起きたのはまさにこれです。
このバグは 2 行に要約できます。
以下の箇所で:
def get_render_environment() -> jinja2.Environment:
loader = jinja2.PackageLoader("memray.reporters")
env = jinja2.Environment(loader=loader)
Jinja 環境が autoescape なしで作成されています。
そして以下の箇所で:
Command line: <code>{{ metadata.command_line }}</code><br>
metadata.command_line が直接 HTML にレンダリングされます。
これが脆弱性の全体です。
なぜなら metadata.command_line は攻撃者の影響を受けるからです。
Memray は、プロファイリング対象のプログラムを実行するために使われたコマンドラインを記録します。
つまり、argv からのユーザー制御の値がメタデータとして保持され、後でレポートに挿入されるということです。
したがって、悪用チェーンは単純明快です:
metadata.command_line に保存するこれにより、メタデータが実行可能なブラウザコンテンツへと変わります。
重要な違いは「実行」です。
フォーマットが崩れた HTML を生み出すバグは数多くあります。 それだけでは不十分です。
今回、攻撃者が制御するコンテンツは、単にページソースに表示されるだけではありませんでした。 ブラウザによってアクティブな HTML として解釈され、JavaScript として実行されました。
これが以下の違いです:
したがって問いは、次のようなものではありませんでした:
「HTML がレポートに現れ得るのか?」
本当の問いは次の通りでした:
「レポートを開いたとき、攻撃者が制御する HTML は実行可能になり得るのか?」
答えはイエスでした。
私の最初の再現コードは、明示的なスクリプトと攻撃者が制御する引数を使用しました:
cat > victim.py <<'PY'
x = [b"A" * 1024 for _ in range(1000)]
print("done")
PY
python -m memray run -o poc.bin victim.py ''
python -m memray flamegraph -o poc.html poc.bin
生成された HTML には、攻撃者が制御する生のマークアップが含まれていました:
Command line: <code>~/memray/src/memray/__main__.py run -o poc.bin victim.py </code><br>
生成されたレポートを開くか更新すると、JavaScript の実行が引き起こされました。
これにより、中心的な主張が確立されました:
その後、協調的な開示の過程で、メンテナは再現コードをさらに簡素化しました:
python -m memray run -o poc.bin -c '# '
このバージョンの方が優れているのは、脆弱な境界をより直接的に切り出しているからです:
ペイロードは意図的に単純なものにしました:
これは派手なペイロードの話ではありません。
これはクリーンな実行プローブです。なぜなら:
この種のバグにとって、それで十分です。
1 つのレポート種別だけでも、この問題を報告するには十分でした。
しかし私は、これが孤立した問題なのか、構造的な問題なのかを知りたいと思いました。
同じ挙動を以下のレポートで確認しました:
--no-web で生成された flamegraph レポートこれには 2 つの理由がありました。
脆弱なシンクが複数の HTML 出力にわたって再利用されていることが示されました。
このバグが CDN でホストされた外部アセットに依存していないことが証明されました。
--no-web でも問題が再現しました。つまり、問題はリモートの JS の動作ではなく、Memray 自身が生成する HTML とテンプレート処理にあったということです。
そのため、この主張ははるかに強力になりました。
メンテナの主な懸念は、現実的な攻撃者の制御可能性でした。
それは妥当です。
これは、認証されていないリモート攻撃者が露出した HTTP エンドポイントに到達して即座に影響を得る、といった種類の問題ではありません。
悪用の条件はより狭いものです:
そのため、現実的な分類は低重大度でした。
だからといって、これは弱いバグではありません。
重大度は悪用条件と想定される影響に関するものです。 妥当性は、その問題が現実のものかどうかに関するものです。
この問題は明らかに現実のものでした:
だからこそ、これは依然として CVE になりました。
修正は最小限かつ正しいものでした。
メンテナは以下の箇所を:
{{ metadata.command_line }}
以下のように変更しました:
{{ metadata.command_line|e }}
これは脆弱なシンクに直接対処するため、正しい修正です。
以下のような生のマークアップを出力する代わりに:
テンプレートは現在、エスケープされたテキストを出力します:
<img src=x onerror=alert(1)>
これにより、ブラウザでの実行経路を排除しつつ、コマンドラインフィールドの情報としての価値が保持されます。
メンテナはまた、テンプレートの残りのコンテキストをレビューし、以下のように結論付けました:
|tojson でレンダリングされているそのため、この問題は正しく metadata.command_line に限定されました。
これはまさに、実際の開示プロセスで期待される修正レビューのあり方です。
これは GitHub Security Advisories を通じて非公開で報告されました。
メンテナたちは:
この問題には次の ID が割り当てられました: CVE-2026-32722
ここでの重要な教訓は単純です:
メタデータは、運用上のものに見えるというだけで、自動的に信頼されるべきではない。
攻撃者が制御するコンテンツが、エスケープされずにブラウザでレンダリングされる出力へ混入してしまえば、それらはどれも意味を持ちません。
ツールが HTML を出力する瞬間から、そのツールは HTML を生成するアプリケーションとして扱われる必要があります。
それが本当の教訓です。
この脆弱性は、巧妙なペイロードの話ではありません。
適切な境界を特定することでした。
Memray は、攻撃者の影響を受けたコマンドライン・メタデータを受け取り、それをエスケープせずに生成された HTML へレンダリングしました。 残りはブラウザが行いました。
だからこそ、これは CVE-2026-32722 になりました。
Memray 1.19.2 で修正されました。
