Files
HIP7CTF_Writeups/de/render_me_this.md
m0rph3us1987 a79656b647 Added writeups
2026-03-08 12:22:39 +01:00

109 lines
4.9 KiB
Markdown

# Render Me This
Willkommen zum Write-up für **Render Me This**. Diese Challenge fällt in die Kategorie "Web" und demonstriert eine schwerwiegende Schwachstelle in modernen Web-Frameworks, bekannt als **Server-Side Template Injection (SSTI)**.
Uns wird eine "Profile Viewer"-Anwendung präsentiert, die den Namen eines Benutzers entgegennimmt und eine benutzerdefinierte Begrüßung rendert. Unser Ziel ist es, die Rendering-Engine auszunutzen, um die Flagge vom Server zu lesen.
---
## 1. Erste Erkundung
Die Challenge stellt uns eine URL und den Quellcode zur Verfügung. Wenn wir die Seite besuchen, sehen wir eine einfache Seite, die "Guest" begrüßt.
Die URL sieht wahrscheinlich so aus:
`http://challenge-url/?name=Guest`
Wenn wir den Parameter `name` zu `Test` ändern, aktualisiert sich die Seite zu "Hello, Test!". Dies bestätigt, dass unsere Eingabe auf der Seite reflektiert wird.
## 2. Quellcode-Analyse
Untersuchen wir die bereitgestellte `app.py`, um zu verstehen, wie die Seite generiert wird.
```python
@app.route('/')
def index():
# Get the 'name' parameter
name = request.args.get('name', 'Guest')
# 1. Check for Blacklisted words
for bad_word in BLACKLIST:
if bad_word in name.lower():
return "Hacker detected! ..."
# 2. Vulnerable Template Construction
template = f'''
...
<h1>Hello, {name}!</h1>
...
'''
# Render the template
return render_template_string(template)
```
### Die Schwachstelle: SSTI
Der kritische Fehler liegt darin, wie der `template`-String konstruiert wird. Der Entwickler verwendet einen Python f-String (`f'''... {name} ...'''`), um die Benutzereingabe *direkt in den Template-Quellcode* einzufügen, bevor er ihn an `render_template_string` übergibt.
In Flask (Jinja2) wird `{{ ... }}` verwendet, um Code innerhalb eines Templates auszuführen. Durch Injizieren von `{{ 7*7 }}` können wir den Server bitten, 7*7 zu berechnen. Wenn die Seite "49" anzeigt, haben wir Codeausführung.
### Das Hindernis: Die Blacklist
Die Anwendung versucht, sich mit einer Blacklist zu sichern:
`BLACKLIST = ["config", "self", "flag"]`
Das bedeutet, wir können nicht die Standard-SSTI-Payloads wie `{{ config }}` oder `{{ self.__dict__ }}` verwenden. Wir können auch nicht einfach `cat flag.txt` ausführen, weil das Wort "flag" verboten ist.
## 3. Entwicklung des Exploits
Wir müssen einen Weg finden, auf das Python `os`-Modul zuzugreifen, um Systembefehle auszuführen, ohne die gesperrten Wörter zu verwenden.
In Python-Web-Frameworks wie Flask ist das `request`-Objekt oft im Template-Kontext verfügbar. Über `request` können wir die Python-Objekthierarchie durchlaufen, um den globalen Geltungsbereich zu erreichen und Module zu importieren.
**Schritt 1: Zugriff auf Built-ins**
Wir können das `request`-Objekt verwenden, um auf den globalen Geltungsbereich zuzugreifen:
`request.application.__globals__`
Von dort aus können wir auf die eingebauten Funktionen von Python zugreifen:
`request.application.__globals__.__builtins__`
**Schritt 2: Importieren von OS**
Jetzt können wir die `__import__`-Funktion verwenden, um das `os`-Modul zu laden:
`request.application.__globals__.__builtins__.__import__('os')`
**Schritt 3: Ausführen von Befehlen**
Mit dem `os`-Modul können wir `popen` verwenden, um Shell-Befehle auszuführen, und `read`, um die Ausgabe zu erhalten:
`.popen('ls').read()`
**Schritt 4: Umgehung des "flag"-Filters**
Wenn wir versuchen, `cat flag.txt` auszuführen, blockiert uns die Anwendung, weil es "flag" enthält.
Wir können dies mit Shell-Wildcards umgehen. Statt `flag.txt` können wir `fl*` sagen.
`cat fl*` passt auf `flag.txt`, enthält aber nicht den verbotenen String "flag".
## 4. Der finale Payload
Alles zusammen sieht unser Payload so aus:
`{{ request.application.__globals__.__builtins__.__import__('os').popen('cat fl*').read() }}`
Wir müssen diesen Payload URL-kodieren, bevor wir ihn an den Server senden.
**Kodierte URL:**
`?name=%7B%7B%20request.application.__globals__.__builtins__.__import__(%27os%27).popen(%27cat%20fl*%27).read()%20%7D%7D`
## 5. Die Lösung
Das Senden des Payloads an den Server führt den Befehl aus, liest die Flaggen-Datei und rendert das Ergebnis auf der Seite.
**Flag:** `{flag:SSTI_Is_Pow3rful_Even_With_Basic_Filters}`
---
## Gelernte Lektionen
* **Kontext ist wichtig:** Verketten Sie niemals Benutzereingaben direkt in einen Template-String.
* **Verwenden Sie das Framework korrekt:** Übergeben Sie Daten immer als Kontextvariablen an die Render-Funktion.
* *Verwundbar:* `render_template_string(f"Hello {name}")`
* *Sicher:* `render_template_string("Hello {{ name }}", name=user_input)`
* **Blacklists versagen:** Der Versuch, bestimmte Wörter ("flag", "config") zu blockieren, ist selten effektiv. Hacker können fast immer einen Weg finden, sie zu umgehen (z.B. String-Verkettung, Kodierung, Wildcards).
Frohes Hacken!