# WORMCON 0x01 2021 - Exclusive

Cryptography – 100 pts (25 solves) – Chall author: BUILDYOURCTF

A home-rolled XOR cipher with a stunning key space of 1 byte, yes a total of 256 different keys. That just asks to be brute-forced and so that is exactly what we will do. Try out all the keys and get our flag.

Check out write-ups by my teammates on K3RN3L4RMY.com

## Exploration

Take a look at the source code. More specifically, can you spot the vulnerability without understanding the encryption process at all?

def splitit(n):
return (n >> 4), (n & 0xF)

def encrypt(n, key1, key2):
m, l = splitit(n)
e = ((m ^ key1) << 4) | (l ^ key2)
return e

alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'

assert all(x in alpha for x in FLAG)

otp = int(os.urandom(1).hex(), 16)
otpm, otpl = splitit(otp)

print(f"{otp = }")
cipher = []

for i,ch in enumerate(FLAG):
if i % 2 == 0:
enc = encrypt(ord(ch), otpm, otpl)
else:
enc = encrypt(ord(ch), otpl, otpm)
cipher.append(enc)

cipher = bytes(cipher).hex()
print(f'{cipher = }')

open('out.txt','w').write(f'cipher = {cipher}')


The encryption function isn’t secure at all, but the biggest problem lies in the following line.

otp = int(os.urandom(1).hex(), 16)


This ‘otp’ variable is used as the key of the encryption process and it is ONLY 1 BYTE LONG! This means that there are only 256 different keys possible, this is way too few and can be easily brute-forced by a computer. In fact, you might even be able to brute-force this on pen and paper within CTF time.

## Exploitation

The brute-force is quite easily set up as we just copy-paste most of the source code but replace the random key generation with a for-loop that iterates over all possible key values, 0 to 255. Somewhere amongst the output will we find our flag! You could make it a bit easier for yourself by adding an if-statement to the final print call that check for the presence of the substring ‘wormcon’ (flag format).

def splitit(n):
return (n >> 4), (n & 0xF)

def encrypt(n, key1, key2):
m, l = splitit(n)
e = ((m ^ key1) << 4) | (l ^ key2)
return e

alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'

for k in range(256):
otpm, otpl = splitit(k)

plain = []

for i,ch in enumerate(bytes.fromhex(cipher)):
if i % 2 == 0:
enc = encrypt(ch, otpm, otpl)
else:
enc = encrypt(ch, otpl, otpm)
plain.append(enc)

plain = bytes(plain)
print(plain)


Ta-da!

wormcon{x0r_n1bbl3_c1ph3r_15_4_h0m3_br3w3d_c1ph3r}