Post

xorxorxor

xorxorxor

challenge.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat challenge.py 
#!/usr/bin/python3
import os
flag = open('flag.txt', 'r').read().strip().encode()

class XOR:
    def __init__(self):
        self.key = os.urandom(4)
    def encrypt(self, data: bytes) -> bytes:
        xored = b''
        for i in range(len(data)):
            xored += bytes([data[i] ^ self.key[i % len(self.key)]])
        return xored
    def decrypt(self, data: bytes) -> bytes:
        return self.encrypt(data)

def main():
    global flag
    crypto = XOR()
    print ('Flag:', crypto.encrypt(flag).hex())

if __name__ == '__main__':
    main()

The encrypt method XORs every byte of the input with the four-byte key in repeating 4-byte blocks (i.e. key[0] ^ data[0], key[1] ^ data[1], … key[3] ^ data[3], key[0] ^ data[4], …);

Understanding encrypt()

key = [k0, k1, k2, k3] For every byte in the ciphertext you pick the bucket whose index is the remainder of the current position divided by 4 (that’s what the modulo % does). Visual table for the 24-byte ciphertext index i : 0 1 2 3 4 5 6 7 8 9 10 11 … 20 21 22 23 i % 4 : 0 1 2 3 0 1 2 3 0 1 2 3 … 0 1 2 3 key used: k0 k1 k2 k3 k0 k1 k2 k3 k0 k1 k2 k3 … k0 k1 k2 k3 So the XOR operations expand to plain[0] = cipher[0] ^ key[0] # k0 plain[1] = cipher[1] ^ key[1] # k1 plain[2] = cipher[2] ^ key[2] # k2 plain[3] = cipher[3] ^ key[3] # k3 plain[4] = cipher[4] ^ key[0] # back to k0 plain[5] = cipher[5] ^ key[1] … plain[23] = cipher[23] ^ key[3] In other words, i % 4 is the “spinning index” that keeps wrapping around 0-3 so the 4-byte key repeats over the whole ciphertext.


The decrypt method is literally the same function (because XOR with the same key is its own inverse).

So how do we decrypt the output.txt?

We first get the key that was used to XOR. Since we know the key is 4-byte self.key = os.urandom(4) and we also know the flag format would be: HTB{}, we will use the first 4byte of flag format HTB{ and XOR it. Afterwards with the key we XOR the cipher text in a repeating 4-byte block.

1
2
3
4
5
6
7
8
9
10
cat decrypt.py   
cipher_hex = "134af6e1297bc4a96f6a87fe046684e8047084ee046d84c5282dd7ef292dc9"
cipher = bytes.fromhex(cipher_hex)

format = b"HTB{"
key = bytes([cipher[i] ^ format[i] for i in range(4)])

decrypt = bytes([cipher[i] ^ key[i % 4] for i in range(len(cipher)) ])

print(decrypt.decode())

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