What the Flip?! Writeup (Dawg CTF 2021)

Dr.DONN4

Type: Crypto

Difficulty: Medium

Prompt: No Prompt

Solution

We are given a server and a script app.py.

app.py

import socketserver
import socket, os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Random import get_random_bytes
from binascii import unhexlify
from secret import FLAG


wlcm_msg ='########################################################################\n'+\
		  '#                            Welcome                                   #\n'+\
		  '#             All connections are monitored and recorded               #\n'+\
		  '#      Disconnect IMMEDIATELY if you are not an authorized user!       #\n'+\
		  '########################################################################\n'


key = get_random_bytes(16)
iv = get_random_bytes(16)
def encrypt_data(data):
	padded = pad(data.encode(),16,style='pkcs7')
	cipher = AES.new(key, AES.MODE_CBC,iv)
	enc = cipher.encrypt(padded)
	return enc.hex()

def decrypt_data(encryptedParams):
	cipher = AES.new(key, AES.MODE_CBC,iv)
	paddedParams = cipher.decrypt( unhexlify(encryptedParams))
	print(paddedParams)
	if b'admin&password=goBigDawgs123' in unpad(paddedParams,16,style='pkcs7'):
		return 1
	else:
		return 0

def send_msg(s, msg):
	enc = msg.encode()
	s.send(enc)

def main(s):

	send_msg(s, wlcm_msg)

	send_msg(s, 'username: ')
	user = s.recv(4096).decode().strip()

	send_msg(s, user +"'s password: " )
	passwd = s.recv(4096).decode().strip()

	msg = 'logged_username=' + user +'&password=' + passwd

	try:
		assert('admin&password=goBigDawgs123' not in msg)
	except AssertionError:
		send_msg(s, 'You cannot login as an admin from an external IP.\nYour activity has been logged. Goodbye!\n')
		raise

	send_msg(s, "Leaked ciphertext: " + encrypt_data(msg)+'\n')
	send_msg(s,"enter ciphertext: ")

	enc_msg = s.recv(4096).decode().strip()

	try:
		check = decrypt_data(enc_msg)
	except Exception as e:
		send_msg(s, str(e) + '\n')
		s.close()

	if check:
		send_msg(s, 'Logged in successfully!\nYour flag is: '+ FLAG)
		s.close()
	else:
		send_msg(s, 'Please try again.')
		s.close()


class TaskHandler(socketserver.BaseRequestHandler):
	def handle(self):
		main(self.request)

if __name__ == '__main__':
	socketserver.ThreadingTCPServer.allow_reuse_address = True
	server = socketserver.ThreadingTCPServer(('0.0.0.0', 3000), TaskHandler)
	server.serve_forever()

Now let us peek into the code and see what we get.

It is a AES CBC mode and seeing the code properly we see it is one of those bitflipping challenges in which we have to flip a bit.

Now,let us login to the server

Cleary we can’t login with admin and the given password.So,we login with bdmin and the given password.It will we easier to flip the bit in the username. And,we get the cipher text.Now,we read the code and see that the cipher text is actually the hex of username='user'&password='pass'

and also there maybe some padding as well.

Now,we check the length of our cipher text it’s 96 which means 48 characters should be there in our input format as mentioned above.But there is only 44 so there is a padding of 4 characters. Thus,we have to flip the first bit ofcourse as we are allowed to do only that so that bdmin changes into admin and then put our cipher text answer.

Now,I have done that by this process

we find that decrypted(43) corrsponds b in the cipher text.

Now,0x6e ^ dec(b)=0x62 which is the original hex of b. Thus, we can say that hex(0x6e ^0x62)=dec(b)

Once we find this we simply XOR: hex(dec(b) ^ hex('a')) to get the bit to replace the first bit

Then, we put the new cipher text to get the flag

I haven’t got into any of the technical stuff of AES-CBC,but I have just explained the Bitflip attack.

flag=DawgCTF{F1ip4J0y}