Rating:
## Wolf
### Challenge
When we connect to the server, it chooses a random length `niv`
between 1 and 11, and a random nonce of that length. We can request
the flag encrypted using AES-GCM with the key `HungryTimberWolf` and
that IV, and ask for the server to encrypt our input with the same
parameters.
```python
#!/usr/bin/env python3
from Cryptodome.Cipher import AES
import os, time, sys, random
from flag import flag
passphrase = b'HungryTimberWolf'
def encrypt(msg, passphrase, niv):
msg_header = 'EPOCH:' + str(int(time.time()))
msg = msg_header + "\n" + msg + '=' * (15 - len(msg) % 16)
aes = AES.new(passphrase, AES.MODE_GCM, nonce = niv)
enc = aes.encrypt_and_digest(msg.encode('utf-8'))[0]
return enc
def die(*args):
pr(*args)
quit()
def pr(*args):
s = " ".join(map(str, args))
sys.stdout.write(s + "\n")
sys.stdout.flush()
def sc():
return sys.stdin.readline().strip()
def main():
border = "+"
pr(border*72)
pr(border, " hi wolf hunters, welcome to the most dangerous hunting ground!! ", border)
pr(border, " decrypt the encrypted message and get the flag as a nice prize! ", border)
pr(border*72)
niv = os.urandom(random.randint(1, 11))
flag_enc = encrypt(flag, passphrase, niv)
while True:
pr("| Options: \n|\t[G]et the encrypted flag \n|\t[T]est the encryption \n|\t[Q]uit")
ans = sc().lower()
if ans == 'g':
pr(f'| encrypt(flag) = {flag_enc.hex()}')
elif ans == 't':
pr("| Please send your message to encrypt: ")
msg_inp = sc()
enc = encrypt(msg_inp, passphrase, niv).hex()
pr(f'| enc = {enc}')
elif ans == 'q':
die("Quitting ...")
else:
die("Bye ...")
if __name__ == '__main__':
main()
```
### Solution
Since `niv` is uniformly random, in one out of 11 connections the nonce will only be one byte long. If this happens, we can easily bruteforce the encrypted flag: we already know the key, and can try all 256 nonce values to see if one of them gives a plaintext that looks like a flag.
A quick Python script collects encrypted flags, which we can dump into a single file to process later:
```python
#! /usr/bin/env python
from pwn import *
serv = pwnlib.tubes.remote.remote('01.cr.yp.toc.tf', 27010)
serv.sendline('g')
serv.sendline('q')
for line in serv.recvall().decode('utf-8').split('\n'):
if 'encrypt(flag)' in line:
print(line.rstrip().split()[-1])
```
The solver isn't much longer:
```python
#! /usr/bin/env python
from Cryptodome.Cipher import AES
import binascii
def trysolve(line):
for iv in range(256):
a = AES.new(b'HungryTimberWolf', AES.MODE_GCM, nonce=bytes([iv]))
flag = a.decrypt(binascii.unhexlify(line))
if b'CTF' in flag:
print(flag)
with open('flags.txt', 'r') as f:
for line in f.readlines():
if not line.startswith('['):
trysolve(line.rstrip())
```
##### Flag
`CCTF{____w0lveS____c4n____be____dan9er0uS____t0____p3oplE____!!!!!!}`