Flash

Writeup for Flash (Web) - Tsuku CTF (2025) πŸ’œ

Description

3, 2, 1, pop!

Solution

Site functionality

When we open the website, we are welcomed to a basic page that says Flash! and presents a start button. Clicking start will cycle through 10 rounds, displaying a 7 digit unique number each time.

When we get to the end, we have to enter the total value.

I thought maybe I could just use a match/replace rule in burp to change the timeout of each round from 500ms to 5000ms, giving me time to write down each number. However, some of the rounds (4-7) don't show any value at all.

Note that each round provides a new session cookie, which we can decode with flask-unsign.

Source code

The challenge comes with source code, app.py is the most interesting.

Breaking it down

The Flask app generates a series of 10 pseudo-random 7-digit numbers using a deterministic PRNG seeded by a secret value and a session ID. The goal is to calculate the sum of all 10 numbers, but only rounds 1–3 and 8–10 are visible.

At startup, the app reads a secret SEED from ./static/seed.txt. This file is served from Flask's static/ directory, making it publicly downloadable.

When we visit /, the app generates a new session with a random session_id and sets the round to 0. The session_id is stored in the Flask session cookie, which is signed but decodable (using flask-unsign).

Each round's number is generated by a linear congruential generator (LCG), with parameters (a, c) and initial state derived from HMACs of the SEED and session_id. The generator advances the state based on the current round index and produces 7 digits per round.

The /flash endpoint shows the digits in an image if the round is in [0, 1, 2, 7, 8, 9]; otherwise, the digits are hidden. It progresses through the rounds automatically, and after round 10, we are redirected to /result.

At /result, we must submit the sum of all 10 numbers. The server recomputes all 10 numbers using the stored session_id and compares the submitted sum to the correct one. If the answer is correct, the flag is displayed.

Steps to solve:

  1. Download seed.txt from /static/seed.txt.

  2. Go through all 10 rounds in a single session, preserving the session cookie.

  3. Decode the session_id from the cookie after the 10 rounds.

  4. Use the known SEED and decoded session_id to regenerate all 10 numbers using the PRNG logic.

  5. Compute the sum and submit it at /result to receive the flag.

We can use ChatGPT to make a quick solve script πŸ™

solve.py

We run the script, and receive the flag 🀀

Not sure if I took the intended approach here, since I didn't actually do any path traversal. I feel like I was tricked into doing a crypto challenge πŸ˜‘

Flag: TsukuCTF25{Tr4d1on4l_P4th_Trav3rs4l}

Last updated