conCTF 2025 — oooo

image

overview~~

The challenge gives oooo.py.

1
2
3
#!/usr/local/bin/python3
import random; FLAG = open("flag.txt", "rb").read(); print("welcome to oooo")
while True: print(bytes(a^b for a, b in zip(FLAG, random.sample(range(0, 256), k=len(FLAG)))).hex() if input() != "exit" else exit())

quick idea

The server gives hex(flag XOR keystream).
The keystream is made with random.sample(range(0, 256), k=len(FLAG)), so all bytes in one response are different.

We know the flag starts with corctf{ and ends with }.

Use the known bytes to figure out some keystream values.
Then, remove any candidate bytes that would repeat the same keystream value in that response.

Repeat this process with many responses until each flag position has only one possible value.

codeee

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
from pwn import remote
import sys

flen=56
pref=b'corctf{'
lim=1200

conn=remote('ctfi.ng',31556)
conn.recvline()

can=[set(range(256))for _ in range(flen)]
for i,b in enumerate(pref):
can[i]={b}
can[-1]={ord('}')}

got=0
while got<lim and not all(len(s)==1 for s in can):
conn.sendline()
line=conn.recvline().strip().decode(errors='ignore')
try:
cipher=bytes.fromhex(line)
except:
continue
if len(cipher)!=flen:
continue
got+=1
ks_list=[]
for p in range(flen):
if len(can[p])==1:
ks_list.append(cipher[p]^next(iter(can[p])))
for k in ks_list:
for p in range(flen):
if len(can[p])==1:continue
bad={x for x in can[p] if (cipher[p]^x)==k}
if bad:can[p]-=bad

if all(len(s)==1 for s in can):
flag=bytes(next(iter(s))for s in can)
print('flaggggggggggggg:',flag.decode())

conn.close()

result!

image

I kinda messed up the screenshot, but whatever lol.