Meow Transmission Revenge - Forensics
Points: 451 | Flag: BITSCTF{r3v3ng3_0f_th3_c4t_m4p_m4st3r} | Solved by: Smothy @ 0xN1umb

what we got
a single file revenge_transmission.png - 128x128 RGB image. challenge says its "chaotic noise" and the cat has a "triple personality" now. metadata is packed with Arnold Cat Map parameters.
the solve
step 1: check the metadata
exiftool revenge_transmission.pngmetadata was absolutely LOADED:
Comment: Meow! The cat has returned, and now I see in full color.
You solved my first puzzle, but can you handle my triple personality?
[Red Personality]
Leaps: 3
Styles: [1, 2, 1]
Spins: [47, 37, 29]
Sequence: [1, 2, 3]
[Green Personality]
Leaps: 3
Styles: [2, 1, 2]
Spins: [20, 50, 20]
Sequence: [2, 3, 1]
[Blue Personality]
Leaps: 3
Styles: [1, 1, 1]
Spins: [10, 80, 10]
Sequence: [3, 1, 2]
World Size: 128x128
Hint: I stopped counting my spins (periods). You figure it out.
ok so Arnold Cat Map with different params per RGB channel. 3 stages each ("leaps"), different matrix styles and iteration counts ("spins"), applied in a specific order ("sequence"). got it.
step 2: the big bamboozle
spent way too long trying to "unscramble" the full image thinking it was noise. turns out... it wasn't noise at all lmao. the image is literally just a picture of a cat. mean horizontal pixel difference was only 2.9 - that's smooth af.
the REAL data was hiding in the LSBs (least significant bits) of each channel. classic stego move ngl.
# extract LSBs
for ch_idx, ch_name in enumerate(['R', 'G', 'B']):
lsb = (pixels[:, :, ch_idx]) & 1
print(f"{ch_name} LSB: {np.sum(lsb)} ones out of {N*N}")R LSB: 319 ones out of 16384 pixels (1.9%)
G LSB: 319 ones out of 16384 pixels (1.9%)
B LSB: 319 ones out of 16384 pixels (1.9%)
exactly 319 white pixels per channel. very sparse, very intentional. the LSB planes showed scattered dots - a scrambled binary image.
step 3: arnold cat map parameters
had to figure out what "Styles" meant. after trying like 5 different interpretations:
- Style 1: standard Arnold Cat Map matrix
[[1,1],[1,2]]→(p=1, q=1) - Style 2: generalized Arnold Cat Map
[[1,2],[1,3]]→(p=2, q=1)
the Arnold Cat Map formula being:
x' = (x + p*y) mod N
y' = (q*x + (1+p*q)*y) mod N
Sequence determines the order stages are applied. e.g. Red's [1,2,3] = apply stage 1 first, then 2, then 3. Green's [2,3,1] = stage 2 first, then 3, then 1.
step 4: unscramble the LSBs
for each channel:
- extract the LSB plane
- build the forward stage order from
Sequence - apply the inverse Arnold Cat Map for each stage, in reverse order
style_map = {
1: (1, 1, 1, 2), # [[1,1],[1,2]]
2: (1, 2, 1, 3), # [[1,2],[1,3]]
}
for ch_idx, ch_name in enumerate(['R', 'G', 'B']):
cfg = configs[ch_name]
lsb = (pixels[:, :, ch_idx] & 1).astype(np.uint8)
# build forward order from sequence
stages = []
for seq_val in cfg['sequence']:
idx = seq_val - 1
style = cfg['styles'][idx]
spins = cfg['spins'][idx]
stages.append((style_map[style], spins))
# apply inverse in reverse order
decoded = lsb.copy()
for (a, b, c, d), spins in reversed(stages):
# inverse of [[a,b],[c,d]] with det=1 is [[d,-b],[-c,a]]
inv = (d % N, (-b) % N, (-c) % N, a % N)
for _ in range(spins):
decoded = cat_scatter(decoded, *inv, N)step 5: profit
the unscrambled LSBs revealed pixel-art text across all three channels:
BITSCTF{r3v3ng3_
0f_th3_c4t_m4p_m
4st3r}

key takeaways
- dont assume the image is scrambled just bc the challenge says so. CHECK THE PIXELS FIRST
- arnold cat map on the LSBs, not the full image - sneaky af
- "triple personality" = different cat map params per RGB channel
- "Styles" = which (p,q) parameter set. Style 1 = (1,1), Style 2 = (2,1)
- "Spins" = iteration count per stage
- "Sequence" = order to apply the stages (1-indexed)
- to reverse: apply inverse matrix for each stage in reverse sequence order
flag
BITSCTF{r3v3ng3_0f_th3_c4t_m4p_m4st3r}
revenge of the cat map master - respect tbh
smothy out ✌️