SuperAES
Crypto challenge created by me for GCC CTF 2024
SuperAES
I made this challenge for GCC CTF 2024, it’s supposed to be medium difficulty.
The problem
The source code can be found here. The challenge implements an AES-CTR with a special way to update the nonce.
The nonce comes from an LCG with some special parameters :
m = 288493873028852398739253829029106548736
a = int(time.time())
b = a%16
s = random.randint(1,m-1)
The interesting thing is that we can more or less choose a and b.
The solution
The vulnerability lies in the LCG, like a lot of the time with AES-CTR. The “key” thing to notice is that the modulus is special, indeed m = 56^22
. Now we need to use that to make the “SuperAES” weak.
With an LCG we have a few options :
Next, we need our multiplier to be a multiple of 56 so that our seed will, iteration after iteration, becomes a multiple of power of 56 until 56^22
.
We now have most of our elements, for us to have b = 0
and a%56 = 0
we need to have :
a % 16 == 0 and a % 56 == 0 => a % lcm(16,56) == 0 => a % 112 == 0
And because the multiplier comes from the current timestamp we can choose to connect to the server at a specific time when the timestamp will be a multiple of 112.
With that done, it’s time to think about how we’re going to get the flag. We know that all the output of the LCG after the 23rd iteration will be 0, which will make the keystream be the same for all the outputs of AES-CTR. Then with the known part of the flag we can retrieve some part of the keystream and hence of the flag with a little loop.
Solve script
from pwn import remote
import time
from itertools import cycle
while int(time.time())%112 !=0:
time.sleep(0.2)
r = remote("0.0.0.0",1337)
print("time : ",int(time.time())%112)
r.recvuntil(b"?")
r.sendline(b"49")
c = bytes.fromhex(r.recvline().decode())
crib = b"GCC{"+b"\x00"*28 + b"}"
keystream = bytes([cc^dd if dd != 0 else 0 for cc,dd in zip(c,cycle(crib))])
print(keystream)
keystream = keystream[16*23:]
keystream_block = [0 for _ in range(16)]
for i,k in enumerate(keystream):
if k != 0:
keystream_block[i%16] = k
print(keystream_block)
print(bytes([cc^dd for cc,dd in zip(c[16*23:],cycle(keystream_block))]))
If you find any issue or have any question, dm me on discord stating the subject in the first message.