Rating: 5.0
#!/usr/bin/env python
from base64 import b64decode
from base64 import b64encode
import socket
import multiprocessing
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
import hashlib
import sys
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, data):
iv = get_random_bytes(AES.block_size)
self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
return b64encode(iv + self.cipher.encrypt(pad(data,
AES.block_size)))
def encrypt_iv(self, data, iv):
self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
return b64encode(iv + self.cipher.encrypt(pad(data,
AES.block_size)))
def decrypt(self, data):
raw = b64decode(data)
self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)
flag = open("flag", "rb").read().strip()
COMMAND = [b'test',b'show']
def run_server(client, aes_key, token):
client.send(b'test Command: ' + AESCipher(aes_key).encrypt(token+COMMAND[0]) + b'\n')
client.send(b'**Cipher oracle**\n')
client.send(b'IV...: ')
iv = b64decode(client.recv(1024).decode().strip())
client.send(b'Message...: ')
msg = b64decode(client.recv(1024).decode().strip())
client.send(b'Ciphertext:' + AESCipher(aes_key).encrypt_iv(msg,iv) + b'\n\n')
while(True):
client.send(b'Enter your command: ')
tt = client.recv(1024).strip()
tt2 = AESCipher(aes_key).decrypt(tt)
client.send(tt2 + b'\n')
if tt2 == token+COMMAND[1]:
client.send(b'The flag is: ' + flag)
client.close()
break
if __name__ == '__main__':
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 16001))
server.listen(1)
while True:
client, address = server.accept()
aes_key = get_random_bytes(AES.block_size)
token = b64encode(get_random_bytes(AES.block_size*10))[:AES.block_size*10]
process = multiprocessing.Process(target=run_server, args=(client, aes_key, token))
process.daemon = True
process.start()
Reading the source code, we can see that the idea is to replace the last block of encryption with show
instead of test
.
In addition, the server allows us to encrypt a string with a given IV.
When you look at the following diagram of AES_CBC decryption, you see that the last block is basically decrypted and then XORed with the previous block. So if we encrypt the string show
with the previous block as the IV we would get the correct final block.
Then we just need to replace that in the string that we were given at the start from the server and we can go ahead and send that to the serevr.
from base64 import b64decode
from base64 import b64encode
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from pwn import *
import hashlib
import sys
r = remote("35.200.115.41", 16001, level='debug')
line = str(r.recvline().replace(b'\n', b'')).split(": ")
http://)
r.recv()
testcommand = line[1]
iv = b64encode(b64decode(testcommand)[:16])
token = b64encode(b64decode(testcommand)[16:AES.block_size*10+16])
tokenlast = b64decode(token)[-16:]
r.sendline(b64encode(tokenlast))
r.sendline(b64encode(b'show'))
line = str(r.recvline().replace(b'\n', b'')).split(":")
encryptedshow = line[2]
payload = b64encode(b64decode(iv)+b64decode(token)+b64decode(encryptedshow)[16:])
r.recvuntil(b'Enter your command: ')
r.sendline(testcommand)
r.recvuntil(b'Enter your command: ')
r.sendline(payload)
r.recv()
r.recv()
show
with the IV and the lastblock that have it work and send the IV at the start of the string sent by the server as the encryption IV but this didn't work because show
was XORed with the lastblock before encryptionshow
to make it have the right length but that didn't work because the data was additionally padded