This page looks best with JavaScript enabled

b01lers CTF 2025 writeups

 ·  ☕ 5 min read  ·  👨‍💻 g4nd1v

web/when

the sunk cost fallacy
https://when.atreides.b01lersc.tf/

Writeup

To get the flag in this CTF challenge, we need to send a POST request to the /gamble endpoint with a specific Date header that results in the server generating a SHA-256 hash starting with two 0xFF bytes. Here’s how you can do it:

  1. Understanding the Vulnerability: The server uses the Date header from the request to generate a Unix timestamp. This timestamp is hashed using SHA-256, and if the first two bytes of the hash are 0xFF 0xFF, the flag is returned.
  2. Controlled Input: Since the Date header is user-controlled, you can brute-force a timestamp that produces the required hash.
  3. Brute-Force the Timestamp: Find a timestamp (integer) such that when converted to a string and hashed with SHA-256, the hash starts with 0xFF 0xFF.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const crypto = require('crypto');

// Function to find a valid timestamp where SHA-256 starts with 0xFF 0xFF
function findValidTimestamp() {
    let n = 0;
    while (true) {
        const str = n.toString();
        const hash = crypto.createHash('sha256').update(str).digest();
        if (hash[0] === 0xFF && hash[1] === 0xFF) {
            return n;
        }
        n++;
    }
}

const validN = findValidTimestamp();
const date = new Date(validN * 1000);
const dateHeader = date.toUTCString();

console.log(`Found valid timestamp: ${validN}`);
console.log(`Set Date header to: "${dateHeader}"`);

The result says,

Found valid timestamp: 30398
Set Date header to: "Thu, 01 Jan 1970 08:26:38 GMT"

Use CURL to send the request

curl -X POST https://when.atreides.b01lersc.tf/gamble -H "date: Thu, 01 Jan 1970 08:26:38 GMT"

Flag

bctf{ninety_nine_percent_of_gamblers_gamble_81dc9bdb}

rev/class-struggle

I miss the good old days before OOP, when we lived in a classless, stateless society…

Writeup

To solve this CTF challenge, we need to reverse-engineer the given obfuscated C code to determine the correct input (flag) that passes the validation check. The code applies several transformations to the input string and compares it against a predefined byte array. Our task is to reverse these transformations to retrieve the flag.

  1. Understand the Obfuscated Code: The code uses macros to disguise the actual operations. By expanding these macros, we can see that the code performs XOR, bitwise rotations, and arithmetic operations on each character of the input.
  2. Identify Key Transformations: The transformations applied to each character include XOR with a position-dependent value, left rotation, addition of a constant, another XOR, and a right rotation.
  3. Reverse the Transformations: To find the original flag, we need to reverse each transformation step in the opposite order. This involves right rotating instead of left rotating, subtracting constants, and XORing with the same values.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def rotate_left(val, n):
    n = n % 8
    return ((val << n) | (val >> (8 - n))) & 0xff

def rotate_right(val, n):
    n = n % 8
    return ((val >> n) | (val << (8 - n))) & 0xff

encrypted = [
    0x32, 0xc0, 0xbf, 0x6c, 0x61, 0x85, 0x5c, 0xe4,
    0x40, 0xd0, 0x8f, 0xa2, 0xef, 0x7c, 0x4a, 0x02,
    0x04, 0x9f, 0x37, 0x18, 0x68, 0x97, 0x39, 0x33,
    0xbe, 0xf1, 0x20, 0xf1, 0x40, 0x83, 0x06, 0x7e,
    0xf1, 0x46, 0xa6, 0x47, 0xfe, 0xc3, 0xc8, 0x67,
    0x04, 0x4d, 0xba, 0x10, 0x9b, 0x33
]

flag = []
for i in range(len(encrypted)):
    e = encrypted[i]
    rot = i % 8
    trans4 = rotate_left(e, rot)
    trans3 = trans4 ^ 0x0F
    trans2 = (trans3 - 42) % 256
    rot_amount = (i + 3) % 7
    trans1 = rotate_right(trans2, rot_amount)
    input_char = trans1 ^ (i * 37) % 256
    flag.append(chr(input_char))

print(''.join(flag))
  1. Rotate Left/Right Functions: These functions handle the bitwise rotations needed to reverse the transformations applied during encryption.
  2. Reversing Steps:
    • Rotate Left: For each encrypted byte, rotate left by i % 8 to undo the final right rotation.
    • XOR with 0x0F: This reverses the XOR applied during encryption.
    • Subtract 42: This undoes the addition of 42 during encryption.
    • Rotate Right: Rotate right by (i + 3) % 7 to reverse the initial left rotation.
    • XOR with Position-Dependent Value: Finally, XOR with i * 37 to retrieve the original character.

Flag

bctf{seizing_the_m3m3s_0f_pr0ducti0n_32187ea8}

web/trouble at the spa

I had this million-dollar app idea the other day, but I can’t get my routing to work! I’m only using state-of-the-art tools and frameworks, so that can’t be the problem… right? Can you navigate me to the endpoint of my dreams?
https://ky28060.github.io/

Writeup

We have a NextJS application with these routes.

<BrowserRouter>
    <Routes>
        <Route index element={<App />} />
        <Route path="/flag" element={<Flag />} />
    </Routes>
</BrowserRouter>

On visiting /flag route, it does not seem to exists on GitHub pages.
I have tried multiple things like

  1. searching from source code - reading JS files, but it seems to be obfuscated. I tried to deobfuscate it with python code manually, but did not get anything out of it.
  2. Next, I went to the repo - as the website is hosted on github pages, we can able to see the source code of the website using this link: https://github.com/ky28060/ky28060.github.io. I tried to find the flag from this file, but got no luck.
  3. Next, I have tried to check the documentation of <BrowserRouter>. It says <Router> that uses the HTML5 history API (pushStatereplaceState and the popstate event) to keep your UI in sync with the URL. Then I have asked ChatGPT to give me vanilla JS code that I might be able to execute in the console of browser. Here is the thread you check I have asked.
    https://chatgpt.com/share/6803ea66-8764-8006-8106-8b58bf952538
    So, from the thread with chatgpt, we can use
  • history.pushState({}, '', '/flag'); - It updates the URL without reloading the page.
  • dispatchEvent(new PopStateEvent('popstate', { state: history.state })); -> popstate is triggered on browser navigation (back/forward).

Flag

bctf{r3wr1t1ng_h1st0ry_1b07a3768fc}

Share on

g4nd1v
WRITTEN BY
g4nd1v
Pentester