libexpat の billion laughs 保護の回避、2.10.0 で修正(CVE-2026-115506)
libexpat 2.9.0 以前は一部の billion laughs 系攻撃を検出するのが遅すぎます。2.10.0 はこの問題と他3件の解析欠陥を修正します。
TL;DR
- 2026年11月2日にリリースされた libexpat 2.10.0 は、4件のセキュリティ欠陥を修正します。最大のものは CVE-2026-115506 で、既存の保護では検出が遅すぎる billion laughs 系の亜種です。残る3件は
storeAttsの符号付き整数オーバーフロー(CVE-2026-118370)、XML_ParserReset経由のサービス拒否(CVE-2026-113351)、空のサブパーサー入力による NULL 参照(CVE-2026-117610)です。 - 前回版の 2.9.0 までのすべてのリリースが影響を受けます。CVE レコードは CVE-2026-115506 を 8.7 High、CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N、他の3件を 8.2 High と評価しています。
- 悪用は知られておらず、2026年11月2日時点で4つの CVE のいずれも CISA の KEV カタログに載っていません。Hanno Böck 氏の公開ペイロードファザーが、検出の隙間を発見した亜種を生成しました。
- 2.10.0 に更新するか、ディストリビューションのバックポートを待ってください。Expat のコピーをバンドルしている場合は、メンテナーの要望どおりコピーを更新してください。
影響を受けますか?
以下のすべてが成り立つ必要があります。
- libexpat 2.9.0 以前、またはそれをバンドルしたソフトウェアを実行していること。Python の
xml.parsers.expatモジュールは Expat をリンクしており、CPython リポジトリ自体がModules/expatにコピーを同梱しています。 - そのソフトウェアが攻撃者制御の XML 文書、またはそれを解析するライブラリを扱っていること。
- 各欠陥で前提は異なります。CVE-2026-115506 は中程度のサイズの文書だけで済みますが、CVE-2026-118370 は単一の開始タグに 2^30 個を超える属性を持つ数ギガバイトの文書が必要です。
メンテナーは Expat を「packaged everywhere」と説明し、最も広く使われる2つのフリーな C 言語 XML パーサーの1つに挙げています。ディストリビューションはランタイムライブラリとして収録し、Python、Perl、Apache のスタックはその上に成り立っています。Debian のセキュリティトラッカーは、対象のすべてのスイートで expat パッケージを脆弱としています。
| ブランチ | 影響を受けるバージョン | 修正バージョン |
|---|---|---|
| libexpat stable | 2.7.2 から 2.9.0(CVE-2026-113351)、1.95.2 から 2.9.0(残る3件) | 2.10.0(2026-11-02) |
リンクしているバージョンを確認してください。Debian と Ubuntu では libexpat1 として配布され、Python インタープリターはリンク先のコピーのバージョンを報告します。
dpkg -l libexpat1
python3 -c "import xml.parsers.expat as e; print(e.EXPAT_VERSION)"
expat_2.9.0 のような出力は影響を受けるバージョンです。2026年11月2日時点で、Debian のセキュリティトラッカーは bookworm、trixie、forky、sid の expat パッケージを脆弱かつ未修正としています。
修正方法
libexpat 2.10.0 に更新してください。
- ディストリビューションからインストールしている場合は、セキュリティ更新の提供を待ち、届き次第適用します。メンテナーの勧告は、このリリースで4件の修正がバックポート可能になったことを示しています。
- ソースからビルドするかコピーをバンドルしている場合は、バンドルしたツリーを
R_2_10_0タグに更新し、すべての利用者を再ビルドします。メンテナーの勧告は、パッケージ作成者とバンドル側に 2.10.0 への更新を求めています。 - バックポートを維持している場合、プルリクエスト #1439 が、billion laughs 修正のバックポート時に不可欠とメンテナーが指摘するフォローアップです。
更新が引き続き主要な修正です。Expat は 2.6.0 から利用できる XML_GE により、ビルド時に一般実体を無効にできます。実行時には XML_SetDefaultHandler がテキスト内の内部定義された一般実体の展開を抑制しますが、その動作と利用可否は組み込みアプリケーションやバインディングに依存します。配備済みアプリケーション向けの汎用的な実行時スイッチではありません。DTD を必要としないアプリケーションは解析前に DTD 付き文書を拒否できますが、正当な文書も拒否します。Expat API 文書を参照してください。
根本原因は何ですか?
4つの欠陥はいずれもパーサー本体の expat/lib/xmlparse.c にあります。この節は 2.9.0 タグでの脆弱なコードを引用します。
CVE-2026-115506:増幅チェックが展開処理の後に実行される。 Expat は解析中にトークンごとの直接バイト数と間接バイト数を数え、比率がしきい値を超えると拒否します。チェック関数は次のとおりです。
static XML_Bool
accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
const char *after, int source_line,
enum XML_Account account) {
...
*additionTarget += bytesMore;
const XmlBigCount countBytesOutput
= rootParser->m_accounting.countBytesDirect
+ rootParser->m_accounting.countBytesIndirect;
const float amplificationFactor
= accountingGetCurrentAmplification(rootParser);
const XML_Bool tolerated
= (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
|| (amplificationFactor
<= rootParser->m_accounting.maximumAmplificationFactor);
...
return tolerated;
}
この関数はトークン処理経路から呼ばれるため、カウンターが更新される時点で、Expat はそのトークンの実体内容をすでに展開しています。Hanno Böck 氏のファザー lololol は、累積比率がしきい値を超えるまで数十秒の展開が続くパラメータの組み合わせを見つけました。修正のプルリクエスト #1433 は各実体に expandedSize フィールドを追加し、展開を始める前にどこまで展開されるかを事前計算します。拒否は前倒しで行われ、プルリクエスト #1439 はその走査を実体値内の CDATA セクション、コメント、処理命令まで拡張します。
CVE-2026-118370:storeAtts の2つの符号付きカウンターがオーバーフローする。 2.9.0 でこの関数は次のように宣言されています。
static enum XML_Error
storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
enum XML_Account account) {
DTD *const dtd = parser->m_dtd; /* save one level of indirection */
int attIndex = 0;
XML_Char *uri;
int nPrefixes = 0;
両方の int カウンターは 2^31 属性で折り返します。2^30 個を超える属性を持つ細工された開始タグは、算術を範囲外に導き、ヒープを破壊します。修正のプルリクエスト #1450 は両方を size_t に変更し、値を int 型のパーサーフィールドに格納する箇所に明示的な INT_MAX 検査を追加します。
CVE-2026-113351:XML_ParserReset がアロケーショントラッカーを相殺しない。 Expat 2.7.2 は、文書サイズに比例したアロケーションを許容するヒープトラッカーを追加しました。再利用のためにパーサーをリセットするとプールされたメモリは保持されますが、トラッカーの bytesAllocated 合計は前の文書のアロケーションを保持します。その結果、同じパーサーへの次の通常の文書が XML_ERROR_NO_MEMORY で拒否されることがあります。修正のプルリクエスト #1431 は bytesAllocatedWhenLastReset を記録して差し引くことで、各文書をゼロから始められるようにします。
CVE-2026-117610:空入力の case が欠落。 2.9.0 の externalEntityInitProcessor2 は XML_TOK_BOM や XML_TOK_PARTIAL などを扱いますが、XML_TOK_NONE の case を持ちません。外部実体サブパーサーでの空で非最終の最初の解析は externalEntityInitProcessor3 にそのまま進み、後続の解析がクラッシュします。プルリクエスト #1444 が case を追加し、XML_ERROR_NONE を返します。
どのように悪用されますか?
4件のいずれについても完全なエクスプロイトは公開されていません。攻撃入力は CVE レコードとメンテナーの勧告に基づきます。
- 攻撃者は、大きな DTD と入れ子の実体展開を含む中程度サイズの XML 文書を送ります。公開ファザーはこの種の文書を検出しきい値の下に可能な限り長くとどまるよう調整しており、解析は
accountingDiffToleratedが拒否するまで数十秒の CPU を消費します(CVE-2026-115506)。 - 攻撃者は、単一の開始タグに 2^30 個を超える属性を持つ数ギガバイトの文書を送ります。
storeAttsのintカウンターが折り返し、属性コピーのループがバッファの範囲外に書き込みます(CVE-2026-118370)。 - 攻撃者は、アプリケーションが
XML_ParserResetで再利用するパーサーで大きな文書を解析させ、その後の2番目の通常の文書がパーサー再生成までXML_ERROR_NO_MEMORYで失敗するようにします(CVE-2026-113351)。 - 攻撃者は外部実体の入力ストリームのチャンク分割を制御し、空で非最終のチャンクで開始させて、次の解析でプロセスをクラッシュさせます(CVE-2026-117610)。
ステップ1の攻撃文書を生成するファザーは公開されています:github.com/hannob/lololol。
攻撃はどのように流れますか?
一文でいうと、深い実体ツリーを持つ細工された文書は増幅チェックが気づくより速く展開するため、パーサーは拒否するまで CPU を焼き続けるか、属性カウンターが折り返して範囲外書き込みに至ります。
XML 文書"] --> B["実体展開が
間接バイトを加算"] B --> C["増幅チェックは各トークンの
後にしか実行されない"] C --> D["展開作業が
拒否より先に行われる"] D --> E["解析ごとに数十秒の
CPU 消費"]
攻撃者は何をできますか?
文書化された影響は、解析プロセスのサービス拒否と、CVE-2026-118370 におけるヒープの範囲外書き込みです。
- 攻撃者は、文書を解析するシステムに届ける以外の特権や操作を必要としません。4つの CVSS v4.0 ベクトルはすべてネットワーク攻撃ベクトルを記録しています。
- CVE-2026-115506 と CVE-2026-113351 はサービス拒否の欠陥です。CVE レコードはそのように説明し、メンテナーの勧告はコード実行の影響を評価していません。
- CVE-2026-118370 は、要求の厳しい前提を持つメモリ破壊です。数ギガバイトの文書に単一の開始タグで 2^30 個を超える属性が必要です。CVE レコードは「アプリケーションのクラッシュまたはヒープメモリの破壊」までとしており、コード実行を示す情報源はありません。
- CVE-2026-117610 は、攻撃者がストリームのチャンク分割を制御するときにプロセスをクラッシュさせます。この前提は CVE レコードに明記されています。
なぜ修正は機能しますか?
4つのプルリクエストが4つのメカニズムを閉じます。
CVE-2026-115506 については、プルリクエスト #1433 は展開開始前に各実体の expandedSize を事前計算し、増幅チェックはバイトカウンターを待つのではなく予測サイズを参照します。
/* Indirect accounting bytes charged when this entity is expanded: 1 per text
character, plus each reference's "&name;"/"%name;" token length and its own
expansion. ... */
XmlBigCount expandedSize;
CVE-2026-118370 については、プルリクエスト #1450 は2つのカウンターを変更し、格納を制限します。
- int attIndex = 0;
+ size_t attIndex = 0;
XML_Char *uri;
- int nPrefixes = 0;
+ size_t nPrefixes = 0;
CVE-2026-113351 については、プルリクエスト #1431 はリセット時の水位を追加します。
+ size_t bytesAllocatedWhenLastReset;
XML_ParserReset はリセット時のアロケーション合計を記録し、次の文書の許容チェックは前の文書が残した分を差し引きます。
CVE-2026-117610 については、プルリクエスト #1444 は欠けていた case を追加します。
+ case XML_TOK_NONE:
+ if (! parser->m_parsingStatus.finalBuffer) {
+ *endPtr = start;
+ }
+ return XML_ERROR_NONE;
メンテナーは、この期間に12件のセキュリティ問題を修正し、20件の新規発見に対して、リリース時点で13件の既知の未修正セキュリティ問題が残っていると述べています。4件の修正は、対象とするメカニズムに対して完結しています。さらに billion laughs の亜種が残るかどうかは、まさにファザーが探り続けていることです。
侵害されたか確認するには?
4つの欠陥はいずれも設計上データを漏らさないため、確認は可用性の異常に焦点を当てます。
- 信頼できない入力を扱う XML パーサーのログを確認します。
XML_ERROR_NO_MEMORYは CVE-2026-113351、解析中の長時間の CPU 消費やXML_ERROR_AMPLIFICATION_LIMIT_BREACHエラーは CVE-2026-115506 の調査の手掛かりになります。これらの観測だけでは悪用を証明できません。 - XML を解析するサービスに異常に大きなペイロードを届けたリクエストを探します。数ギガバイトの送信も含みます(CVE-2026-118370)。
- CVE-2026-117610 については、外部実体サブパーサー経路のクラッシュダンプを確認します。
Expat が拒否した billion laughs の文書は、拒否エラー以外の痕跡を残しません。拒否されたからといって、サービスが劣化したとは限りません。
年表
- 2026-10-20:
XML_ParserResetのサービス拒否について CVE-2026-113351 が公開されます(Pu Cheng 氏による報告)。 - 2026-10-22:Hanno Böck 氏のファザーによる亜種の発見を経て、billion laughs 検出の隙間について CVE-2026-115506 が公開されます。
- 2026-10-23:
XML_ParserResetの修正、プルリクエスト #1431 がマージされます。 - 2026-10-26:billion laughs のフォローアップ、プルリクエスト #1439 がマージされます。
- 2026-10-28:CVE-2026-117610 と CVE-2026-118370 が公開されます。
- 2026-10-29:残りの修正、プルリクエスト #1444 と #1450 がマージされます。
- 2026-11-02:4件すべての修正を含む libexpat 2.10.0 がリリースされます。
よくある質問
4件のうちどれを優先すべきですか?
CVE-2026-115506 です。入力はどんな XML エンドポイントも受け入れる中程度サイズの文書で、コストは解析ごとに数十秒の CPU です。CVE-2026-118370 はメモリ破壊ですが、数ギガバイトという前提がはるかに届けにくくしています。
Python アプリケーションは影響を受けますか?
影響を受ける Expat をリンクするプロセスすべてが対象で、xml.parsers.expat 経由の Python も含みます。インタープリターは EXPAT_VERSION でリンクバージョンを報告します。システムパッケージの Python では、システムの libexpat1 を更新すればパーサーも更新されます。
実世界で悪用されましたか?
4件のいずれについても悪用の報告はなく、2026年11月2日時点で KEV カタログにも載っていません。公開ファザーは攻撃を実証しており、実際の使用を示すものではありません。
1件だけ 2.7.2 の下限があるのはなぜですか?
CVE-2026-113351 が壊すアロケーショントラッカーは 2.7.2 で追加されたため、この欠陥はそれ以前には存在できません。残る3件は保護より前から存在するか、2.0 以前からある経路に存在するため、レコードは「2.9.0 まで」としています。
実体展開を無効にするだけではだめですか?
ビルド時と実行時の制御は異なります。XML_GE はビルド時の一般実体サポートを制御し、XML_SetDefaultHandler はテキスト内の内部一般実体の展開を変更します。アプリケーションやバインディングが公開する機能を確認し、互換性をテストしてください。2.10.0 への更新が引き続き主要な修正です。
誰が見つけ、誰が直したのですか?
Hanno Böck 氏が自身のファザーで billion laughs の亜種を見つけ、Pu Cheng 氏がリセットの欠陥を報告しました。展開前の事前計算という修正の鍵となる考えは Shani Yosef 氏がもたらし、リリース勧告と CVE のクレジットが記録しています。
Ostorlab で Expat の実行場所を把握する
Ostorlab の依存関係スキャンは、サービスとコンテナイメージがインストールするパッケージの完全な在庫を構築し、影響を受ける libexpat を持つすべてのデプロイを見つけます。Ostorlab でスキャンを実行し、2.10.0 またはバックポート済みの更新を必要なすべての場所で確認してください。
参考文献
- Expat メンテナーのリリース勧告「Expat 2.10.0 released, fixes four vulnerabilities」、2026年11月2日公開、攻撃の説明、クレジット、プルリクエスト #1439 へのバックポート注記を含む:https://blog.hartwork.org/posts/expat-2-10-0-released/(2026年11月2日確認)
- oss-security のリリース告知「libexpat 2.10.0 fixes two vulnerabilities」、2026年11月2日公開、4つの CVE、変更履歴の項目、修正プルリクエストの一覧を含む:https://www.openwall.com/lists/oss-security/2026/11/02/6(2026年11月2日確認)
- GitHub の Expat 2.10.0 リリースページ、2026年11月2日公開:https://github.com/libexpat/libexpat/releases/tag/R_2_10_0(2026年11月2日確認)
- CVE-2026-115506 レコード、2026年10月22日公開、CVSS v4.0 8.7 High、CVSS v3.1 7.5 High:https://www.cve.org/CVERecord?id=CVE-2026-115506(2026年11月2日確認)
- CVE-2026-113351 レコード、2026年10月20日公開、CVSS v4.0 8.2 High、CVSS v3.1 5.9 Medium:https://www.cve.org/CVERecord?id=CVE-2026-113351(2026年11月2日確認)
- CVE-2026-117610 レコード、2026年10月28日公開、CVSS v4.0 8.2 High、CVSS v3.1 5.9 Medium:https://www.cve.org/CVERecord?id=CVE-2026-117610(2026年11月2日確認)
- CVE-2026-118370 レコード、2026年10月28日公開、CVSS v4.0 8.2 High、CVSS v3.1 5.9 Medium:https://www.cve.org/CVERecord?id=CVE-2026-118370(2026年11月2日確認)
- CVE-2026-113351 の修正プルリクエスト #1431、2026年10月23日マージ:https://github.com/libexpat/libexpat/pull/1431(2026年11月2日確認)
- CVE-2026-115506 の修正プルリクエスト #1433(事前計算された展開サイズ)と、CDATA・コメント・処理命令を扱うフォローアップ #1439:https://github.com/libexpat/libexpat/pull/1433(2026年11月2日確認)
- CVE-2026-117610 の修正プルリクエスト #1444、2026年10月29日マージ:https://github.com/libexpat/libexpat/pull/1444(2026年11月2日確認)
- CVE-2026-118370 の修正プルリクエスト #1450、2026年10月29日マージ:https://github.com/libexpat/libexpat/pull/1450(2026年11月2日確認)
- Hanno Böck 氏の billion laughs 亜種向けペイロードファザー、Expat の変更履歴からリンク:https://github.com/hannob/lololol(2026年11月2日確認)
- Expat プロジェクトのホームページ、Expat がどこでもパッケージされていること、利用者・バインディング・ラッパーの一覧:https://libexpat.github.io/(2026年11月2日確認)
- 引用コードのための R_2_9_0 タグにおける
expat/lib/xmlparse.cの脆弱なソース:https://github.com/libexpat/libexpat/blob/R_2_9_0/expat/lib/xmlparse.c(2026年11月2日確認) - CVE-2026-115506 の Debian セキュリティトラッカー、2026年11月2日時点で bookworm、trixie、forky、sid の expat を脆弱かつ未修正と表示:https://security-tracker.debian.org/tracker/CVE-2026-115506(2026年11月2日確認)
- CVE-2026-115506 の Ubuntu セキュリティページ、脆弱な expat ソースパッケージとシステムの expat を使うパッケージの一覧:https://ubuntu.com/security/CVE-2026-115506(2026年11月2日確認)
- CISA KEV カタログの JSON フィード、4つの CVE について検索、2026年11月2日時点で一致なし:https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json(2026年11月2日確認)