GPN CTF 2026 writeup — 19 challenges solved across reverse, crypto, web, pwn, and misc

GPN CTF 2026 Writeup: All 19 Challenges Solved

Platform GPN CTF 2026 (kitctf) Difficulty Mixed (Easy → Hard) OS Jeopardy — Reverse, Crypto, Web, Pwn, Misc Tags AVX2 lane-swap miscompilation discovery + Kannan-embedding SIS lattice attack, NTRU mod-q reduction bug (c mod p == m), ECDSA nonce reuse from MD5(uuid3) collisions via fastcoll, eBPF signed-comparison verifier bypass with patched bzImage, JVM AOT cache override of bytecode, PHP 7.4 PHAR deserialization across two TCP races, Pydantic ForwardRef eval in create_model, CSS attribute-selector cookie exfiltration through Link: rel=stylesheet, holpy proof-checker thm re-axiomatization, knitout front/back-bed bitmap, ternary amplitude-modulated UART, Hamiltonian path on 250-node FSM extracted from jump tables, RFC 5424 syslog stream demux, Rust setuid TOCTOU symlink swap GPN CTF 2026 is the Gulaschprogrammiernacht CTF hosted annually by KITCTF at the GPN hacker camp in Karlsruhe, Germany. The 2026 edition runs a Jeopardy board across reverse engineering, crypto, web, pwn, and misc, with a sharp lean toward low-level systems bugs — a missing mod q in an NTRU implementation, a 4-way AVX2 lane-swap in a gcc -O3 -mavx2 build, a deleted BPF_ADJ_END_FROM_* check in a custom kernel, a JVM AOT cache that silently overrides a JAR method. The flavour throughout is kitchen — recipes, ovens, pots — and the flags universally read like Bavarian beer-tent slogans. ...

June 7, 2026 · 32 min · 6647 words · CyberSecurity Elite Team
GPN CTF 2026 Recipe for Disaster writeup — gets() overflows char note[32] into an adjacent int price field

GPN CTF 2026 — Recipe for Disaster: gets() Overflow into an Adjacent int

Platform GPN CTF 2026 (kitctf) Difficulty Easy OS Pwn — stack buffer overflow, adjacent-field overwrite Tags reading the Item struct layout to confirm note and price are adjacent with no padding, sending 32 bytes of A plus four bytes of 0xff to set price to -1, observing verify_total trigger print_coupon, recognising gets() as a deprecated-in-C11 vulnerability since the Morris Worm (1988) Recipe for Disaster is the GPN CTF 2026 pwn challenge, and the most direct teaching example you’ll ever see of why gets was removed from C11. A note-taking program reads into a 32-byte note field with gets() — no length limit. Type 35 characters and the 33rd through 36th overflow into the adjacent int price field in the same Item struct. Set price = -1 and verify_total() triggers print_coupon() → flag. The flag itself names the lesson: ...

June 7, 2026 · 6 min · 1104 words · CyberSecurity Elite Team
GPN CTF 2026 Customer Service writeup — three bugs stacked in a holpy proof checker yield ex contradictione quodlibet

GPN CTF 2026 — Customer Service: Three Bugs in a holpy Proof Checker

Platform GPN CTF 2026 (kitctf) Difficulty Medium-Hard OS Misc — holpy proof checker, LCF-style higher-order logic Tags reading 115 lines of checker glue around holpy's monitor, spotting list == 1 as a dead branch, recognising Theorem.get_extension always re-axiomatizes, exploiting concl.is_const('false') as a name comparison in an EmptyTheory scope, declaring a homemade false constant and proving it with a one-line theorem rule Customer Service is the GPN CTF 2026 misc challenge that builds an LCF-style higher-order-logic proof assistant from holpy, pretends to forbid axioms, and gets its guard wrong three ways. The exploit is a three-item JSON payload: declare a false constant of our own, axiomatize it, ship a one-line proof. The flag spells the lesson: ...

June 7, 2026 · 7 min · 1489 words · CyberSecurity Elite Team
GPN CTF 2026 Organized writeup — ternary amplitude-modulated UART hidden in per-12,500-byte window popcount density

GPN CTF 2026 — Organized: Ternary Amplitude-Modulated UART in Popcount Density

