CIT 2026 was a mixed-format CTF with 44 challenges across ten categories. The strongest narrative this year was the OSINT chain around the vitrinefox persona, plus two linked Windows Fullpwn boxes. This post groups the solved work by category.

Fullpwn

Wingstop 1 and 2

Both challenges shared the same Windows target. Wingstop 1 used CVE-2025-47812, an unauthenticated Lua injection in Wing FTP Server. The username form field is stored in a Lua long-string ([[…]]); a null byte followed by ]] terminates the string early, and the rest of the username is interpreted as Lua code. Sending a payload like anonymous%00]]%0dprint(%22pwn%22)%0d-- and then fetching /dir.html with the issued UID cookie executed arbitrary Lua.

From the Wing FTP admin interface, backup.xml gave Admin:Password@1, but that credential is for Wing FTP, not Windows. The exploit landed as wingstop\bob. On bob’s desktop sat flag1:

CIT{p1Gs_0n_tH3_W1ng}

Wingstop 2 was the privilege-escalation half. The live service was unstable and taken offline before the final stage could run, but the chain was fully mapped:

  • bob had SeImpersonatePrivilege and was in NT AUTHORITY\SERVICE.
  • A prior solver had dropped gp.b64 / gp.hex payloads in C:\Users\Public\.
  • The plan was certutil -decode → GodPotato → NT AUTHORITY\SYSTEM → read Administrator\Desktop\flag2.txt.

Every piece was verified in isolation except the final read.

OSINT

Follow the Flock

A clue about something that “smells like pizza” in New Haven pointed at State Street, home to Modern Apizza and NHPD Rekor ALPRs. The flag was the street address:

CIT{State_Street_New_Haven_CT}

Cartographer’s Secret

This one rewarded reading raw OSM tags instead of rendered tiles. A Flock Safety ALPR pole at the edge of the University of New Haven campus, node 13735855418, carries a flag=* tag:

[out:json];
node["man_made"="surveillance"]["manufacturer"="Flock Safety"]["flag"](41,-73.5,42,-72);
out tags;

Response:

{
  "id": 13735855418,
  "tags": {
    "camera:type": "fixed",
    "flag": "CIT{ch3ck_th3_OSM_t4gs}",
    "manufacturer": "Flock Safety",
    "surveillance:type": "ALPR"
  }
}

Flag: CIT{ch3ck_th3_OSM_t4gs}

The Curator’s Exit — Part 2

Part 2 was unsolved. The persona VitrineFox / Remy Beauvillier had X, Linktree, LinkedIn, PCPartPicker, Dropbox, and OSM accounts. The Dropbox PDF was a decoy, Wayback was empty, and the decisive evidence — the two accounts followed by @vitrinefox — was blocked behind X’s login wall. The real flag remains unknown.

Web

Sign Up and Enjoy

A Flask app with stock signed session cookies. Registration was allowed, but existing usernames returned That username is already in use., which gave username enumeration. Sprayed synthetic default passwords (operations / Aa1!aaaa, administrator / Abcd1234!, etc.) were weak, and so was the Flask secret:

Password1!

Decode any real user’s session, change role from standard to admin, re-sign, and request /admin:

from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
# secret = 'Password1!'
# forged = { ... "role": "admin" ... }

Flag: CIT{W3ak_S3cr3t5_C@n_B3_Un5ign3d}

Forensics

Wiretap

A ~4-minute mono WAV contained a fake phone call: dial tone, DTMF, ringback, then a modem handshake. Demodulating the two Bell 103 FSK channels recovered an HTTP request and response:

GET /SiliconValley/Heights/4721/diary.html HTTP/1.0
Host: www.geocities.com

The response was a 90s GeoCities page with three SVG scanlines. Rendering them produced a hidden banner:

g3t_0ff_th3_ph0n3_1m_0n_th3_1ntern3t

Flag: CIT{g3t_0ff_th3_ph0n3_1m_0n_th3_1ntern3t}

Reverse Engineering

Escape Room

A statically linked ELF presented a terminal menu for a “ROOM 7B EGRESS TERMINAL.” The facility log entries gave the correct state:

  • Hallway lights: OFF
  • Vent route: east bypass
  • Camera bus: bus 3 / mirror relay
  • Door-control patch: applied exactly twice
  • Emergency battery bridge: ENGAGED
  • Maintenance shell: run mirror, then hush

Reversing buildOverrideToken and roomSignature showed the token is a deterministic function of those state values. Replicating the algorithm in Python produced the 12-character override token, which unlocked the door.

Flag: CIT{Vc282vlhCxIJ}

Faultline

A command-line “seam optimizer” scored 12-character profiles through stress, shear, grain, load, and seal metrics. The binary embedded target arrays for each metric, so the problem reduced to a constraint-satisfaction puzzle. Dumping the targets and feeding them to Z3 found the unique profile SDPKGTCMJRFL, which produced a token and the flag.

Flag: CIT{12z4PXVTa3x3}

Crypto

Farcical Clones

A decimal ciphertext with a Star Wars skin. Splitting each number by divmod(byte, 25) separated a random clone digit from a real core value in [0,24]. The cores used a 25-letter alphabet (no J) and a Beaufort one-time pad:

core = (K - P) mod 25

The intro sentence is the crib: May the Force be with you, young padawan. CIT. Deriving the key from the crib decrypts the 55-byte message. The flag body reads JediMasterCipherDyas — a play on Sifo-Dyas.

Flag: CIT{JediMasterCipherDyas}

Baby Exponent

Small RSA exponent led to straightforward recovery.

Flag: CIT{sm4ll_3xp0n3nt_g0_brrr}

Brainiac

Brainfuck output whose SHA1 matched a flag-shaped string.

Flag: CIT{Wh@t_in_th3_w0rld_i$_th1s_l@ngu@g3}

Misc

121 Gigawatts

A floppy disk image. strings found the flag directly.

Flag: CIT{fl0ppy_d1sk_fluX_c4p4c1tor}

Your Car Called

CAN-bus-style challenge. The flag was:

CIT{my_0th3r_c4r_1s_a_c4n_bus}

Dog Barking

A password-recovery / pet-name puzzle. The answer was:

CIT{b4rking_up_th3_wr0ng_tr33}

Quick wins

ChallengeCategoryOne-liner
robotsMiscFollow robots.txt.
what’s the wordMiscThe bird is the word.
the onionCryptoLayered rot / substitution.
rotor rooterCryptoRotor cipher + known plaintext.

Takeaways

  • OSINT: One node with a literal flag=* tag is easy to miss when everyone is staring at map tiles. Always query raw OSM tags.
  • Web: Default/weak Flask secrets are still a live CTF trope; re-signing session cookies is a fast path to admin.
  • Forensics: Old protocols (Bell 103, DTMF, Zmodem) keep coming back. Demodulation beats staring at waveforms.
  • Reverse: When a binary gives you a menu and logs, treat the logs as the specification and the binary as the oracle.
  • Crypto: Separate payload from noise before attacking the cipher; here the random clone digit masked a simple mod-25 Beaufort OTP.