# CRACKING RANDOM PYTHON LESS THAN 32 BITS ```python3= import random import binteger from Crypto.Cipher import AES from Crypto.Util.Padding import pad def getbit(): return random.getrandbits(1) known = b'' for _ in range(2500): word = binteger.Bin([getbit() for _ in range(8)]).int known += word.to_bytes(1, "big") FLAG = b'W1{day-la-flag}' key = b"" for _ in range(16): word = binteger.Bin([getbit() for _ in range(8)]).int key += word.to_bytes(1, "big") cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(FLAG, 16)) # out 5f0512a86cd89a47ecd5cb77af672f9137ecb5cd0c6141d0d5cd4257f0b4e72588d87fbc0f4cb0660ee0a4483bfbdf1a ``` ### rbtree_mersenne.py ```python3= class Twister: N = 624 M = 397 A = 0x9908b0df def __init__(self): self.state = [ [ (1 << (32 * i + (31 - j))) for j in range(32) ] for i in range(624)] self.index = 0 @staticmethod def _xor(a, b): return [x ^ y for x, y in zip(a, b)] @staticmethod def _and(a, x): return [ v if (x >> (31 - i)) & 1 else 0 for i, v in enumerate(a) ] @staticmethod def _shiftr(a, x): return [0] * x + a[:-x] @staticmethod def _shiftl(a, x): return a[x:] + [0] * x def get32bits(self): if self.index >= self.N: for kk in range(self.N): y = self.state[kk][:1] + self.state[(kk + 1) % self.N][1:] z = [ y[-1] if (self.A >> (31 - i)) & 1 else 0 for i in range(32) ] self.state[kk] = self._xor(self.state[(kk + self.M) % self.N], self._shiftr(y, 1)) self.state[kk] = self._xor(self.state[kk], z) self.index = 0 y = self.state[self.index] y = self._xor(y, self._shiftr(y, 11)) y = self._xor(y, self._and(self._shiftl(y, 7), 0x9d2c5680)) y = self._xor(y, self._and(self._shiftl(y, 15), 0xefc60000)) y = self._xor(y, self._shiftr(y, 18)) self.index += 1 return y def getrandbits(self, bit): return self.get32bits()[:bit] class Solver: def __init__(self): self.equations = [] self.outputs = [] def insert(self, equation, output): for eq, o in zip(self.equations, self.outputs): lsb = eq & -eq if equation & lsb: equation ^= eq output ^= o if equation == 0: return lsb = equation & -equation for i in range(len(self.equations)): if self.equations[i] & lsb: self.equations[i] ^= equation self.outputs[i] ^= output self.equations.append(equation) self.outputs.append(output) def solve(self): num = 0 for i, eq in enumerate(self.equations): if self.outputs[i]: # Assume every free variable is 0 num |= eq & -eq state = [ (num >> (32 * i)) & 0xFFFFFFFF for i in range(624) ] return state ``` ### Solve.py ```python3= from z3 import * import rbtree_mersenne import random import time def rbtree_solve(outputs): twister = rbtree_mersenne.Twister() solver = rbtree_mersenne.Solver() for tup in outputs: res, bits = tup equation = twister.getrandbits(bits) for i in range(bits): solver.insert(equation[i], (res >> (bits - 1 - i)) & 1) state = solver.solve() return (3, tuple(state + [0]), None) def z3_solve(outputs): MT = [BitVec(f'm{i}', 32) for i in range(624)] s = Solver() def tamper(y): y ^= LShR(y, 11) y ^= (y << 7) & 0x9D2C5680 y ^= (y << 15) & 0xEFC60000 return y ^ LShR(y, 18) def getnext(): x = Concat(Extract(31, 31, MT[0]), Extract(30, 0, MT[1])) y = If(x & 1 == 0, BitVecVal(0, 32), 0x9908B0DF) MT.append(MT[397] ^ LShR(x, 1) ^ y) return tamper(MT.pop(0)) def getrandbits(n): return Extract(31, 32 - n, getnext()) s.add([getrandbits(op[1]) == op[0] for op in outputs]) assert(s.check() == sat) state = [s.model()[x].as_long() for x in [BitVec(f'm{i}', 32) for i in range(624)]] return (3, tuple(state + [0]), None) def int_to_binary(num): binary_string = bin(num)[2:] padded_binary_string = binary_string.zfill(8) return padded_binary_string if __name__ == "__main__": with open('known','rb') as file: data = file.read() binary = "" for i in data: binary += str(int_to_binary(i)) bits = 1 number = 2500*8 output = [] for _ in range(number): output.append((int(binary[_]), bits)) print(output) st = time.time() random.setstate(rbtree_solve(output)) for _ in range(number): assert output[_][0] == random.getrandbits(bits) key = "" for _ in range(16*8): key += str(random.getrandbits(bits)) fin = time.time() print(f"rbtree took {fin - st} seconds.") print(key) st = time.time() random.setstate(z3_solve(output)) for _ in range(number): assert output[_][0] == random.getrandbits(bits) for _ in range(10000): assert check[_] == random.getrandbits(bits) fin = time.time() print(f"z3 took {fin - st} seconds.") ``` ```python3= import random import binteger from Crypto.Cipher import AES from Crypto.Util.Padding import pad key = "00011101111110110010011101011111100111101101001000111110110000001101101010011110010001010101101000011100110100101110010011010001" integer_value = int(key, 2) key = integer_value.to_bytes((len(key) + 7) // 8, byteorder='big') cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.decrypt(bytes.fromhex("5f0512a86cd89a47ecd5cb77af672f9137ecb5cd0c6141d0d5cd4257f0b4e72588d87fbc0f4cb0660ee0a4483bfbdf1a")) print(ciphertext) ```