Skip to main content

CTF

Tinyirc

· loading
CODEGATE 2026 Quals - tinyIRC # Category: Pwn Challenge: tinyIRC Remote launcher: nc 15.165.70.236 20998 Solver: solve.py TL;DL # The wrapper port is not the IRC service. It prints the real port, keeps the wrapper process attached to the child, and becomes the side channel that later carries the leak and the flag. Inside the IRC server, QUIT clears a client slot while the recv loop is still using the stale pointer, and a reused slot can come back with a negative input_len. That negative length becomes a reusable cross-slot overwrite. I used it first to turn memmove() into printf() for a same-process libc leak, then to replace strtok@got with system() and run cat /home/ctf/flag >&2.

Oldschool

· loading
CODEGATE 2026 Quals - oldschool # Category: Reverse / AEG Challenge: oldschool Description: Back to the past Solver: solver.py Client helper: drive_client.py TL;DL # The provided Go client is only a courier. The real challenge is the ELF it downloads every round. Each round binary checks sha256(input[:4]), uses those same four bytes to decrypt a 7-instruction VM program, runs the remaining 60 bytes through that VM, applies one more generated bytewise transform, and compares the result against a target buffer in .rodata. I solved it by separating the stable part from the unstable part: recover the 4-byte prefix statically, invert the VM cleanly, and let one or two GDB probes reveal the final generated stage instead of trying to re-lift that tail by hand every round.

Greybox

· loading
CODEGATE 2026 Quals - Greybox # Category: Reverse Engineering Challenge: Oh! My Greybox Keeps Running! Files: deploy/prob, deploy/target Solver: solver.py TL;DL # The binary hides a small VM behind fake FILE state and libc teardown machinery. The hard part is not the arithmetic. The hard part is recognizing that the weird runtime wrapper is only there to make the VM harder to spot. Once I aligned the handler table correctly and confirmed how the scheduler dispatches handlers, the shortest reliable solve was to record one concrete trace, replay that trace symbolically, and let z3 recover the 64-byte accepted input.

Comqtt

· loading
CODEGATE 2026 Quals - comqtt # Category: Pwn Challenge: mqtt / comqtt Solver: solve.py TL;DL # The broker has a retained-message deletion bug that leaves a stale tail entry behind after compaction. On the next retained insert, that stale slot frees a payload pointer that a live retained entry still references. Because each client runs in its own thread and glibc tcache is per-thread, that one mistake becomes a cross-thread tcache-dup primitive. I used it first to build an arbitrary-read oracle, then to dump the live libc image, resolve system() from the in-memory ELF data, and finally overwrite free@GOT with the real runtime address instead of guessing a libc version.

Cogwartslang

· loading
CODEGATE 2026 Quals - CogwartsLang # Category: Reverse Engineering Challenge: CogwartsLang Solver: solve.grim TL;DL # The language syntax is mostly decoration. The real challenge is the oracle host module loaded by harness. Once I understood that the important state lived in the host and not in the source language, the solve became a timing problem: reconstruct the oracle’s arithmetic, identify the exact checkpoint and ticket values, and call the host functions in the right order without accidentally burning extra ticks.

Cobweb

· loading
CODEGATE 2026 Quals - Cobweb # Category: Web Challenge: Cobweb Description: I wanted to create a web application.. but I don't know how to use web frameworks. So I decided to use pure C to make a web application! Solver: exploit_admin_post_xss.py Transport helper: solve.py TL;DL # The challenge looks like a stored-XSS task at first, but that is only half right. The actual entry point is a one-byte stack overwrite in edit_post. If I make the escaped content length land exactly on 0x6000, the trailing NUL from html_escape() zeros the low byte of the saved user_id local. That pushes the request into the admin SQL branch, rewrites my post as user_id = 0, and then the admin-only render path decodes the escaped content back into raw HTML. Only after that ownership flip does the stored script become real JavaScript in the bot’s browser.

Explorer

· loading
First look # This challenge shipped a bzImage, an initramfs.cpio.gz, and a remote VM that dropped into a BusyBox shell. That immediately made me think “driver challenge,” so I unpacked the initramfs before spending time on the remote instance.

Requiem

· loading
First look # requiem is a stripped Rust ELF, which usually means a lot of disassembly noise before you get to the part that matters.

Lactf 1986

· loading
First look # This one came with a very on-theme setup: a tiny CHALL.EXE DOS program and a floppy image containing the same executable. A quick strings pass already told me it was a flag checker:

The Cat

· loading
Status # This page was originally just a template, and I do not have enough real solve artifacts in the repo to turn it into a proper writeup without inventing details.