All skills
ljagiello avatar

/ctf-misc

@61c2efe
by Lukasz Jagielloljagiello/ctf-skills3.4k stars
393

Provides miscellaneous CTF challenge techniques for problems that do not cleanly fit the main categories. Use for encoding puzzles, pyjails, bash jails, RF/SDR, DNS oddities, unicode tricks, esoteric languages, QR or audio puzzles, constraint solving, game theory, unusual sandbox escapes, and hybrid logic puzzles. Prefer a more specific skill first when the challenge is mainly web, pwn, reverse, forensics, malware, OSINT, or crypto. Treat this as the fallback skill for genuine cross-category or edge-case challenges, not the default starting point.

Use this Skill: https://skilld.dev/gh/ljagiello/ctf-skills/ctf-misc

This session only. Nothing lands on disk.

games-and-vms-2.md

≈2.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

CTF Misc - Games, VMs & Constraint Solving (Part 2)

Table of Contents


Cookie Checkpoint Game Brute-Forcing (BYPASS CTF 2025)

Pattern (Signal from the Deck): Server-side game where selecting tiles increases score. Incorrect choice resets the game. Score tracked via session cookies.

Technique: Save cookies before each guess, restore on failure to avoid resetting progress.

import requests

URL = "https://target.example.com"

def solve():
    s = requests.Session()
    s.post(f"{URL}/api/new")

    while True:
        data = s.get(f"{URL}/api/signal").json()
        if data.get('done'):
            break

        checkpoint = s.cookies.get_dict()

        for tile_id in range(1, 10):
            r = s.post(f"{URL}/api/click", json={'clicked': tile_id})
            res = r.json()

            if res.get('correct'):
                if res.get('done'):
                    print(f"FLAG: {res.get('flag')}")
                    return
                break
            else:
                s.cookies.clear()
                s.cookies.update(checkpoint)

Key insight: Session cookies act as save states. Preserving and restoring cookies on failure enables deterministic brute-forcing without game reset penalties.


Flask Session Cookie Game State Leakage (BYPASS CTF 2025)

Pattern (Hungry, Not Stupid): Flask game stores correct answers in signed session cookies. Use flask-unsign -d to decode the cookie and reveal server-side game state without playing.

# Decode Flask session cookie (no secret needed for reading)
flask-unsign -d -c '<cookie_value>'

Example decoded state:

{
  "all_food_pos": [{"x": 16, "y": 12}, {"x": 16, "y": 28}, {"x": 9, "y": 24}],
  "correct_food_pos": {"x": 16, "y": 28},
  "level": 0
}

Key insight: Flask session cookies are signed but not encrypted by default. flask-unsign -d decodes them without the secret key, exposing server-side game state including correct answers.

Detection: Base64-looking session cookies with periods (.) separating segments. Flask uses itsdangerous signing format.


WebSocket Game Manipulation + Cryptic Hint Decoding (BYPASS CTF 2025)

Pattern (Maze of the Unseen): Browser-based maze game with invisible walls. Checkpoints verified server-side via WebSocket. Cryptic hint encodes target coordinates.

Technique:

  1. Open browser console, inspect WebSocket messages and player object
  2. Decode cryptic hints (e.g., "mosquito were not available" → MQTT → port 1883)
  3. Teleport directly to target coordinates via console
function teleport(x, y) {
    player.x = x;
    player.y = y;
    verifyProgress(Math.round(player.x), Math.round(player.y));
    console.log(`Teleported to x:${player.x}, y:${player.y}`);
}

// "mosquito" → MQTT (port 1883), "not available" → 404
teleport(1883, 404);

Common cryptic hint mappings:

  • "mosquito" → MQTT (Mosquitto broker, port 1883)
  • "not found" / "not available" → HTTP 404
  • Port numbers, protocol defaults, or ASCII values as coordinates

Key insight: Browser-based games expose their state in the JS console. Modify player.x/player.y or equivalent properties directly, then call the progress verification function.


Server Time-Only Validation Bypass (BYPASS CTF 2025)

Pattern (Level Devil): Side-scrolling game requiring traversal of a map. Server validates that enough time has elapsed (map_length / speed) but doesn't verify actual movement.

import requests
import time

TARGET = "https://target.example.com"

s = requests.Session()
r = s.post(f"{TARGET}/api/start")
session_id = r.json().get('session_id')

# Wait for required traversal time (e.g., 4800px / 240px/s = 20s + margin)
time.sleep(25)

s.post(f"{TARGET}/api/collect_flag", json={'session_id': session_id})
r = s.post(f"{TARGET}/api/win", json={'session_id': session_id})
print(r.json().get('flag'))

Key insight: When servers validate only elapsed time (not player position, inputs, or movement), start a session, sleep for the required duration, then submit the win request. Always check if the game API has start/win endpoints that can be called directly.


De Bruijn Sequence for Substring Coverage (BearCatCTF 2026)

