# OT or NOT OT - zer0pts CTF 2021 ###### tags: `zer0pts CTF 2021` `crypto` ## overview The server encrypts flag with AES-CBC. Then provide us the chance to get the AES key. We repeatedly input some parameters $a, b, c, d$, then the server manipulates 2 bits of the key with them and gives us $x, y, z$. $x, y, z$ are calculated as shown the following. $u = a^rc^s \mod p$ $v = b^rc^s \mod p$ $x = u \oplus k_i$ $y = v \oplus k_{i+1}$ $z = d^rt^s \mod p$ ```python= import os import signal import random from base64 import b64encode from Crypto.Util.number import getStrongPrime, bytes_to_long from Crypto.Util.Padding import pad from Crypto.Cipher import AES from flag import flag p = getStrongPrime(1024) key = os.urandom(32) iv = os.urandom(AES.block_size) aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) c = aes.encrypt(pad(flag, AES.block_size)) key = bytes_to_long(key) print("Encrypted flag: {}".format(b64encode(iv + c).decode())) print("p = {}".format(p)) print("key.bit_length() = {}".format(key.bit_length())) signal.alarm(600) while key > 0: r = random.randint(2, p-1) s = random.randint(2, p-1) t = random.randint(2, p-1) print("t = {}".format(t)) a = int(input("a = ")) % p b = int(input("b = ")) % p c = int(input("c = ")) % p d = int(input("d = ")) % p assert all([a > 1 , b > 1 , c > 1 , d > 1]) assert len(set([a,b,c,d])) == 4 u = pow(a, r, p) * pow(c, s, p) % p v = pow(b, r, p) * pow(c, s, p) % p x = u ^ (key & 1) y = v ^ ((key >> 1) & 1) z = pow(d, r, p) * pow(t, s, p) % p key = key >> 2 print("x = {}".format(x)) print("y = {}".format(y)) print("z = {}".format(z)) ``` ## solution This seems like some of **oblivious transfer** protocols. We can easily get either $k_i$ xor $k_{i+1}$. Then how do we get both of them? As we can input four values, we must think out of how to choose them. For example, if $u = v$ or $u = -v$ stands, the challenge seems to become pretty easy. Thus, we need to make $a^rc^s \equiv b^rc^s \mod n$ or $a^rc^s \equiv -b^rc^s \mod n$. In order to do it, choosing $a = -b$ is the easiest way. $c, d$ should satisfy $c \equiv t^{-1}, d \equiv a^{-1} \pmod p$. Then $u \equiv a^rt^{-s}, v \equiv (-a)^rt^{-s}, z \equiv a^{-r}t^s \pmod n$ stands. So $u \equiv z^{-1} \mod p$ and $v \equiv \pm u$. ## exploit ```python= from ptrlib import Socket from Crypto.Cipher import AES import base64 import os HOST = os.getenv("HOST", "localhost") PORT = os.getenv("PORT", "9999") sock = Socket(HOST, int(PORT)) b = base64.b64decode(sock.recvlineafter("flag: ")) iv, cipher = b[:AES.block_size], b[AES.block_size:] p = int(sock.recvlineafter("p = ")) keylen = int(sock.recvlineafter("() = ")) binflag = "" for i in range((keylen + 1) // 2): print(i) t = int(sock.recvlineafter("t = ")) a = 2 b = p - a c = pow(t, -1, p) d = pow(a, -1, p) sock.sendlineafter("a = ", str(a)) sock.sendlineafter("b = ", str(b)) sock.sendlineafter("c = ", str(c)) sock.sendlineafter("d = ", str(d)) x = int(sock.recvlineafter("x = ")) y = int(sock.recvlineafter("y = ")) z = int(sock.recvlineafter("z = ")) u = pow(z, -1, p) v = -u % p m0 = x ^ u if x == y or x == -y % p: # (m0 == m1 and r is even) or (m0 == m1 and r is odd) m1 = m0 elif abs(x - y) == 1 or abs(x - (-y%p)) == 1: # (m0 != m1 and r is even) or (m0 != m1 and r is odd) m1 = m0 ^ 1 else: raise ValueError("WOW") binflag += str(m0) binflag += str(m1) key = int.to_bytes(int(binflag[::-1], 2), 32, 'big') aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) flag = aes.decrypt(cipher) print(flag) sock.close() ```