Rating:
This is an AES ECB challenge.
Which means that for tow identical block the two ciphered block will be the same.
The string shape that will be ciphered is retrieved from the source code:
`Flag + input + key +padding`
Now the key can be recovered.
To do that, the size of a cipher block must be find:
This is easy: 16 (it was written in the source code)
Now the size of a possible offset must be found, because the input data must be placed in a new block:
With some manually down test, the size of the flag part was estimated to be at least 2 block. That is down by the input of random data and checking the untouched part.
To do that a function is created it take as argument the connection socket:
```python
blocksize = 16
flagBloksCount = 2
flagSize = blocksize*flagBloksCount
def findOffset (s ):
OffsetChar = '_'
offsetLen = 0
offsetStr = ""
data = 'A'*blocksize*2;
while offsetLen < blocksize:
offsetStr = OffsetChar*offsetLen
s.send((offsetStr+data+"\n").encode())
Rdata = s.recv(1024)
Rdatat = Rdata.decode().split('\n')
rd = b64decode(Rdatat[0])
if (rd[flagBloksCount*blocksize] == rd[(flagBloksCount+1)*blocksize] ):
print(f"offset fond : {offsetLen}")
return offsetLen
offsetLen += 1
pass
print("offset error")
exit(1)
```
Here there is no offset
From that point one letter can be recover by filling the input with $blocksize -1$ characters and get the cipher value of that block through the oracle. Then tests are made to find which character from the key was put inside the block. To do the test the oracle is input with $blocksize -1$ characters plus the character to test and the resulting block is compared to initial block.
To get the full key the operation is repeated until the key is found.
But after a certain amount of try we are kick out, so a reconnection system is implemented
After the key is recovered the flag and the key are decoded:
`DUCTF{ECB_M0DE_K3YP4D_D474_L34k}!_SECRETSOURCE_!`
the key : `!_SECRETSOURCE_!`
the flag : `DUCTF{ECB_M0DE_K3YP4D_D474_L34k}`
The full code :
```python
#!/usr/bin/python3
import sys
import os
from Crypto.Cipher import AES
from base64 import b64decode
import socket
import time
flagSize = 32
blocksize = 16
flagBloksCount = 2
# reconnection socket
class tcpSocket:
"""docstring for mSocket"""
def __init__(self):
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.a = 0
def connect(self, host, port):
self.host = host
self.port = port
self.s.connect((self.host, self.port))
def reconect(self,st =""):
print("recon"+st)
self.s.close()
time.sleep(5)
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((self.host, self.port))
self.s.recv(1024)
def send(self, data):
try:
if(self.a > 128):
self.reconect(" wanted")
self.a = 0
self.s.send(data)
self.a +=1
time.sleep(0.25)
except:
print (self.a)
self.reconect(" error")
self.a = 0
self.send(data)
def recv(self, size):
d = self.s.recv(size)
if len(d) == 0:
print (self.a)
return d
def close(self):
self.s.close()
# function to find eventual offset
def findOffset (s ):
OffsetChar = '_'
offsetLen = 0
offsetStr = ""
data = 'A'*blocksize*2;
while offsetLen < 52:
offsetStr = OffsetChar*offsetLen
s.send((offsetStr+data+"\n").encode())
Rdata = s.recv(1024)
Rdatat = Rdata.decode().split('\n')
rd = b64decode(Rdatat[0])
if (rd[flagBloksCount*blocksize] == rd[(flagBloksCount+1)*blocksize] ):
print(f"offset fond : {offsetLen}")
return offsetLen
offsetLen += 1
pass
print("offset error")
exit(1)
# define an simple oracle function
def oracle(s,data):
s.send((data+"\n").encode())
Rdata = s.recv(1024)
Rdatat = Rdata.decode().split('\n')
rd = b64decode(Rdatat[0])
return rd
def printByte(b, g=16):
out = ""
i = 0
m = len(b)
while i < m:
out += b[i:i+g].hex()
out += " "
i+=g
print(out)
# function to find a single character
def solvLetter(s, offsetLen, found, bp, bm):
offset = 'B'*offsetLen
static = '~'*(blocksize-len(found)-1)
d = oracle(s,offset+static)
target = d[bp:bm]
for i in range(33,128):
val = (offset+static+found+chr(i))
#print(val)
ct = oracle(s, val)
if (ct[bp:bm] == target):
print(f"found : {chr(i)} => {i}")
print(f"{found}{chr(i)}")
return chr(i)
return None
#function to find the key (use solvLetter)
def solvKey(s):
offsetLen = findOffset(s)
found = ""
bp = flagBloksCount*blocksize
bm = (flagBloksCount+1)*blocksize
for i in range(0,16):
found += solvLetter(s,offsetLen,found,bp,bm)
print(f"key : \"{found}\", length = {len(found)}")
return found
def main():
s = tcpSocket()
s.connect("pwn-2021.duc.tf", 31914)
print(s.recv(1024))
key = solvKey(s).encode()
# decoding flag
data = oracle(s,"")
printByte(data)
cipher = AES.new(key, AES.MODE_ECB)
pt = cipher.decrypt(data)
print(pt)
s.close()
exit(0)
if __name__ == "__main__":
main()
```