Skip to main content
  1. CTF Writeups/
  2. NahamCon 2025 CTF/

FlagsFlagsFlags

·
Rev 97pt 285 Solves Upx Pwntools
subzcuber
Author
subzcuber
i like to imagine i’m funny

Author: @Kkevsterrr

Did you see the strings? One of those is right, I can just feel it.


We are given the binary flagsflagsflags

ctfs/nahamcon/rev
❯ file flagsflagsflags
flagsflagsflags: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header

ctfs/nahamcon/rev
❯ strings flagsflagsflags | head
vUPX!
P/PI
3	_
...

We can see it’s packed with upx. I know about upx thanks to Harshit Jain

So first step is unpacking

upx -d flagsflagsflags

Now if we run strings on the binary, we notice one huge dump in the middle. That contains 100,000 different flags, i.e. the strings the description is referring to

You can apparently create a solve script template with pwntools, so I did that

❯ pwn template ./flagsflagsflags > solve.py
 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
```py
#!/usr/bin/env python3
from pwn import *
import re

FLAG_LIST = open("./flagsflagsflags", "r")
data = FLAG_LIST.read()
FLAG_PATTERN = r'flag{[a-f0-9]*}'
flags = re.findall(FLAG_PATTERN, data)
FLAG_LIST.close()

exe = context.binary = ELF(args.EXE or './flagsflagsflags')

def start(argv=[], *a, **kw):
    '''Start the exploit against the target.'''
    if args.GDB:
        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
    else:
        return process([exe.path] + argv, *a, **kw)

gdbscript = '''
tbreak *0x{exe.entry:x}
continue
'''.format(**locals())

#===========================================================
#                    EXPLOIT GOES HERE
#===========================================================
# Arch:     amd64-64-little
# RELRO:      No RELRO
# Stack:      No canary found
# NX:         NX enabled
# PIE:        No PIE (0x400000)

count = 0
for flag in flags:
    io = start()
    print(f"[{count}] - {flag}")
    print(io.recvline())
    io.sendline(flag.encode())
    output = io.recvall()
    print(output)
    count = count + 1
    if (output.decode().strip() != "Incorrect flag!"):
        print(output)
        break
    io.close()
```

This took like 2-3 hours and around the 10,000th flag I found the correct one.

A thought I had for making this faster was batching multiple processes. But also someone on discord said that the correct flag was just given straight up in the ghidra output. It was a 4MB unpacked binary so I didn’t dare to use ghidra, but okay.

Reply by Email

Related

Praise our RNG Gods
Rev 436pt 58 Solves Random() Python Bytecode Dis
prolly my fav chal of the ctf
Loginator
Rev 50pt 153 Solves
i <3 phineas&ferb
Baby Rev
Rev 50pt 233 Solves Obfuscation
python obfuscation