
Bloomberg Memrays Stored XSS über nicht maskierte Befehlszeilen-Metadaten
Bloomberg Memray: Stored XSS über nicht escapte Befehlszeilen-Metadaten
Ich stieß auf dieses Problem, als ich Memray, den Python-Speicherprofiler von Bloomberg, mit einer einfachen Frage im Hinterkopf untersuchte:
Können angreiferkontrollierte Laufzeit-Metadaten unsicher in browser-gerenderte Berichtsausgaben gelangen?
In diesem Fall lautete die Antwort: ja.
Der Fehler lag im Pfad zur HTML-Berichtserstellung von Memray, wo Befehlszeilen-Metadaten ohne Escaping in einen im Browser geöffneten Bericht gerendert wurden. Dadurch wurde ein operatives Feld zu einer ausführbaren HTML-Senke, was letztendlich zu CVE-2026-32722 führte.
Projekt: Memray auf GitHub
Advisory: 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 ist ein Python-Speicherprofiler.
Er instrumentiert einen Python-Prozess, zeichnet das Allokationsverhalten auf und erstellt Berichte, die Entwicklern helfen zu verstehen:
Einige dieser Berichte werden als HTML ausgegeben und im Browser geöffnet.
Das macht die Berichtserstellung zu einer echten Sicherheitsgrenze.
Die relevante Frage ist nicht, ob Memray ein „lokales Tool“ ist.
Die relevante Frage ist, ob angreiferkontrollierte Daten unsicher in browser-gerenderte Ausgaben gelangen können.
In diesem Fall konnten sie das.
Viele unterschätzen Entwicklerwerkzeuge.
Das ist ein Fehler.
Sobald ein Tool:
übernimmt es dieselben Risiken bei der Ausgabe-Kodierung wie eine Webanwendung.
Das war hier das Kernproblem.
Dieser Fehler lag nicht in der Profiling-Logik. Er lag nicht in der Allokationsverfolgung. Er lag nicht in der nativen Speicherverwaltung.
Es war ein klassischer Fehler an der Vertrauensgrenze:
Das reicht aus, um eine echte Sicherheitslücke zu erzeugen.
Ich bin nicht mit Fuzzing zufälliger CLI-Optionen oder der Jagd nach Abstürzen an Memray herangegangen.
Der stärkere Ansatz war, zuerst die Sicherheitsoberfläche mit der höchsten Erfolgswahrscheinlichkeit zu identifizieren.
Für Memray war das die HTML-Berichtserstellung.
Warum?
Weil HTML-Ausgaben eine Browser-Senke einführen und Browser-Senken gewöhnliche Metadatenfehler in Sicherheitsprobleme verwandeln, wenn:
Genau das ist hier passiert.
Der Fehler lässt sich auf zwei Zeilen reduzieren.
In:
def get_render_environment() -> jinja2.Environment:
loader = jinja2.PackageLoader("memray.reporters")
env = jinja2.Environment(loader=loader)
wird die Jinja-Umgebung ohne Autoescape erstellt.
Dann in:
Command line: <code>{{ metadata.command_line }}</code><br>
wird metadata.command_line direkt in HTML gerendert.
Das ist die gesamte Sicherheitslücke.
Weil metadata.command_line angreiferbeeinflusst ist.
Memray zeichnet die Befehlszeile auf, mit der das profilierte Programm ausgeführt wurde.
Das bedeutet, dass benutzerkontrollierte Werte aus argv als Metadaten erhalten bleiben und später in den Bericht eingefügt werden.
Die Exploit-Kette ist also unkompliziert:
metadata.command_lineDas verwandelt Metadaten in ausführbaren Browser-Inhalt.
Der wichtige Unterschied ist die Ausführung.
Viele Fehler erzeugen fehlerhaftes HTML. Das allein reicht nicht aus.
Hier war angreiferkontrollierter Inhalt nicht nur im Seitenquelltext sichtbar. Er wurde vom Browser als aktives HTML interpretiert und als JavaScript ausgeführt.
Das ist der Unterschied zwischen:
Die Frage war also nicht:
„Kann HTML im Bericht erscheinen?“
Die eigentliche Frage war:
„Kann angreiferkontrolliertes HTML ausführbar werden, wenn der Bericht geöffnet wird?“
Die Antwort war ja.
Mein erster Reproduktionsversuch bestand aus einem expliziten Skript plus einem angreiferkontrollierten Argument:
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
Das erzeugte HTML enthielt rohes, angreiferkontrolliertes Markup:
Command line: <code>~/memray/src/memray/__main__.py run -o poc.bin victim.py </code><br>
Das Öffnen oder Aktualisieren des erzeugten Berichts löste die JavaScript-Ausführung aus.
Das belegte die Kernaussage:
Später, während der koordinierten Offenlegung, vereinfachte der Maintainer den Reproduktionsversuch weiter:
python -m memray run -o poc.bin -c '# '
Diese Version ist besser, weil sie die anfällige Grenze direkter isoliert:
Das Payload war absichtlich einfach:
Es geht nicht um ausgefallene Payloads.
Es ist ein sauberer Ausführungstest, weil:
Für diese Klasse von Fehlern reicht das.
Ein einziger Berichtstyp hätte bereits ausgereicht, um das Problem zu rechtfertigen.
Aber ich wollte wissen, ob das Problem isoliert oder strukturell war.
Ich bestätigte dasselbe Verhalten bei:
--no-web erzeugten Flamegraph-BerichtenDas war aus zwei Gründen wichtig.
Es zeigte, dass die anfällige Senke über mehrere HTML-Ausgaben hinweg wiederverwendet wurde.
Es bewies, dass der Fehler nicht von externen, auf CDNs gehosteten Ressourcen abhing.
--no-web reproduzierte das Problem weiterhin, was bedeutet, dass das Problem in Memrays eigenem generierten HTML und der Template-Verarbeitung lag, nicht im Verhalten von Remote-JS.
Das machte den Fall deutlich stärker.
Die Hauptsorge des Maintainers war die praktische Kontrolle durch den Angreifer.
Das ist fair.
Dies ist nicht die Art von Problem, bei der ein nicht authentifizierter Remote-Angreifer einen exponierten HTTP-Endpunkt trifft und sofortige Auswirkungen erzielt.
Die Ausnutzungsbedingung ist enger:
Die realistische Einstufung war also ein niedriger Schweregrad.
Das macht es nicht zu einem schwachen Fehler.
Beim Schweregrad geht es um Ausnutzungsbedingungen und wahrscheinliche Auswirkungen. Bei der Gültigkeit geht es darum, ob das Problem real ist.
Dieses Problem war eindeutig real:
Deshalb wurde daraus trotzdem ein CVE.
Die Behebung war minimal und korrekt.
Der Maintainer änderte:
{{ metadata.command_line }}
zu:
{{ metadata.command_line|e }}
Das ist die richtige Behebung, weil sie die anfällige Senke direkt adressiert.
Anstatt rohes Markup wie dieses auszugeben:
gibt das Template nun escapten Text aus:
<img src=x onerror=alert(1)>
Das erhält den Informationswert des Befehlszeilenfelds und entfernt gleichzeitig den Browser-Ausführungspfad.
Der Maintainer prüfte auch den restlichen Template-Kontext und kam zu dem Schluss, dass:
|tojson gerendert wurdenDas Problem wurde also korrekt auf metadata.command_line eingegrenzt.
Genau eine solche Überprüfung der Behebung wünscht man sich bei einer echten Offenlegung.
Dies wurde vertraulich über GitHub Security Advisories gemeldet.
Die Maintainer:
Dem Problem wurde zugewiesen: CVE-2026-32722
Die wichtigste Lektion hier ist einfach:
Metadaten sind nicht automatisch vertrauenswürdig, nur weil sie operativ wirken.
All das spielt keine Rolle, sobald angreiferkontrollierter Inhalt ohne Escaping in browser-gerenderte Ausgaben gelangt.
In dem Moment, in dem ein Tool HTML ausgibt, muss es wie eine HTML-erzeugende Anwendung behandelt werden.
Das ist die eigentliche Erkenntnis.
Bei dieser Sicherheitslücke ging es nicht um ein raffiniertes Payload.
Es ging darum, die richtige Grenze zu identifizieren.
Memray übernahm angreiferbeeinflusste Befehlszeilen-Metadaten und renderte sie ohne Escaping in das generierte HTML. Der Browser erledigte den Rest.
Deshalb wurde daraus CVE-2026-32722.
Behoben in Memray 1.19.2.
