TL;DR: Wir haben eine weitere verzögerte RCE in MECCHA CHAMELEON gefunden. Beim Spielen auf einer Map eines Angreifers kann dieser eine freigegebene Funktion ausnutzen, um beliebig Dateien auf dem System des Opfers zu schreiben. Dies kann nach einem Neustart zu einer Remote Code Execution (RCE) auf dem System des Opfers führen. Wir haben das Problem den Entwicklern des Spiels gemeldet, und sie haben die Schwachstelle im Update 4.0.0 behoben. Dieses Update wird vor dem Start des Spiels automatisch installiert.
Wenn Sie gerne spielen, haben Sie wahrscheinlich von MECCHA CHAMELEON gehört. Das Online-Versteckspiel hat sich zu einer globalen Obsession entwickelt und sich im ersten Monat 15 Millionen Mal verkauft!
Als Spieler haben Sie eine sehr einfache Aufgabe: Finden Sie ein gutes Versteck und bemalen Sie einen weißen Charakter passend zur Umgebung. Dann macht sich einer Ihrer Freunde auf die Suche nach Ihnen. Sie gewinnen, wenn der Sucher Sie nicht findet. So einfach ist das!

Von der Community erstellte Maps tragen maßgeblich zum Reiz bei. Jeder kann eine neue Map erstellen, in der sich Spieler verstecken können, und diese über den Steam Workshop veröffentlichen. Diese neuen Maps können Kunstgalerien, nostalgische Bereiche aus anderen Spielen oder Ähnliches sein.

Ein bestimmtes Feature ist uns sofort aufgefallen: Wenn man auf einem Server spielt, wählt der Lobby-Host die Map aus. Alle Spieler in dieser Lobby werden daraufhin aufgefordert, diese Map herunterzuladen (der Bildschirm weist einen sogar darauf hin, wenn man den Download noch nicht abgeschlossen hat und das Spiel aufhält!). Das ist sehr praktisch, bedeutet aber auch, dass der Beitritt zu einem Match das Laden einer Map beinhalten kann, die von jemandem erstellt wurde, den man noch nie zuvor getroffen hat. Wir wollten herausfinden, was diese heruntergeladene Map eigentlich tun durfte.
In der Vergangenheit war MECCHA CHAMELEON bereits von einer RCE beim Laden einer Map betroffen, wie in diesem Blogbeitrag im Detail beschrieben wird. Dieser spezifische Bug wurde inzwischen zwar gepatcht, aber wurden damit auch alle Wege zu einer RCE beseitigt?
Was ist eine MECCHA CHAMELEON-Map eigentlich?
Man könnte annehmen, dass eine Map nur aus einigen Assets besteht: Modellen für die Szenerie und Texturen, damit alles stimmig aussieht. So funktioniert eine Map in vielen Spielen, bei denen die heruntergeladene Datei das Level beschreibt, während das Spiel selbst die Logik steuert.
MECCHA CHAMELEON-Maps bieten mehr Kontrolle als das. Das Spiel basiert auf der Unreal Engine 5.6.1, und seine Maps können Blueprints enthalten. Blueprint ist das visuelle Programmiersystem von Unreal. Man kann Nodes miteinander verknüpfen, damit etwas passiert, wenn ein Spieler eine Taste drückt, einen Bereich betritt oder einfach die Map lädt.

Natürlich kann eine Map nicht jede beliebige Funktion innerhalb von Unreal oder MECCHA CHAMELEON aufrufen. Eine native Funktion muss zunächst für Blueprint freigegeben (exposed) werden. Wenn die Map startet, werden ihre Graphen im Prozess von MECCHA CHAMELEON ausgeführt. Ruft ein Graph eine freigegebene Funktion auf, führt das Spiel diese Aktion unter Verwendung des Benutzerkontos und der Berechtigungen des Spielers aus.
Das bedeutet, dass benutzerdefinierte Maps eine interessante Attack Surface darstellen:
- Ein Lobby-Host kann alle anderen Spieler dazu auffordern, eine Map herunterzuladen und auszuführen.
- Diese Map kann dann Blueprint-Graphen innerhalb des Spiels ausführen.
Also begann ich, mein Augenmerk auf die Engine-Funktionen zu richten, die ich erreichen konnte.
Aufrufenbare Funktionen finden
Glücklicherweise müssen wir nicht raten, welche Funktionen von Unreal bereitgestellt werden. Wir können einen Blueprint-Graphen öffnen und einen Rechtsklick auf einen leeren Bereich machen. Nun zeigt Unreal ein durchsuchbares Menü von Nodes an, die diesem Graphen hinzugefügt werden können.

