BITSCTF - Reverse Mishap

Reversing Mishap

Points

PTS 500

Description

I set out to create a brutal Reverse Engineering challenge for this CTF using Deepseek. It delivered… a little too well. Now there’s so much randomness in the code that even I can’t reverse it to recover the flag. 💀

Flag

BITSCTF{i_guess_t3xt_f1les_h3v3_m3tad4ta_as_W3ll_451a587f}

Download

Quick Writeup

Rust version

Rust commit hash

the commit hash 051478957371ee0084a7c0913941d2a8c4757bb9 belongs to release 1.80.0

Identifying libraries

Using strings i found the binary are using this libraries

Seach for index.crates.io

Create a Cargo.toml file to use same depencies with same version

[package]
name = "demo"
version = "0.1.0"
edition = "2021"

[dependencies]
rand_core = "0.6.4"
rand_chacha = "0.3.1"
generic-array = "0.14.7"
cipher = "0.4.4"
aes = "0.8.4"
ppv-lite86 = "0.2.20"
rand = "0.8"

Flare capa - Information

The Capa information will be used when crafting a Rust application, which we will use to generate a signature.

Extract information

Deepseek

The challenge description mentioned Deepseek, so I used Deepseek R1 to generate a Rust application with the same library versions and Flare-Capa information. Two or three examples were used while trying to utilize all possible methods. (You can use the Solver code to be used as example).

Build the example And Make Signature

cargo build

Load the demo binary in IDA and create Sig file.

Produce Signature

Applying the signature

Load the signature to identify functions

Load signature
Decompiled main

Solution

After recognizing all the functions used by the program's main function, use Deepseek again to find a solution to retrieve the flag by sending the decompiled code to Deepseek.

// Found flag with timestamp 1738793904: BITSCTF{i_guess_t3xt_f1les_h3v3_m3tad4ta_as_W3ll_451a587f}
use std::fs::File;
use std::io::Read;

use aes::Aes256;
use cipher::{BlockDecrypt, KeyInit};
use generic_array::GenericArray;

// from rand 0.8+ 
use rand::rngs::StdRng;
use rand_core::{RngCore, SeedableRng};

fn main() {
    // Read encrypted file
    let mut ciphertext = Vec::new();
    let mut file = File::open("flag.txt").expect("File open failed");
    file.read_to_end(&mut ciphertext).expect("Read failed");

    // If the puzzle code uses a 64-bit seed (u64) for `StdRng`, do the same:
    // exiftool flag.txt # File Modification Date/Time     : 2025:02:06 03:48:24+05:30
    // date --date="2025-02-06 03:48:24 +0530" +"%s" # 1738793904
    let release_time: u64 = 1738793904;

    // Adjust if you want
    let window = 0;  

    for secs in (release_time.saturating_sub(window))..=(release_time.saturating_add(window)) {
        
        // 1) Initialize "standard RNG" from the puzzle's timestamp
        let mut rng = StdRng::seed_from_u64(secs);

        // 2) Derive AES-256 key
        let mut key = [0u8; 32];
        rng.fill_bytes(&mut key);

        // Debug
        // println!("Trying timestamp {:X} with AES key = {:02X?}", secs, key);

        // 3) Decrypt using AES-256 ECB
        let mut data = ciphertext.clone();
        let cipher = Aes256::new(GenericArray::from_slice(&key));
        for chunk in data.chunks_mut(16) {
            cipher.decrypt_block(GenericArray::from_mut_slice(chunk));
        }

        // 4) Check for a "BITSCTF{...}" pattern
        if let Some(end) = data.iter().position(|&b| b == b'}') {
            if data.starts_with(b"BITSCTF{") {
                let flag = &data[..=end];
                if let Ok(flag_str) = std::str::from_utf8(flag) {
                    println!("Found flag with timestamp {}: {}", secs, flag_str);
                    return;
                }
            }
        }
    }

    println!("Flag not found in time window");
}

Last updated

Was this helpful?