← Back to Writeups
CUSTOMEasyWeb

Flag Admin v1

XESXOR9/10/20262 min read
#web#custom#easy#custom_header_injection#js_deobfuscation#modpow_bruteforce

Flag Admin v1

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: custom_header_injection, js_deobfuscation, modpow_bruteforce

Summary

Walkthrough of the CUSTOM Web Flag Admin v1. URL: http://kslweb1.spb.ctf.su/burp/header/

Description

"How to add this header?" — The page instructs to send the header X-Custom-Object: flag to get the flag.

URL: http://kslweb1.spb.ctf.su/burp/header/

Analysis

The page contains a "Get the flag" button and a hint about needing to send the X-Custom-Object: flag header. The admin.js file is heavily obfuscated:

  • RC4-encrypted string array
  • Anti-debugging protection
  • String access function b(index, key)

Analysis of the deobfuscated code reveals a challenge-response protocol:

  1. Server returns a challenge (number)
  2. Client must find gamma and beta such that pow(gamma, beta) mod 31337 == challenge
  3. Server verifies the solution and returns the flag ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

Step 1: First request — getting the challenge

POST request to handler.php with parameter method=init and header X-Custom-Object: flag:

http
POST /burp/header/handler.php HTTP/1.1
Host: kslweb1.spb.ctf.su
X-Custom-Object: flag
Content-Type: application/x-www-form-urlencoded

method=init

Server returns the challenge value (a number).

Step 2: Computing the solution

Modular exponentiation implementation from JS:

python
def modpow(base, exp, mod=31337):
    if exp == 0:
        return 1
    if exp % 2 == 0:
        t = modpow(base, exp // 2, mod)
        return (t * t) % mod
    else:
        return (modpow(base, exp - 1, mod) * base) % mod

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

Bruteforce gamma (range ~200-400) and beta (range ~200-600):

python
def find_solution(challenge):
    for gamma in range(200, 400):
        for beta in range(200, 600):
            if modpow(gamma, beta) == challenge:
                return gamma, beta
    return None, None

Step 3: Second request — getting the flag

POST request with the found values:

http
POST /burp/header/handler.php HTTP/1.1
Host: kslweb1.spb.ctf.su
X-Custom-Object: flag
Content-Type: application/x-www-form-urlencoded
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

method=flag&gamma=<value>&beta=<value>

Full solve script

python
#!/usr/bin/env python3
import requests

URL = "http://kslweb1.spb.ctf.su/burp/header/handler.php"
HEADERS = {"X-Custom-Object": "flag"}

def modpow(base, exp, mod=31337):
    if exp == 0:
        return 1
    if exp % 2 == 0:
        t = modpow(base, exp // 2, mod)
        return (t * t) % mod
    else:
        return (modpow(base, exp - 1, mod) * base) % mod

def find_solution(challenge):
    for gamma in range(200, 400):
        for beta in range(200, 600):
            if modpow(gamma, beta) == challenge:
                return gamma, beta
    return None, None
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

# Step 1: Get challenge
r = requests.post(URL, headers=HEADERS, data={"method": "init"})
challenge = int(r.text.strip())
print(f"Challenge: {challenge}")

# Step 2: Find solution
gamma, beta = find_solution(challenge)
print(f"Solution: gamma={gamma}, beta={beta}")

# Step 3: Get flag
r = requests.post(URL, headers=HEADERS, data={
    "method": "flag",
    "gamma": gamma,
    "beta": beta
})
print(f"Flag: {r.text}")

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

Key Takeaways / Lessons

  • Mastering custom_header_injection, js_deobfuscation, modpow_bruteforce was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-03-11 • CUSTOM • Flag Admin v1 • Difficulty: Easy