Aus Sicherheitssicht war ich hauptsächlich an Funktionen interessiert, die auf irgendeine Weise mit dem Betriebssystem interagierten. Daher suchte ich nach solchen, die Folgendes enthalten: save, export, write, record, open, launch, und load. Wir gelangten von einer riesigen Liste von APIs zu einer überschaubaren Liste, die ich genauer untersuchen konnte.
Also untersuchte ich diese Funktionen eine nach der anderen, bis ich auf Folgendes stieß: Finish Recording Output. Diese fiel auf, weil sie eine Datei im Dateisystem mit einem absoluten oder relativen Pfad erstellen konnte. Außerdem speichert sie Audio-Output, den wir höchstwahrscheinlich auf die eine oder andere Weise kontrollieren können.

Beliebiges Schreiben von Dateien
Um also die Finish Recording Output Funktion zu verwenden, sollte ein Map-Creator eine Aufzeichnung starten, einen Sound abspielen und die Aufzeichnung dann stoppen. Anschließend geben wir einen Namen und einen Pfad zum Speichern an. Die Funktion ist weiter dokumentiert hier.

Das ist also ein klarer Fall von „Übergeben Sie einfach ein beliebiges Verzeichnis und einen Dateinamen, und wir können diese Dateien überall im System speichern“, oder?
Nun ja, Sie können einfach C:\Users\Public\ als Dateipfad angeben und dort Dateien schreiben, oder Sie verwenden einen relativen Pfad. Das standardmäßig verwendete Verzeichnis ist C:\Users\<user>\AppData\Local\Chameleon\Saved\BouncedWavFiles, sodass wir durch die Angabe eines Pfads, der mit ../../../../../beginnt, zum Home-Verzeichnis des Benutzers gelangen können, ohne dessen Benutzernamen kennen zu müssen.
Wir könnten nun also Dateien an beliebigen Orten im Dateisystem speichern. Der einfachste Weg, dies in eine verzögerte Remotecodeausführung (RCE) zu eskalieren, besteht darin, eine ausführbare Datei nach C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startupzu schreiben. Immer wenn Ihr Gerät hochfährt, werden automatisch alle Dateien in diesem Ordner ausgeführt.
Wir haben diesen Wert als Aufnahmenamen übergeben:
..\..\..\..\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\poc.exeUnd als wir unsere Map verpackt und ausgeführt haben, stellten wir fest, dass...

Es hat quasi funktioniert? Wir haben tatsächlich eine Datei in den Autostart-Ordner geschrieben. Aber unsere .exe-Endung ist nicht erhalten geblieben. Die Unreal Engine fügt jedem von uns angegebenen Dateinamen die Endung .wav hinzu, weshalb Windows die Datei nach wie vor als WAV-Datei und nicht als ausführbare Datei behandelt.
Entfernen des .wav-Suffix
Ich habe eine Weile darüber nachgedacht, wie wir das umgehen könnten. Was wir hier haben, ist ein klassischer Fall von zwei unterschiedlichen Systemen, die Annahmen treffen. Die Unreal Engine handhabt einen Pfad auf ihre Weise, und Windows interpretiert einen Pfad auf eine andere. Ich musste lediglich eine Diskrepanz zwischen beiden finden.
Für die Unreal Engine ist das unglaublich einfach: Sie sieht einfach einen String und hängt .wav dran. Windows ist da etwas komplexer. Windows hat maximale Dateinamenlängen und Zeichen, die in einem Pfad unzulässig sind, wie etwa das Null-Zeichen (U+0000).
Aber was passiert, wenn wir versuchen, eine Datei zu erstellen, deren Name ein Null-Zeichen enthält? Nun, in den meisten Fällen erhalten Sie einen Fehler:

