⬅ BACK
DawgCTF 2020 Writeups

Writeups for some DawgCTF 2020 challenges. I participated on team misc and we came #2.


Criss Cross, Criss Cross

crypto (450pts)

see:{LET ME IIIIIIIIIIIIIIN!}

crypto.ctf.umbccd.io 13375

(no brute force is required for this challenge)

Author: pleoxconfusa

client.py:

# -*- coding: utf-8 -*-
"""
Welcome to the CBC-MAC oracle!
Our oracle's function is a CBC message authentication code function!
The oracle is found at umbccd.io:13375, and your methods are:
    tst - returns a 16 byte MAC followed by its message
    vfy - verifies the contents of the message after the : in "vfy:...",
          returning a status message of the result.

@author: pleoxconfusa
"""

import socket

BLOCK_SIZE = 16
pad = lambda s: s + ((BLOCK_SIZE - len(s) % BLOCK_SIZE) % BLOCK_SIZE) * b'\0'


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('crypto.ctf.umbccd.io', 13375)
sock.connect(server_address)

#available methods: flg, enc, dec.


msg = 'tst'.encode()
sock.sendall(msg)
tst = sock.recv(1024)
print(tst)#not decoded, because now the oracle sends encrypted bytes.

mac = tst[:BLOCK_SIZE]
txt = tst[BLOCK_SIZE:]

msg = 'vfy:'.encode() + mac + txt #sanity check.
sock.sendall(msg)
res = sock.recv(1024)#receive the encryption as 16 bytes of iv followed by ct.
print(res)

msg = 'vfy:'.encode() + mac + pad(txt) #sanity double check, trying to win by padding.
sock.sendall(msg)
res = sock.recv(1024)#receive the encryption as 16 bytes of iv followed by ct.
print(res)

msg = b'vfy:' + mac + b'The first 16 bytes do not authenticate this message.' #sanity triple check
sock.sendall(msg)
res = sock.recv(1024)
print(res) 
    
sock.close()

Solution

We are given access to a CBC-MAC oracle. The oracle's functionality is limited to giving us a single plaintext message and its MAC, and verifying any number of messages. The plaintext message and MAC that the oracle gives is the exact same every time. The goal of the challenge is to verify any message other than the one given.

The CBC-MAC of a message mm is the last ciphertext block of the encryption of mm using an IV of 0\textbf{0}.

We can easily create a message different to mm whose CBC-MAC is the same as the CBC-MAC for mm. We do this by appending the XOR between CBC-MAC(m)\text{CBC-MAC}(m) and m1m_1 (where m1m_1 is the first plaintext block of mm), and the remaining plaintext blocks of mm to mm itself. When the CBC-MAC is calculated for this message, it will be the same as the CBC-MAC for mm. Visually:

Here, we take m1=m1MACm'_1 = m_1 \oplus \text{MAC} and m2=m2m'_2 = m_2, m3=m3m'_3 = m_3, and so on. This produces the same MAC as CBC-MAC(m)\text{CBC-MAC}(m) because when m1m'_1 is XORed with MAC\text{MAC}, the result is m1m_1 and so the remainder of the encryption process is identical to process for computing CBC-MAC(m)\text{CBC-MAC}(m).

Implementation:

from pwn import xor, remote
BLOCK_SIZE = 16
pad = lambda s: s + ((BLOCK_SIZE - len(s) % BLOCK_SIZE) % BLOCK_SIZE) * b'\0'

conn = remote('crypto.ctf.umbccd.io', 13375)
conn.sendline('tst')
tst = conn.recv()
mac = tst[:16]
msg = pad(b'The first 16 bytes authenticate this message.')
m = mac + msg + xor(mac, msg[:16]) + msg[16:]
conn.send(b'vfy:'+m)
print(conn.recv().decode())

Flag: DawgCTF{[email protected][email protected][email protected]}


Cha Cha Real Smooth

crypto (500pts)

Can you crack RSA?

crypto.ctf.umbccd.io 13374

(no brute force is required for this challenge)

Author: pleoxconfusa

client.py:

# -*- coding: utf-8 -*-
"""
Welcome to the RSA oracle!
Our oracle's function is RSA encryption!
The oracle is found at umbccd.io:13374, and your methods are:
    flg - returns the encrypted flag as bytes
    nnn - returns the N of the rsa function as bytes
    enc - returns the encryption of the message after the : in "enc:..."
          as a bytes representation of an int.
    dec - returns the decryption of the ciphertext int in bytes after the : in "dec:..."
          as a bytes string.
    
@author: pleoxconfusa
"""

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('crypto.ctf.umbccd.io', 13374)
sock.connect(server_address)

#available methods: flg, enc, dec, nnn.


#public knowledge, may be useful later.
#msg = 'nnn'.encode()
#sock.sendall(msg)
#ct = sock.recv(1024)

msg = 'flg'.encode()
sock.sendall(msg)
ct = sock.recv(1024)
print("flag ct: ",ct) 

#msg = b'dec:' + ct
#sock.sendall(msg)
#print(sock.recv(1024))
    
sock.close()

Solution

The RSA encryption/decryption oracle gives us the flag ciphertext, cc, and the public modulus, nn, and allows us to encrypt/decrypt any message (except for the flag ciphertext). The server sends and receives values as bytes. Although we can't ask for the decryption of cc directly, the decryption of c+nc + n will give the decryption of cc while appearing different to the oracle. This follows from the fact that n0(modn)n \equiv 0 \pmod n and the following properties of modular arithmetic:

kakb(modm)a+cb+d(modm)\begin{aligned} ka &\equiv kb \pmod m \\ a + c &\equiv b + d \pmod m \end{aligned}

given ab(modm)a \equiv b \pmod m, cd(modm)c \equiv d \pmod m and for all integers kk.

Note: If we ask for the encryption of \x01, we see that the server returns back

\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

This tells us that the server expects and sends bytes in little endian and up to 128 bytes (RSA modulus is 1024 bits).

Implementation:

from Crypto.Util.number import *
from pwn import remote

conn = remote('crypto.ctf.umbccd.io', 13374)
conn.send('flg')
c = bytes_to_long(conn.recv()[::-1])
conn.send('nnn')
n = bytes_to_long(conn.recv()[::-1])
conn.send(b'dec:'+long_to_bytes(c+n)[::-1])
m1 = conn.recv()
print(m1)

Flag: DawgCTF{[email protected]}