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 = 16flagBloksCount = 2flagSize = 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/python3import sysimport osfrom Crypto.Cipher import AESfrom base64 import b64decodeimport socketimport time
flagSize = 32
blocksize = 16
flagBloksCount = 2
# reconnection socketclass 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 offsetdef 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 functiondef 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()```