Aber nicht jede API wirft einen Fehler. Einige APIs auf niedriger Ebene interpretieren die Null-Bytes tatsächlich als Abschlusszeichen und verwerfen alles, was darauf folgt:

Was passiert also, wenn wir versuchen, unsere schädliche Map zu verwenden, um eine Aufnahme zu speichern unter:
POC.exe\u0000IGNORED
Wir sehen, dass es funktioniert hat! Wir können nun Dateien mit beliebigen Namen an beliebigen Orten im Dateisystem speichern!
Aber ein großes Problem bleibt bestehen. Wir schreiben lediglich die Bytes einer WAV-Audioaufnahme in eine Datei mit einer .exe Endung. Wir müssen irgendwie auch den Inhalt dieser Datei kontrollieren, um sie ausführbar zu machen!

Schädliche Bytes in einer Audioaufnahme
Wir standen nun vor einer neuen Herausforderung. Wir speicherten eine echte Audioaufnahme als WAV-Datei an einem beliebigen Ort. Damit dieser Exploit jedoch funktioniert, müssen wir auch steuern können, was in diese Datei geschrieben wird.
Um das zu erreichen, verwenden wir eine unkomprimierte PCM-WAV-Datei. WAV ist das Dateicontainer, das einige Metadaten gefolgt von Audiodaten enthält. WAV-Dateien können Audio in verschiedenen Formaten speichern, daher sagt uns die .wav Erweiterung allein nicht, wie die Samples codiert sind.
PCM steht für Puls-Code-Modulation. Bei PCM wird jede Messung der Schallwelle direkt als Zahl gespeichert. Unser Proof of Concept nutzte 16-Bit-PCM, sodass jedes Sample exakt zwei Byte belegte. „Unkomprimiert“ bedeutet, dass diese Zahlen geschrieben wurden, ohne sie durch einen Codec wie MP3 zu schleusen.
Eine unkomprimierte PCM-WAV-Datei besteht aus Audiosample-Werten. Wenn wir diese Zahlen präzise kontrollieren, kontrollieren wir auch die Bytes, die zu ihrer Speicherung verwendet werden.
Das ist sehr wichtig, da normale Audiokomprimierung versucht, die wahrgenommene Klangqualität zu erhalten und nicht die exakten Bytes jedes Samples. Eine komprimierte Version kann also vollkommen identisch klingen, während sie völlig andere Bytes enthält.
Eine vereinfachte WAV sieht wie folgt aus:
+-------------------------------+
| RIFF/WAVE-Header | Erstellt von Unreal
| Format, Kanäle, Abtastrate |
| Größe des Datenbereichs |
+-------------------------------+
| PCM-Sample 0 | Gesteuert über die Schallwelle
| PCM-Sample 1 |
| PCM-Sample 2 |
| ... |
Unreal steuert den Header einschließlich der RIFF und WAVE Kennungen. Wir können diese nicht modifizieren, da unsere Kontrolle über die Datei erst im PCM-Datenbereich beginnt. Das schließt viele ausführbare Dateien für unseren Exploit aus. Wir können kein normales .exe oder .dllhochladen, da beide einen gültigen PE-Header benötigen, um ausgeführt zu werden.
Wir benötigten daher einen Dateityp, dessen Parser nicht darauf bestand, seinen Header beim ersten Byte zu finden. Windows HTML Applications erwiesen sich hierfür als perfekte Wahl.
Ein HTML Application ist ein altes Windows-Format mit der .hta Erweiterung. Die Datei sieht im Wesentlichen wie eine Webseite aus: Sie kann HTML, CSS sowie entweder JScript oder VBScript enthalten. Windows öffnet sie mit mshta.exe (Microsoft HTML Application Host).
Der entscheidende Unterschied zu einer normalen Webseite liegt darin, wo dieses Skript ausgeführt wird. Eine HTA wird wie eine Desktop-Anwendung behandelt und nicht wie eine Seite innerhalb der Browser-Sandbox. Ihr Skript kann Windows-COM-Objekte erstellen, einschließlich WScript.Shell, womit ein weiterer Prozess gestartet werden kann.
Eine vereinfachte Version unseres Proof-of-Concept-Payloads sieht so aus:
[RIFF/WAVE header ...]
<html>
<head>
<hta:application />
<script>
new ActiveXObject("WScript.Shell").Run("calc.exe");
</script>
</head>
</html>
Wenn diese Datei geöffnet wird, mshta.exe werden die WAV-Metadaten am Anfang ignoriert und das Skript ausgeführt. Anschließend WScript.Shell wird der Taschenrechner gestartet.
Die verbleibende Herausforderung bestand darin, dieses HTML und Skript in den PCM-Samples der WAV-Datei zu platzieren.
Our payload used 16-bit PCM. Each sample is a 16-bit number stored in little-endian order, meaning the least significant byte is written first. Take the sample value 0x683C: it appears in the WAV as 3C 68, which also happens to be the ASCII text <h.
The same conversion works for a longer string. The text <html> has these ASCII bytes:
3C 68 74 6D 6C 3EDurch die Gruppierung in Paare erhalten wir drei 16-Bit-Sample-Werte:
Bytes: 3C 68 | 74 6D | 6C 3E
Samples: 0x683C 0x6D74 0x3E6C
Wenn diese Samples wieder in eine Little-Endian-WAV-Datei geschrieben werden, erscheinen die ursprünglichen Bytes wieder. In der Realität war viel Feintuning nötig, um das Ganze zum Laufen zu bringen, aber schlussendlich ist es uns gelungen, eine echte WAV-Datei zu erstellen, die unseren hta Payload enthielt.

