Skip to content

Latest commit

 

History

History
289 lines (240 loc) · 12.2 KB

Securechannel.md

File metadata and controls

289 lines (240 loc) · 12.2 KB

[494 pts] Secure Channel

Description:

You are able to watch the encrypted chat of Alice and Bob. They are talking about the flag right now. Can you get the flag from them?

P.S.: Alice and Bob once have said that they will communicate with some trivial encoding, so people will not notice their message at first glance. Although, people will know it if they search it on the internet! Sure, all of it is printable characters.

Watch the conversation: nc 103.152.242.56 8231

Talk with Alice: nc 103.152.242.56 8230

Talk with Bob: nc 103.152.242.56 8232

Author: prajnapras19

secrets.py: https://github.com/kisanakkkkk/writeup/blob/main/compfest%202021/Secure%20Channel/secrets.py

alice-bob.py: https://github.com/kisanakkkkk/writeup/blob/main/compfest%202021/Secure%20Channel/alice-bob.py

talk-with-alice.py: https://github.com/kisanakkkkk/writeup/blob/main/compfest%202021/Secure%20Channel/talk-with-alice.py

talk-with-bob.py: https://github.com/kisanakkkkk/writeup/blob/main/compfest%202021/Secure%20Channel/talk-with-bob.py

TL;DR

Solution:

  1. find bob's secret by brute force & compare our custom secret
  2. get bob's key by doing function make_private_part(bobsecret, alicepublic, p)
  3. create unpad function which remove unprintable string
  4. decrypt each messages from the convo, unpad, decode with ascii85
  5. profit

Summary

We are given 4 files:

  1. Secrets.py which contains the public and private part generator and other function, also class descriptions of the person named bob & alice, and ourselves.
  2. Alice-bob.py which contains the code for the conversation between bob & alice about the flag we are looking for.
  3. Talk-with-alice.py which contains the code when we want to have a conversation with alice.
  4. Talk-with-bob.py which is the same as Talk- with-Alice.py, but with person bob.

Let's jump right into the interesting part, the secrets.py file shows that each person has a SECRET object whose value is static but hidden. It says that Bob's character SECRET value is in the range of 2 - 100, which means it's small enough to do bruteforce. Let's try to get Bob SECRET by digging deeper the file talk-with-bob.py

Finding Bob's SECRET

In talk-with-bob.py, we will be asked to enter our SECRET (you can fill whatever you want, base64-encoded) and the value of g in base64-encoded form, with a note the value must be g > 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF. This g value will be "calculated" with the respective SECRET and the p value (obtained from getPrime function) and will become the respective public part which will be printed later. So from here we can brute-force our SECRET from 2 - 100, then compare our public part and Bob's public part and if they are the same, that means we get Bob's secret value. Don't mind the g too much, as long as we follow the conditions where g value is greater than 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF (when doing bruteforce I use g value of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1). With the following code:

import binascii
import base64
from pwn import *
from Crypto.Util.number import getPrime, bytes_to_long  as bl, long_to_bytes as lb
from Crypto.Cipher import AES

def getBobSecret():
	for i in range(2, 100):
		r = remote('103.152.242.56', 8232)
		mysecret = base64.b64encode(lb(i))
		opener = base64.b64encode(lb(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1))
		r.sendline(mysecret)
		r.sendline(opener)
		r.recvuntil("p: ")
		p = r.recvline().strip()
		r.recvuntil("Bob's public part: ")
		bobPub = r.recvline().strip()
		r.recvuntil("Your public part: ")
		myPub = r.recvline().strip()
		if bobPub == myPub:
			print("bobSecret is", i)
			return i
		else:
			r.close()

if __name__ == "__main__":
	bobSecret = getBobSecret()

Our public part and Bob's public part are the same, meaning that the SECRET we entered and Bob's SECRET are also the same, which is 73. With this information, we can "reveal" the contents of alice and bob's conversations that were initially encrypted.

Finding Bob's KEY & Decrypt the Messages

If we look at the Secrets.py file, every time a person wants to send a message, the message will be encrypted with AES_CBC in the send_message() function which requires a KEY from the sender. This KEY is generated by the make_private_part() function which requires the SECRET of the sender, the Public Part of the recipient, and the p value provided at the beginning (see the formula in the alice-bob.py file). We already have Bob's SECRET, Alice's PublicPart (printed in the program, just decode it and convert it to decimal), and the p value which has also been printed. Just generate the KEY based on the make_private_part() function, then decrypt the message based on the receive_message() function.

import binascii
import base64
from pwn import *
from Crypto.Util.number import getPrime, bytes_to_long  as bl, long_to_bytes as lb
from Crypto.Cipher import AES

