CTF Write-up: Flappy
Challenge Information
- Name: Flappy
- Points: 500
- URL: http://exp.cybergame.sk:7010
1. Initial Discovery
Upon visiting the challenge URL, we are presented with a Flappy Bird game clone. The page title is "Flappy Bird (Rust/WASM)", indicating it's built using Rust and WebAssembly.
Inspecting the HTML source reveals a hidden div with the ID score-sync, which contains a "Sign in" form styled like Google's login page. There is also a suggestive comment in the HTML:
<!-- TODO: remove before production flag{n0t_th3_r34l_fl4g_l0l} class="hljs-keyword">-->The game's logic is loaded via a WebAssembly module:
class="hljs-keyword"><script type="module">
import init from './pkg/flappy_wasm.js';
(async()=>{await init()})();
class="hljs-keyword"></script>2. Analyzing the Sign-in Process
By manually showing the hidden modal and attempting to sign in, we can observe the network traffic. We used a Playwright script to automate this:
# Snippet of the interception logic
page.evaluate(class="hljs-string">"document.getElementById(class="hljs-string">'score-sync').style.display = class="hljs-string">'flex';")
page.fill(class="hljs-string">"#sc-email", class="hljs-string">"test@example.com")
page.fill(class="hljs-string">"#sc-pass", class="hljs-string">"password123")
page.click(class="hljs-string">".sc-btn")The sign-in attempt triggers a POST request to http://exp.cybergame.sk:7010/api/v1/oauth/token with the following JSON payload:
{
"grant_type": "authorization_code",
"code": "Jy5eNwU3LBoUQBkXcQAdXl5DD0QaQwMBOwVAAA==",
"client_id": "894271536842-k3jhf9x2v7m1n5p4q6r0s8t.apps.googleusercontent.com",
"redirect_uri": "http://exp.cybergame.sk:7010/callback",
"ua": "Mozilla/5.0 (X11; Linux x86_64) ...",
"res": "1280x720",
"ts": 1775226704008
}3. Reverse-Engineering the code Parameter
The code parameter appears to be a base64-encoded string that changes based on the input email. Given the nature of CTF challenges, this is a common pattern for client-side XOR encryption.
If the code is generated by email ^ flag, we can retrieve the flag by calculating code ^ email.
4. Extracting the Flag
We wrote a script to submit a long email address and perform the XOR operation on the resulting code.
import base64
import json
def xor_bytes(b1, b2):
return bytes(a ^ b for a, b in zip(b1, b2))
# ... after capturing the code ...
decoded = base64.b64decode(code_b64)
email_bytes = email.encode()
flag_bytes = xor_bytes(decoded, email_bytes)
print(fclass="hljs-string">"Flag: {flag_bytes.decode(class="hljs-string">'utf-8', errors=class="hljs-string">'ignore')}")Using the email a_very_very_long_email_longer_@cybergame.sk, we successfully extracted the flag.
5. Final Flag
SK-CERT{y0ur_cr3d3n7i4ls_4r3_fl4pping_4w4y}
Conclusion
The challenge demonstrates the danger of performing "encryption" or data obfuscation on the client side with static keys. By intercepting the request and controlling the input plaintext (the email), the secret XOR key (the flag) was easily recovered.