Skip to content

Latest commit

 

History

History
62 lines (43 loc) · 1.97 KB

README.md

File metadata and controls

62 lines (43 loc) · 1.97 KB

Halloween

Category

Crypto

Description

Boo! Do you believe in ghosts ? I sure don't.

Host : nc crypto.heroctf.fr 9001
Format : Hero{flag}
Author : Alol

Files

Write up

The challenge prints the encrypted flag before repeatedly encrypting user-submitted data. On the surface this is not vulnerable code, the key and nonce are generated properly meaning that the keystream should be 2**68 bytes long before the counter wraps around and the keystream repeats. We need to dig deeper.

Looking into the source code we find this function that, supposedly, increments the counter.

# https://github.com/drobotun/gostcrypto/blob/master/gostcrypto/gostcipher/gost_34_13_2015.py#L841C5-L841C8
    def _inc_ctr(self, ctr: bytearray) -> bytearray:
        internal = 0
        bit = bytearray(self.block_size)
        bit[self.block_size - 1] = 0x01
        for i in range(self.block_size):
            internal = ctr[i] + bit[i] + (internal << 8)
            ctr[i] = internal & 0xff
        return ctr

The screenshot below taken from one of the tickets I handled illustrates clearly what's wrong.

The counter isn't incremented properly, effectively downgrading it from a 64 bit integer to a 8 bit integer. This, in turn, means that the keystream is now only 4096 bytes long. We can perform a chosen plaintext attack to recover the full keystream and decrypt the flag.

from pwn import *

BS = 16
# io = process(["python3", "chall.py"])
io = remote("crypto.heroctf.fr", 9001)

io.recvuntil(b"It's almost Halloween, time to get sp00")
flag = bytes.fromhex(io.recvuntil(b"00ky")[:-4].decode())
io.recvline()

io.sendline(b"00" * BS * 256)
keystream = bytes.fromhex(io.recvlineS())

print(xor(flag, keystream[-(1 + len(flag) // BS) * BS :])[: len(flag)])

Flag

Hero{5p00ky_5c4ry_fl4w3d_cryp70_1mpl3m3n74710ns_53nd_5h1v3r5_d0wn_y0ur_5p1n3}