DubiousDoubloon - Web
Points: 152 | Flag: BCCTF{Fl1p&F1sH} | Solved by: Smothy @ 0xN1umb

what we got
a pirate-themed coin flip game at http://chal.bearcatctf.io:33099/. the description says to get 15 heads in a row to "break the curse". the coin has a 20% win chance and you can buy upgrades to increase it... but ngl who has time for that lmao
the solve
ok so first thing i see is the game logic is all in WASM - index.js just imports from a ./pkg/unfair_wasm_game.js which loads unfair_wasm_game_bg.wasm. the JS is basically just UI fluff, all the real logic (flip_coin, get_state, buy_upgrade) lives in the wasm binary
import init, { flip_coin, get_state, buy_upgrade } from './pkg/unfair_wasm_game.js';the flip_coin() function either returns "HEADS", "TAILS", or if it starts with BCCTF{ - thats the flag. so the flag is literally constructed inside the wasm when your streak hits 15
so instead of actually playing this rigged casino game... lets just look at whats in the wasm lol
$ wasm2wat game.wasm -o game.watfound the flip_coin function (func 119) and traced through the "you win" branch. when streak > 14, it allocates 16 bytes and stores two hardcoded i64 constants:
i64.const 9027592108531675185
i64.store offset=8 align=1
i64.const 7802058946457584450
i64.store align=1these are just the flag bytes stored as little-endian 64-bit integers lmaooo. quick python decode:
import struct
v1 = 7802058946457584450 # offset 0
v2 = 9027592108531675185 # offset 8
b1 = struct.pack('<q', v1) # b'BCCTF{Fl'
b2 = struct.pack('<q', v2) # b'1p&F1sH}'
print(b1 + b2)
# b'BCCTF{Fl1p&F1sH}'no encryption, no xor, no nothing. the flag was just sitting there in the binary as raw bytes the whole time. the wasm only does a utf-8 validation on it before returning (standard rust str check) and panics with "Flag Decode Failed" if its invalid - but its plain ascii so that never triggers

why would i flip a coin 15 times when i can just struct.unpack twice fr
flag
BCCTF{Fl1p&F1sH}
smothy out ✌️