Platform GPN CTF 2026 (kitctf) Difficulty Medium OS Misc — signal recovery from high-entropy file, ternary amplitude modulation Tags rejecting the 'random noise' default hypothesis, computing per-window popcount means and run-lengths, plotting a 200-bin histogram to spot three peaks instead of two, recognising the structure as UART (start bit, 8 data bits LSB-first, stop bit) framed by a mid-amplitude idle marker, decoding 49 frames to ASCII Organized is the GPN CTF 2026 misc challenge whose entire trick is recognising the carrier’s organization. The handout is a 7,650,000-byte file that looks like noise — file(1) calls it data, every bit position is 1 with probability ≈ 0.287. The “organization” is hidden in the bit-density of windows, not in the bytes themselves: per-12,500-byte window popcount falls into one of three sharp levels, giving a 612-trit string. Past a 24-trit preamble, the rest is 49 UART-style frames of 12 trits each. Decode → ASCII → flag: ...

June 7, 2026 · 7 min · 1433 words · CyberSecurity Elite Team
GPN CTF 2026 Pharry writeup — PHP 7.4 PHAR deserialization via md5_file vs file_get_contents two-connection race

GPN CTF 2026 — Pharry: PHP 7.4 PHAR Deserialization via Two-Connection Race

Platform GPN CTF 2026 (kitctf) Difficulty Medium-Hard OS Web — PHP 7.4, PHAR metadata unserialize, two-TCP-connection trick Tags recognising md5_file and file_get_contents both open separate TCP connections to a URL, hosting a connection-counting HTTP server that returns different responses per attempt, crafting a PHAR with a User object metadata that fires system() in __destruct, triggering PHAR unserialize via md5_file(phar:///tmp/...) for RCE Pharry is the GPN CTF 2026 PHP challenge that turns a md5_file + file_get_contents integrity-check pair into a PHAR deserialization. Both PHP functions open separate TCP connections to the URL they’re given, so a counting server can serve one response to md5_file (close to make it return FALSE) and another to file_get_contents (return a PHAR). The PHAR ends up at /tmp/remote_file.jpg. A second request to phar:///tmp/remote_file.jpg/a.txt triggers PHP’s metadata unserialize(), which fires User::__destruct() → system("rm " . $avatar_path) → RCE. ...

June 7, 2026 · 7 min · 1368 words · CyberSecurity Elite Team
GPN CTF 2026 Tinyweb writeup — Link header CSS injection plus body[onload^=...] attribute selectors leak the cookie character-by-character

GPN CTF 2026 — Tinyweb: Link Header CSS Injection + Attribute Selector Exfil

Platform GPN CTF 2026 (kitctf) Difficulty Medium OS Web — Node http, Link header injection, CSS attribute-selector exfil Tags recognising unescape as a percent-decoder that mangles header values, splitting on Link header's comma+semicolon structural separators, injecting a second link entry with rel=stylesheet, hosting the CSS on a serveo-style tunnel that doesn't show an interstitial, iterating body[onload^=prefix] one character at a time Tinyweb is GPN CTF 2026’s pure-web challenge — 481 bytes of one-line Node http server with two reflection points and one admin bot that visits attacker-supplied URLs with the flag in a cookie. The intended path turns out to be an XS-Leaks-style CSS attribute-selector exfiltration: inject rel=stylesheet into the Link header via unescape percent-decoding, host CSS that uses body[onload^="prefix"] selectors to fire background: url(...) requests, iterate one character at a time. ~45 iterations of ~35 seconds each recover: ...

June 7, 2026 · 6 min · 1273 words · CyberSecurity Elite Team
GPN CTF 2026 Königsberg Delivery Problem writeup — Binary Ninja HLIL, Stack View, Graph View, and jump-table resolution for a 250-state FSM

GPN CTF 2026 — Königsberg Delivery Problem: A Binary Ninja Workflow

Platform GPN CTF 2026 (kitctf) Difficulty Medium-Hard OS Reverse — 250-state FSM, jump tables, Hamiltonian path Tags reading 4,500 lines of straight-line dispatch in Binja HLIL instead of objdump, using Stack View to identify a per-state visit-counter array, extracting jump-table successors via function.basic_blocks[i].outgoing_edges, building a 250-node directed graph from Binja-recovered edges, running Warnsdorff's heuristic DFS to find a Hamiltonian path in ~70 ms Königsberg Delivery Problem is the GPN CTF 2026 reverse challenge that turned a 4,500-line straight-line dispatch routine into a graph-theory problem. The binary cartographer is 140 KB, not stripped, dynamically linked, x86-64 PIE. The interesting function cfg() is 250 logically-identical state blocks ending in indirect jmp rdx jumps over .rodata jump tables. The win condition is “visit every state at least once” — Hamiltonian path on a 250-node directed graph. ...

June 7, 2026 · 9 min · 1901 words · CyberSecurity Elite Team
GPN CTF 2026 Leftover Leftovers writeup — one-byte iconst_0 → iconst_1 patch inside the JVM AOT cache bypasses a verifyStuff hash check

GPN CTF 2026 — Leftover Leftovers: One-Byte JVM AOT Cache Patch

Platform GPN CTF 2026 (kitctf) Difficulty Medium-Hard OS Reverse — JVM AOT cache, Javalin, JEP 295 CDS archive Tags reading a JVM AOT cache's ConstMethodView blob, locating a per-method bytecode body inside a 51 MB CDS archive, identifying that the upload validator hashes class structure not bytecode bytes, patching one byte (iconst_0 → iconst_1) to flip a verifier's return, then chaining the original leftovers exploit on Stage 2 Leftover Leftovers is the GPN CTF 2026 follow-on to leftovers. The challenge bolts an upload-and-validate stage on the front: a Javalin server lets you POST /init a candidate cache.aot, runs verifyStuff on it, and if the hash matches the original it writes /tmp/cache.aot for the original leftovers Server to consume. ...

June 7, 2026 · 7 min · 1367 words · CyberSecurity Elite Team
GPN CTF 2026 Stupidcontract writeup — patched kernel strips BPF verifier bounds checks, signed-comparison OOB write clobbers SUCCESS

GPN CTF 2026 — Stupidcontract: Patched eBPF Verifier + Signed-Cmp OOB

Platform GPN CTF 2026 (kitctf) Difficulty Hard OS Reverse — Linux kernel forensics, eBPF, Rust aya Tags unpacking bzImage to vmlinux ELF, string-diffing two kernels with shifted section layout to find removed verifier messages, reading eBPF disassembly to spot signed-compare bypass, exploiting unchecked map-value pointer arithmetic with a negative index, beating a 20%-RNG bit-flip gate by detecting the win and switching to a neutral index Stupidcontract is the GPN CTF 2026 reverse challenge that lives at the intersection of kernel forensics and eBPF. The handout ships two kernel images — patched.bzImage and unpatched.bzImage — plus a Rust/aya userspace runner that loads an eBPF program against a 101-byte .bss map. The challenge is to figure out what was patched and exploit it. ...

June 7, 2026 · 8 min · 1619 words · CyberSecurity Elite Team
GPN CTF 2026 Easy-DSA writeup — UUID3 MD5 collision to force ECDSA nonce reuse on P-521 and recover the private key

GPN CTF 2026 — Easy-DSA: UUID3 MD5 Collision → ECDSA Nonce Reuse

Platform GPN CTF 2026 (kitctf) Difficulty Medium OS Crypto — ECDSA on P-521, deterministic nonce, MD5 collision Tags recognising uuid3 as MD5(ns || name), generating identical-prefix MD5 collisions with Marc Stevens' fastcoll, recovering the nonce from two signatures with the same r, sign-flip check against the published public key, forging fresh signatures with the recovered private key Easy-DSA is a classic cryptographic-engineering blunder dressed up in a Mongolian-barbecue narrative. The server signs arbitrary recipes with ECDSA on P-521. The “secure” nonce is derived through uuid3, which is MD5 under the hood. Marc Stevens’ fastcoll generates two messages that MD5-collide under the namespace prefix, forcing the ECDSA nonce to repeat. Standard nonce-reuse algebra recovers the private key in one round. Forge a fresh signature, claim the flag: ...

June 7, 2026 · 6 min · 1208 words · CyberSecurity Elite Team
Educational content for authorized testing only. · Disclaimer · Editorial Policy · Sitemap