SunshineCTF 2025 Writeup - Bits of Space
This attack is a classic cryptographic vulnerability called CBC Bit-Flipping Attack, which targets data encrypted using the CBC mode. The goal of the challenge is to get the server to decrypt a malicious packet that results in a specific, secret value for the `device_id` (0xdeadbabe).
Challenge:

1. The Core Weakness: CBC Mode Decryption
The entire attack hinges on how the AES-CBC mode decrypts data. In CBC mode, every block of plaintext (Pi) depends not only on the current ciphertext block (Ci) and the secret key (K) but also on the previous ciphertext block (Ci−1) via a simple XOR operation.
The decryption formula is:
Pi=DK(Ci)⊕Ci−1
For the very first block (P1), the previous "ciphertext" block is the Initialization Vector (IV):
P1=DK(C1)⊕IV
In our challenge, the device ID is located entirely within the first block of plaintext (P1). Since we know the entire encrypted packet structure is IV+C1+C2, we can control the IV to change the resulting P1 (the device_id) without knowing the secret key K.
2. The Setup and the Unknown
We have three key pieces of data:
- Original Encrypted Packet (
voyager.bin): This contains the Original IV (IVorig) and the Ciphertext (C1∣∣C2). - Target Plaintext: The desired
device_idis 0xdeadbabe (Ptarget). - Original Plaintext (The Unknown): The original
device_id(Porig) that thevoyager.binfile was created with. We assume it's one of the other three valid, non-flag device IDs.
The Bit-Flipping Formula
We need to calculate a New IV (IVnew) such that:
Ptarget=DK(C1)⊕IVnew
By rearranging the original decryption formula, we can solve for DK(C1):
DK(C1)=Porig⊕IVorig
Substituting this back into the target formula gives us the final calculation needed to craft the IVnew:
IVnew=Porig⊕IVorig⊕Ptarget
This means we take the original IV, XOR it with the original device ID, and then XOR it with the target device ID.
3. The Exploit Implementation (Iterative Solution)
Since we don't know the exact Porig that was used, the script must try all three possibilities for the original device ID: 0x13371337, 0x1337babe, and 0xdeadbeef.
Code Snippet: The Calculation
The following Python code performs the XOR calculation byte-by-byte for the 4-byte device_id field.
# The actual 48-byte content of voyager.bin (IV_ORIG is the first 16 bytes)
VOYAGER_BIN_CONTENT = bytes.fromhex("5e60383e8ebbee04aa00a3bead867ef9edf4f6a2fb241b9e21b4926b5134e3d2c86a561443d312ceb0338c664881496a")
IV_ORIG = VOYAGER_BIN_CONTENT[:16]
# The target device ID (0xdeadbabe) in little-endian bytes
TARGET_DEVICE_ID_BYTES = struct.pack("<I", 0xdeadbabe)
# Loop through possible original IDs
POSSIBLE_ORIG_DEVICE_IDS = [0x13371337, 0x1337babe, 0xdeadbeef]
for orig_id_int in POSSIBLE_ORIG_DEVICE_IDS:
# 1. Get the original ID bytes (P_orig)
ORIG_DEVICE_ID_BYTES = struct.pack("<I", orig_id_int)
# 2. Calculate the new IV prefix (IV_new[0:4])
IV_PREFIX_NEW = bytes([
(a ^ b ^ c) for a, b, c in zip(ORIG_DEVICE_ID_BYTES, IV_ORIG[:4], TARGET_DEVICE_ID_BYTES)
])
# 3. Construct the malicious packet
IV_NEW = IV_PREFIX_NEW + IV_ORIG[4:] # New prefix + rest of original IV
MALICIOUS_PACKET = IV_NEW + VOYAGER_BIN_CONTENT[16:] # New IV + original Ciphertext body
# 4. Send MALICIOUS_PACKET to the server
# ...
When the correct original ID is used, the server's decryption process will mathematically flip the device_id to 0xdeadbabe, authenticate us to the restricted relay, and give us the flag. If an incorrect ID is assumed, the plaintext will be corrupted, resulting in a PKCS#7 padding error and the "Invalid subscription" message.
