AESential Lesson
465
Thought I’d give you an essential lesson to how you shouldn’t get input for AES in ECB mode.
nc 18.218.238.95 12345
Learning by the description we can already know the cryptography used here is AES ECB mode, we are provided a file with the encryption process:
1 | #!/usr/bin/env python2 |
By looking at the script we can already see, the flag has 32 bytes of size, the key as well, the encryption method processes as follows, since it’s ECB we know the plaintext will be split in blocks of 16 bytes and for each block will be applied the AES encryption function with the key provided in the file:
Before the encryption we can see the program asks for an input to be encrypted, the input is concatenated with the flag and then it’s applied some padding to fill the last blocks, for example imagine the padding character is 1, the sent input is ‘A’ and the respective flag is ‘TUCTF{MY_B34UT1FULL_FL4G_L0L_XD}’ the padding will be applied to the plaintext as follows:
1 | Block1 Block2 Block3 Block4 |
After this as described before the encryption is applied to each block with a key, now we can perform an attack without needing the key, we can bruteforce the flag byte by byte, imagine we sent an input of 15 ‘A’s the first block of the plaintext and ciphertext will be as follows (The key in this examples is a random key chosen by me):
1 | Plaintext |
Now we know that input + 1st_char_of_the_flag its corresponding ciphertext is the 1st block, now we just need to send the inputs for every character possible until we match the 1st ciphertext we got with the input of 14 “A”s:
1 | Plain Block1 Cipher Block1 |
As we can see from above we matched the 82b094debf0605ef9d46ad671ac3605d, we now know the first character of the flag is T, well we already knew that! but this was just a confirmation :), using this method we can get the first 16 bytes of the flag, but how can we get the last 16 ? For this we need to find the padding character we can do this by sending an A to the server, imagine the padding character is _:
1 | Plaintext |
Now we want to extract the block 3 or the block 4 cipher, block 3 also works because we actually know that the last byte of the flag is }, so making an example using block 3 2a5831079c0a0591601f25278f4623f3
1 | Plain Block1 Cipher Block1 |
Now that we have the padding character, we need to work something similar on how we got the 1st part of the flag, but now instead of comparing with the 1st block of the cipher we want to compare it with the 3rd block by sending an A we get the 3rd block cipher:
1 | Plaintext |
Now we test all characters until we match this block3:
1 | Plain Block1 Cipher Block1 |
With this we get the last character of the flag, now repeat this for the rest of the characters, I’ll give one more example how to get the next character, sending “AA” as input:
1 | Plaintext |
Now brute force it like this:
1 | Plain Block1 Cipher Block1 |
Repeat this and you’ll get every character of the flag, the python script I implemented for this:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53from pwn import *
import re
import string
import time
r = remote('18.218.238.95', 12345)
#r = process('./redacted.py')
print r.recvuntil('Enter your text here: ')
flag = ''
if flag == '':
r.sendline('A'*15)
encrypted_first = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
for i in range(16):
for c in string.printable:
r.sendline('A'*(15-i) + flag+c)
encrypted = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
if encrypted[:32] == encrypted_first[:32]:
flag += c
r.sendline('A'*(15-i-1))
encrypted_first = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
break
padding_char = ''
if padding_char == '':
r.sendline('A'*1)
encrypted_first = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
time.sleep(0.1)
for x in string.printable:
p = '}'+ x*15
r.sendline(p)
encrypted = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
if encrypted[:32] == encrypted_first[64:64+32]:
padding_char = x
break
r.sendline('}'*1)
encrypted_first = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
flag_part2 = ''
for i in range(16,0,-1):
for c in string.printable:
p = c+flag_part2 + (i-1)*padding_char
r.sendline(p)
encrypted = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
if encrypted[:32] == encrypted_first[64:64+32]:
flag_part2 = c + flag_part2
r.sendline('A'*(17-i+1))
encrypted_first = re.findall(r'[a-f0-9]{32,}', r.recvuntil('Enter your text here: '))[0]
print flag+flag_part2
break
Running it
1 | python reeas.py |