Die Map zusammenfügen
Zu diesem Zeitpunkt funktionierte jeder Teil für sich allein. Die verbleibende Aufgabe bestand darin, alles in einer Map zusammenzuführen, die den vollständigen Angriff beim Laden des Levels auf einmal ausführen konnte.
Also hängen wir uns an BeginPlay und führen Folgendes aus:
StartRecordingOutputauf einem privaten Submix (um Interferenzen mit dem normalen Spiel-Audio zu vermeiden)PlaySound2Dmit unserem vorbereiteten unkomprimierten PCMStopRecordingOutputVerwenden../../../../Roaming/Microsoft/Windows/Start Menu/Programs/Startup/poc.exe\u0000IGNOREDals Dateinamen

Wir haben die Map erstellt und (privat!) auf Steam hochgeladen.

Und das ist das Ergebnis in diesem Proof-of-Concept-Video:
Ist MECCHA CHAMELEON jetzt also sicher?
Die von uns gemeldete Schwachstelle wurde behoben und kann nicht mehr ausgenutzt werden. Die StopRecordingOutput Funktion erstellt keine Dateien mehr (selbst beabsichtigte Dateien nicht).
Wir haben zudem alle Steam Workshop-Maps überprüft, um festzustellen, ob bereits welche dieses Problem ausnutzten, aber keine entsprechenden gefunden.
Während der Arbeit an diesem PoC wandten sich jedoch mehrere Personen an uns, um uns mitzuteilen, dass sie möglicherweise weitere Schwachstellen in MECCHA CHAMELEON entdeckt haben. Sie nannten keine Details, und diese wurden von uns weder nachgewiesen noch überprüft.
Wir fordern Sie daher auf, beim Spielen mit benutzerdefinierten Maps vorsichtig und wachsam zu sein. Wir empfehlen, ausschließlich auf den offiziellen Maps zu spielen, die von MECCHA CHAMELEON bereitgestellt werden.
Zeitplan der Offenlegung
- 11. August 2026: Versuch der Kontaktaufnahme über Twitter
- 12. August 2026: Versuch der Kontaktaufnahme über Discord
- 13. August 2026: Versuch der Kontaktaufnahme über den Steam-Support und die E-Mail-Adresse der Entwickelnden
- 17. August 2026: Erneute Kontaktaufnahme über Twitter gelungen. Die entwickelnde Person antwortete, dass das Problem behoben werde.
- 20. August 2026: MECCHA CHAMELEON 4.0.0 wurde mit einem Patch für diese Schwachstelle veröffentlicht