Provably Secure 1 and 2
Wanted to try out something different, so I decided to use Motion Canvas to try and make a video writeup for the 2 easy crypto challs…
Only to end up spending 2 weeks to make a 5 minute video with no audio lol.
Here’s a short text writeup for the both of them:
Provably Secure 1
Server doesn’t properly handle checking already encrypted ciphertext, so we can literally just treat it like an encryption/decryption oracle.
We have to guess m_bit
128 times depending on which message is encrypted, so just using two unique messages is fine.
Shortcut: use 0
and 1
so we can just cast to int and directly send it to the server.
from pwn import *
from tqdm import tqdm
# nc mc.ax 31493
r = remote('mc.ax', 31493)
for i in tqdm(range(128)):
r.recvuntil(b'Action:')
r.sendline(b'1')
r.recvuntil(b'm0 (16 byte hexstring):')
r.sendline(b'0'*32)
r.recvuntil(b'm1 (16 byte hexstring):')
r.sendline(b'0'*31 + b'1')
ct = r.recvline().strip()
r.recvuntil(b'Action:')
r.sendline(b'2')
r.recvuntil(b'ct (512 byte hexstring):')
r.sendline(ct)
m = r.recvline().strip()
m = int(m) # 0 or 1
r.recvuntil(b'Action:')
r.sendline(b'0')
r.recvuntil(b'm_bit guess:')
r.sendline(str(m).encode())
r.interactive()
Provably Secure 2
Actual exploitation of encryption scheme, but same setup, just with fatal bug fixed.
There is two keys, so we can split scheme into two seperate functions that occur on the same pair.
Treat encryption and decryption as black box functions: (r
is random bytes, m_b
is chosen message)
We have free control over m_1
, m_2
for encryption, and c_1
, c_2
for decryption.
However, we don’t know r
.
But consider the following construction where we swap ciphertext pairs: (apologies for spaghetti but hopefully you get the idea)
Notice that if m_b1
and m_b2
are the same, then is the same as .
So all we have to do is vary one message, and recover m_bit
based on whether the swapped decryption changes or not.
from pwn import *
from tqdm import tqdm
# nc mc.ax 31497
r = remote('mc.ax', 31497)
for i in tqdm(range(128)):
r.recvuntil(b'Action:')
r.sendline(b'1')
r.recvuntil(b'm0 (16 byte hexstring):')
r.sendline(b'0'*32)
r.recvuntil(b'm1 (16 byte hexstring):')
r.sendline(b'0'*31 + b'1')
ct1 = r.recvline().strip()
r.recvuntil(b'Action:')
r.sendline(b'1')
r.recvuntil(b'm0 (16 byte hexstring):')
r.sendline(b'0'*32 )
r.recvuntil(b'm1 (16 byte hexstring):')
r.sendline(b'0'*31 + b'2')
ct2 = r.recvline().strip()
dt1 = ct1[:512] + ct2[512:]
dt2 = ct2[:512] + ct1[512:]
r.recvuntil(b'Action:')
r.sendline(b'2')
r.recvuntil(b'ct (512 byte hexstring):')
r.sendline(dt1)
m1 = r.recvline().strip()
r.recvuntil(b'Action:')
r.sendline(b'2')
r.recvuntil(b'ct (512 byte hexstring):')
r.sendline(dt2)
m2 = r.recvline().strip()
m = int(m1 != m2)
r.recvuntil(b'Action:')
r.sendline(b'0')
r.recvuntil(b'm_bit guess:')
r.sendline(str(m).encode())
r.interactive()
Video
Here’s the video if you want to watch it, but its basically the same as above with cooler visuals.