Pattern (Brown's Revenge): Server generates random n-bit binary code each round. Input must contain the code as a substring. Pass 20+ rounds with a single fixed input under a character limit.

def de_bruijn(k, n):
    """Generate de Bruijn sequence B(k, n): cyclic sequence containing
    every k-ary string of length n exactly once as a substring."""
    a = [0] * k * n
    sequence = []
    def db(t, p):
        if t > n:
            if n % p == 0:
                sequence.extend(a[1:p+1])
        else:
            a[t] = a[t - p]
            db(t + 1, p)
            for j in range(a[t - p] + 1, k):
                a[t] = j
                db(t + 1, t)
    db(1, 1)
    return sequence

# For 12-bit binary codes: B(2, 12) has length 4096
seq = ''.join(map(str, de_bruijn(2, 12)))
payload = seq + seq[:11]  # Linearize: 4096 + 11 = 4107 chars
# Every possible 12-bit code appears as a substring

Key insight: De Bruijn sequence B(k, n) contains all k^n possible n-length strings over alphabet k as substrings, with cyclic length k^n. To linearize (non-cyclic), append the first n-1 characters. Total length = k^n + n - 1. Send the same string every round — it contains every possible code.

Detection: Must find arbitrary n-bit pattern as substring of limited-length input. Character budget matches de Bruijn length (k^n + n - 1).


Brainfuck Interpreter Instrumentation (BearCatCTF 2026)

Pattern (Ghost Ship): Large Brainfuck program (10K+ instructions) validates a flag character-by-character. Full reverse engineering is impractical.

Per-character brute-force via instrumentation:

  1. Instrument a Brainfuck interpreter to track tape cell values
  2. Identify a "wrong count" cell that increments per incorrect character
  3. For each position, try all printable ASCII — pick the character that doesn't increment the wrong counter
def run_bf_instrumented(code, input_bytes, max_steps=500000):
    tape = [0] * 30000
    dp, ip, inp_idx = 0, 0, 0
    for _ in range(max_steps):
        if ip >= len(code): break
        c = code[ip]
        if c == '+': tape[dp] = (tape[dp] + 1) % 256
        elif c == '-': tape[dp] = (tape[dp] - 1) % 256
        elif c == '>': dp += 1
        elif c == '<': dp -= 1
        elif c == '.': pass  # output
        elif c == ',':
            tape[dp] = input_bytes[inp_idx] if inp_idx < len(input_bytes) else 0
            inp_idx += 1
        elif c == '[' and tape[dp] == 0:
            # skip to matching ]
            ...
        elif c == ']' and tape[dp] != 0:
            # jump back to matching [
            ...
        ip += 1
    return tape

# Brute-force: ~40 positions × 95 chars = 3800 runs
flag = []
for pos in range(40):
    for c in range(32, 127):
        candidate = flag + [c] + [ord('A')] * (39 - pos)
        tape = run_bf_instrumented(code, candidate)
        if tape[WRONG_COUNT_CELL] == 0:  # No errors up to this position
            flag.append(c)
            break

Key insight: Brainfuck programs that validate input character-by-character can be brute-forced without understanding the program logic. Instrument the interpreter to observe tape state, find the cell that tracks validation progress, and optimize per-character search. ~3800 runs completes in minutes.


WASM Linear Memory Manipulation (BearCatCTF 2026)

Pattern (Dubious Doubloon): Browser game compiled to WebAssembly with win conditions requiring luck (e.g., 15 consecutive coin flips). WASM linear memory is flat and unprotected.

Direct memory patching in Node.js:

const { readFileSync } = require('fs');
const wasmBuffer = readFileSync('game.wasm');
const { instance } = await WebAssembly.instantiate(wasmBuffer, imports);
const mem = new DataView(instance.exports.memory.buffer);

// Patch game variables at known offsets
mem.setInt32(0x102918, 14, true);   // streak counter = 14 (need 15)
mem.setInt32(0x102898, 100, true);  // win chance = 100%

// One more flip → guaranteed win → flag decoded
const result = instance.exports.flipCoin();

Key insight: Unlike WAT patching (modifying the binary), memory manipulation patches runtime state after loading. All WASM variables live in flat linear memory at fixed offsets. Use wasm-objdump -x game.wasm or search for known constants to find variable offsets. No need to understand the full game logic — just set the state to "about to win".

Detection: WASM game requiring statistically impossible sequences (streaks, perfect scores). Game logic is in .wasm file loadable in Node.js.


References

  • BYPASS CTF 2025 "Signal from the Deck": Cookie checkpoint game brute-forcing
  • BYPASS CTF 2025 "Hungry, Not Stupid": Flask cookie game state leakage
  • BYPASS CTF 2025 "Maze of the Unseen": WebSocket teleportation + cryptic hints
  • BYPASS CTF 2025 "Level Devil": Server time-only validation bypass
  • BearCatCTF 2026 "Brown's Revenge": De Bruijn sequence substring coverage
  • BearCatCTF 2026 "Ghost Ship": Brainfuck instrumentation brute-force
  • BearCatCTF 2026 "Dubious Doubloon": WASM linear memory state patching

See also: games-and-vms.md for WASM patching, Roblox reversing, PyInstaller, Z3, K8s RBAC, floating-point exploitation, custom assembly sandbox escape, and multi-phase crypto games.

Source: SKILL.md on GitHub

3 alerts17d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub17d

    The skill is a comprehensive reference and cheat sheet repository for Capture The Flag (CTF) miscellaneous challenges, including jail escapes, cryptography puzzles, and Linux privilege escalation techniques. While automated scanners flag signatures of exploit payloads and shell patterns within the documentation, these are entirely educational examples and static reference material aligned with the primary purpose of the skill.

  • Socket17d

    2 alerts: gptSecurity, gptMalware

  • Snyk17d

    Risk: HIGH · 1 issue

  • Runlayer6mo

    5/7 files flagged

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at 61c2efe. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 3 weeks ago
metadata
{
  "user-invocable": "false"
}
All 1 allowed tools
Bash Read Write Edit Glob Grep Task WebFetch WebSearch Skill
Other metadata
compatibility
Requires filesystem-based agent (Claude Code or similar) with bash, Python 3, and internet access for tool installation.

README badge

README badge for ljagiello/ctf-skills/ctf-misc