Twisted: 認証されていないリモートDoS攻撃の脆弱性 – TwistedのXML要素ストリームにおける、制限のない要素ツリーの利用

## 概要

TwistedのXML要素ストリームにおける、制限のないメモリ割り当ての脆弱性により、認証されていないリモートのクライアントが、単一のストリームを通じてサーバーのメモリを枯渇させることが可能です。クライアントがコンテナ要素を開き、子要素をストリームで送信し続ける際に、コンテナ要素を閉じない場合、すべての要素がメモリ上にツリー構造として構築されます。このツリー構造には、深さや子要素の数に対する制限はありません。要素は、終了タグが受信されるまで解放されません。したがって、攻撃者が決して閉じないコンテナ要素は、プロセスが終了するまで、その下に存在するすべての要素を保持し続けます。

## 詳細

`ExpatElementStream._onStartElement`関数では、各開始タグに対して、新しい要素が現在の要素の子要素に追加され、その要素に移行されます。この際、深さや子要素の数に対するチェックは行われません。

```python
# src/twisted/words/xish/domish.py (ExpatElementStream._onStartElement)
if self.documentStarted == 1:
if self.currElem != None:
self.currElem.children.append(e) # 子要素を追加: 深さ/数の制限なし
e.parent = self.currElem
self.currElem = e # 新しい要素に移行
```

要素は、ドキュメントのルート直下にある場合にのみ、終了タグを受信した際に生成され、解放されます。したがって、閉じられないコンテナ要素は、その下に存在するすべての子要素を保持し続けます。トランスポート層の`XmlStream.dataReceived`関数は、サイズ制限なしで`self.stream.parse(data)`にバイトデータを送信します。また、XMPPでは、認証の前にストリームの解析が行われます。

影響を受ける製品: Twisted、`twisted.words.xish.domish`要素ストリーム
影響を受けるバージョン: 26.4.0で確認済み

## 脆弱性の検証方法

このスタックを使用している信頼できないXMLストリームを解析するエンドポイント(例えば、XMPPサーバー)において、開始タグ`stream:stream`とコンテナ要素を送信してストリームを開き、その後、子要素を継続的にストリームで送信し、コンテナ要素の終了タグを送信しないようにします。認証情報は不要です。なぜなら、XMPPではストリームの解析は認証の前に実行されるからです。

すべての要素がコンテナに追加され、保持されます。これは、コンテナが送信されることがないためであり、また、未完成のドキュメントのサイズに関する制限が、データ転送に適用されないためです。各小さな要素がメモリ内で完全なオブジェクトとして扱われるため、メモリ使用量は送信されるデータ量よりもはるかに大きくなります。そのため、帯域幅の限られたクライアントや、そのようなストリームが複数ある場合、プロセスはメモリの枯渇を引き起こします。

## 影響

これは、メモリの枯渇によって引き起こされる、認証されていないリモートDoS(サービス拒否)攻撃です。単一のストリームが、要素ごとのオブジェクトのオーバーヘッドによって増幅されながら、メモリ内で要素ツリーを無限に成長させ、最終的にオペレーティングシステムがサーバーを停止させます。機密性や完全性への影響はなく、コードの実行もありません。影響を受けるのは、このスタックを使用して、信頼できないXMLストリームを解析するすべてのシステムです。例えば、XMPPサーバーやコンポーネントなどです。

影響を受けるパッケージ:
- pip (Python) Twisted: 影響を受けるバージョンは26.4.0以下、修正は26.10.0rc1で実施済み。