🐺 Verifier
Welcome to my new verifier system, hope to be safe ;P Wolves are social animals
Code
from Crypto.Util.number import *from random import getrandbits
SIZE = 64FLAG = bytes_to_long(b'NHNC{FAKE_FLAG}')e = 37p, q = getPrime(1024), getPrime(1024)while ((p-1)*(q-1)) % e == 0 : p, q = getPrime(1024), getPrime(1024)N = p*q
print("=== ICEDTEA Verifier 1.0 ===")
while True: OTP = getrandbits(SIZE) print(f"signed: {pow(OTP, e, N)}") TRIAL = int(input("OTP: ")) if TRIAL == OTP: print(f"signed: {pow(FLAG, e, N)}") continue
print(f"Invalid OTP, {OTP}")
To get the flag we need to do a two things. First we need to input the correct OTP, then we need to decrypt the RSA signed flag
Vulnerability
The first vulnerability lies in using pythons getrandbits
functions. Since pythons random library uses a Mersenne Twister we are able to predict the next input given a few inputs. We can use any Mersenne Twister cracker libarary in python like this
The second lies in being able to connect to the instance multiple times with different values of N. First we can get N from the multiple signed OTPs. Then we can do a Håstad’s broadcast attack to recover the signed flag
Exploit
import mathfrom pwn import *from mt19937_crack import RandomSolverfrom Crypto.Util.number import *from sympy.ntheory.modular import crtimport gmpy2
e=37def getnextguess(l): randomSolver = RandomSolver() for i in l: randomSolver.submit_getrandbits(i, 64) return randomSolver
Nlist = []flaglist = []
for _ in range(10): io = remote('160.187.199.7', 31337)
io.recvline().decode() # get the introduction line
#Set up lists for otps and calculating the N val otps = [] ns = []
for i in range(312):
o = io.recvline() c = int(o.decode().split(':')[1].strip(" \n'")) # get the signed otp
io.sendline(b"1")
o = io.recvline() num = int(o.decode().split(',')[1].strip(" \n'")) # get the real otp
otps.append(num)
m = pow(num, e) - c ns.append(m)
# Now we can find the N using the multiple signed OTPs N_candidate = ns[0] for i in range(1, len(ns)): N_candidate = math.gcd(N_candidate, ns[i]) Nlist.append(N_candidate)
s = getnextguess(otps) s.solve()
io.recvline() io.sendline(str(s.getrandbits(64)).encode()) # Send the predicted OTP
o = io.recvline() encrypted_flag = int(o.decode().split(':')[2].strip(" \n'"))
flaglist.append(encrypted_flag) io.close()
# Use chinese remainder theorem to get the flag using the values so far C, _ = crt(Nlist, flaglist) m_recovered, exact = gmpy2.iroot(C, e) print(long_to_bytes(m_recovered))
After running this code for a few minutes we can get our flag like so
[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'\x94\xde\xa7lU\x14\xad'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'a\x82Q\x04\xbb.\xe9\x98\x1a\xe7\xa6\xca\x88\xbd'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b';U\x82T\x85\xf2 \x86Z\x10\xfa\xdb\x1e\xc2\xd2%\xd9\xb0e)+'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b"'\xf1\xa2\x04\x91\xc6\xd8\x88(\x11\xad\xb1\xd4N*I\xc2$\xed\xa1J\x7f\x87w|\x03\xcb\xb1"[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'\x186\x9d\xad\xcf\x8c\xbf\x01\xa8N\xe7}\xfa\xa6\x1b\x18N&}\xc11\xd4o\x9a\xb2HP\xab\x8d$\x0f\xb8\xb1<\xa3'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'\x0f\xb63\x8e\xc5\xb4\xa4K\xd88\x89\xc4\x17Ft\x86i\x0b\xb0\x8a>\x18\xb5R\x8d\x05\xac\xa7\xe3\xe9\xae\x02Bw\xf4\xbf\x08\x0f\r\xfc\xba\r'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'\t\xe4@\x03\x996\xe1\xc3\x0f\x8a\xd5\x80I\x0e\xb0\xb2o\x83\x8e9\xb1\x19\x93\xc9\xb6\xec<D\x16g/l\xf8\x81\xdco\xfc\xb4j(\xefJ\xcd\xa8+A}\xe9\xd6'[+] Opening connection to 160.187.199.7 on port 31337: Done[*] Closed connection to 160.187.199.7 port 31337b'NHNC{using_big_intergers_in_python_is_still_a_pain}'
Conclusion
Don’t use pythons random library for OTPs since it can be predicted. Don’t let users get the same encrypted message with the same N or else it can be revealed.