def getBobSecret():
	for i in range(2, 100):
		r = remote('103.152.242.56', 8232)
		mysecret = base64.b64encode(lb(i))
		opener = base64.b64encode(lb(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1))
		r.sendline(mysecret)
		r.sendline(opener)
		r.recvuntil("p: ")
		p = r.recvline().strip()
		r.recvuntil("Bob's public part: ")
		bobPub = r.recvline().strip()
		r.recvuntil("Your public part: ")
		myPub = r.recvline().strip()
		if bobPub == myPub:
			print("bobSecret is", i)
			return i
		else:
			r.close()

def receive_message(key, enc_message):
	iv = enc_message[:16]
	enc = enc_message[16:]
	cipher = AES.new(key, AES.MODE_CBC, iv)
	msg = cipher.decrypt(enc)
	return msg

def createKey(secret, gx, p):
	key = lb(pow(gx, secret, p) % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
	while (len(key) != 16):
		key += b'\x01'
	return key

if __name__ == "__main__":
	# bobSecret = getBobSecret()
	bobSecret = 73
	r = remote('103.152.242.56', 8231)
	opener = base64.b64encode(lb(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1))
	r.sendline(opener)
	r.recvuntil("p:")
	p = int(r.recvline().strip().decode())
	r.recvuntil("Alice\'s public part:")
	alicePub = bl(base64.b64decode(r.recvline().strip().decode()))
	key = createKey(bobSecret, alicePub, p)
	print('bobKey is', key)
	for i in range(15):
		r.recvuntil("Messages from Alice:\n")
		AliceMessage = r.recvline().strip()
		decrypted = receive_message(key, base64.b64decode(AliceMessage))
		print('a:', decrypted)
		r.recvuntil("Messages from Bob:\n")
		bobMessage = r.recvline().strip()
		decrypted = receive_message(key, base64.b64decode(bobMessage))
		print('b:', decrypted)

*Note: A key from one of the person can be used to decrypt other person messages, so we can also read Alice's message with Bob's Key

Why is the output seems ... weird? That's because the message is still padded (see the pad() function in the secrets.py file). Simply put, this pad function will add random unprintable string characters between the original message characters. How to delete it? just make an unpad() function which contains looping per character of the message and returns all the characters which is in string.printable.

import binascii
import base64
from pwn import *
from Crypto.Util.number import getPrime, bytes_to_long  as bl, long_to_bytes as lb
from Crypto.Cipher import AES

def getBobSecret():
	for i in range(2, 100):
		r = remote('103.152.242.56', 8232)
		mysecret = base64.b64encode(lb(i))
		opener = base64.b64encode(lb(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1))
		r.sendline(mysecret)
		r.sendline(opener)
		r.recvuntil("p: ")
		p = r.recvline().strip()
		r.recvuntil("Bob's public part: ")
		bobPub = r.recvline().strip()
		r.recvuntil("Your public part: ")
		myPub = r.recvline().strip()
		if bobPub == myPub:
			print("bobSecret is", i)
			return i
		else:
			r.close()

def unpad(msg):
    res = ""
    for i in msg:
        if chr(i) in string.printable:
            res+=chr(i)
    return res

def receive_message(key, enc_message):
	iv = enc_message[:16]
	enc = enc_message[16:]
	cipher = AES.new(key, AES.MODE_CBC, iv)
	msg = cipher.decrypt(enc)
	return unpad(msg)

def createKey(secret, gx, p):
	key = lb(pow(gx, secret, p) % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
	while (len(key) != 16):
		key += b'\x01'
	return key

if __name__ == "__main__":
	# bobSecret = getBobSecret()
	bobSecret = 73
	r = remote('103.152.242.56', 8231)
	opener = base64.b64encode(lb(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1))
	r.sendline(opener)
	r.recvuntil("p:")
	p = int(r.recvline().strip().decode())
	r.recvuntil("Alice\'s public part:")
	alicePub = bl(base64.b64decode(r.recvline().strip().decode()))
	key = createKey(bobSecret, alicePub, p)
	print('bobKey is', key)
	for i in range(15):
		r.recvuntil("Messages from Alice:\n")
		AliceMessage = r.recvline().strip()
		decrypted = receive_message(key, base64.b64decode(AliceMessage))
		print('a:', decrypted)
		r.recvuntil("Messages from Bob:\n")
		bobMessage = r.recvline().strip()
		decrypted = receive_message(key, base64.b64decode(bobMessage))
		print('b:', decrypted)

Why is it still weird -.-? The description said that the messages they exchange with each other are still encoded in a "specific" encoding. After searching for a while, it turns out that the message is encoded with ascii85, and we can decode it using base64.a85decode(msg) python function. We just need to refine our code, read the conversation, and get the flag.

Full Code: https://github.com/kisanakkkkk/writeup/blob/main/compfest%202021/Secure%20Channel/solve.py

Flag = COMPFEST13{4fd29464a28a1b39559f4fc500b41c4b17ec8ad74512394a830b51506AIUEOuh_f8facf99fe}