# [NPUCTF2020]Mersenne twister
链接
from hashlib import * | |
from itertools import * | |
from binascii import hexlify , unhexlify | |
from flag import flag ,seed | |
assert len(flag) == 26 | |
assert flag[:7] == 'npuctf{' | |
assert flag[-1] == '}' | |
XOR = lambda s1 ,s2 : bytes([x1 ^ x2 for x1 ,x2 in zip(s1 , s2)]) | |
class mt73991: | |
def __init__(self , seed): | |
self.state = [seed] + [0] * 232 | |
self.flag = 0 | |
self.srand() | |
self.generate() | |
def srand(self): | |
for i in range(232): | |
self.state[i+1] = 1812433253 * (self.state[i] ^ (self.state[i] >> 27)) - i | |
self.state[i+1] &= 0xffffffff | |
def generate(self): | |
for i in range(233): | |
y = (self.state[i] & 0x80000000) | (self.state[(i+1)%233] & 0x7fffffff) | |
temp = y >> 1 | |
temp ^= self.state[(i + 130) % 233] | |
if y & 1: | |
temp ^= 0x9908f23f | |
self.state[i] = temp | |
def getramdanbits(self): | |
if self.flag == 233: | |
self.generate() | |
self.flag = 0 | |
bits = self.Next(self.state[self.flag]).to_bytes(4 , 'big') | |
self.flag += 1 | |
return bits | |
def Next(self , tmp): | |
tmp ^= (tmp >> 11) | |
tmp ^= (tmp << 7) & 0x9ddf4680 | |
tmp ^= (tmp << 15) & 0xefc65400 | |
tmp ^= (tmp >> 18) & 0x34adf670 | |
return tmp | |
def encrypt(key , plain): | |
tmp = md5(plain).digest() | |
return hexlify(XOR(tmp , key)) | |
if __name__ == "__main__": | |
flag = flag.encode() | |
random = mt73991(seed) | |
f = open('./cipher.txt' , 'wb') | |
for i in flag: | |
key = b''.join([random.getramdanbits() for _ in range(4)]) | |
cipher = encrypt(key , chr(i).encode()) | |
f.write(cipher) | |
#cef4876036ee8b55aa59bca043725bf350a5e491debdef7ef7d63e9609a288ca1e2c82a7fe566bd8709e73c8d495ea504a486ed11189faf8e6fb35617e47d2d1ad5e4783e96afeaae9f7104ec477fb39fe4ec619bf58289709e15c4449f03fc51cba918cd0ebfdc12376b41e7815406482733b3b200826b6c78d86563edaea94dccf459a4291517a4b8367d7b4a53aeecd7e0accf661bfc726f5ba62e1c0e04100108ad32e7d5711f780185cba5cf31d328bee84066be4ab9582cf9d4bfe3c6f96a7732e1c37d800c90fd46277147f0a26c149dcd5eeb0f2df0c075627bc220be5eefdd67186056ac28c21e155a7f247664aaecdb498134de274df10114d1f06f84dd21820f150d69c9439d909dec0f5ccfeab61b62db2ea91d31bc8163ff16c7f458006bd5ac4a5f5bfae2770b23ccfb7195b76aa0a9aa146831667a7b9fe08c19e691afadccb3ca5169ef3fabaa3dad47d536e89ed4cee6f788bc969c3ad3137850ebfc46a73af2b0c036c3da4b4a16506f499445c604dd73eeb846a52f881515a3ad0ab448b4f9ed3e0ab1fffac60b223dde6450ba6198e90e14de107aaf2 |
MT19937 类型的
我们可以知道 flag 以 'npuctf' 开头
那么可以求出前几个随机数
这道题需要求的是 seed
那么只要知道第一个随机数就可以
然后可以逆一下 next ()
之前存的脚本
import string | |
from binascii import hexlify | |
from hashlib import md5 | |
from Crypto.Util.number import * | |
# right shift inverse | |
def inverse_right(res, shift, bits=32): | |
tmp = res | |
for i in range(bits // shift): | |
tmp = res ^ tmp >> shift | |
return tmp | |
# right shift with mask inverse | |
def inverse_right_mask(res, shift, mask, bits=32): | |
tmp = res | |
for i in range(bits // shift): | |
tmp = res ^ tmp >> shift & mask | |
return tmp | |
# left shift inverse | |
def inverse_left(res, shift, bits=32): | |
tmp = res | |
for i in range(bits // shift): | |
tmp = res ^ tmp << shift | |
return tmp | |
# left shift with mask inverse | |
def inverse_left_mask(res, shift, mask, bits=32): | |
tmp = res | |
for i in range(bits // shift): | |
tmp = res ^ tmp << shift & mask | |
return tmp | |
def extract_number(tmp): | |
tmp ^= (tmp >> 11) | |
tmp ^= (tmp << 7) & 0x9ddf4680 | |
tmp ^= (tmp << 15) & 0xefc65400 | |
tmp ^= (tmp >> 18) & 0x34adf670 | |
return tmp | |
def recover(y): | |
y = inverse_right_mask(y,18,0x34adf670) | |
y = inverse_left_mask(y,15,0xefc65400) | |
y = inverse_left_mask(y,7,0x9ddf4680) | |
y = inverse_right(y,11) | |
return y | |
c='cef4876036ee8b55aa59bca043725bf350a5e491debdef7ef7d63e9609a288ca1e2c82a7fe566bd8709e73c8d495ea504a486ed11189faf8e6fb35617e47d2d1ad5e4783e96afeaae9f7104ec477fb39fe4ec619bf58289709e15c4449f03fc51cba918cd0ebfdc12376b41e7815406482733b3b200826b6c78d86563edaea94dccf459a4291517a4b8367d7b4a53aeecd7e0accf661bfc726f5ba62e1c0e04100108ad32e7d5711f780185cba5cf31d328bee84066be4ab9582cf9d4bfe3c6f96a7732e1c37d800c90fd46277147f0a26c149dcd5eeb0f2df0c075627bc220be5eefdd67186056ac28c21e155a7f247664aaecdb498134de274df10114d1f06f84dd21820f150d69c9439d909dec0f5ccfeab61b62db2ea91d31bc8163ff16c7f458006bd5ac4a5f5bfae2770b23ccfb7195b76aa0a9aa146831667a7b9fe08c19e691afadccb3ca5169ef3fabaa3dad47d536e89ed4cee6f788bc969c3ad3137850ebfc46a73af2b0c036c3da4b4a16506f499445c604dd73eeb846a52f881515a3ad0ab448b4f9ed3e0ab1fffac60b223dde6450ba6198e90e14de107aaf2' | |
p='n'.encode() | |
tmp=md5(p).hexdigest() | |
XOR = lambda s1 ,s2 : bytes([x1 ^ x2 for x1 ,x2 in zip(s1 , s2)]) | |
m1=eval('0x'+tmp[:8])^eval('0x'+c[:8]) | |
key=recover(m1) | |
print(key) |
然后再求 seed
爆破 seed,python 跑起来很慢。。。用 c++ 跑
更新一下
知道 flag 最后一位‘}’ 就知道 state [103] 可以求出 state [104]
就可以逆回去
from gmpy2 import invert | |
def backtrace(cur): | |
high = 0x80000000 | |
low = 0x7fffffff | |
mask = 0x9908f23f | |
state = cur | |
tmp = state[1]^state[0] | |
if tmp & high == high: | |
tmp ^= mask | |
tmp <<= 1 | |
tmp |= 1 | |
else: | |
tmp <<=1 | |
# recover highest bit | |
state= tmp & low | |
return state | |
def invert_right(res,shift): | |
tmp = res | |
for i in range(32//shift): | |
res = tmp^res>>shift | |
return res&0xFFFFFFFF | |
def recover(last): | |
n = 1<<32 | |
inv = invert(1812433253,n) | |
for i in range(104,0,-1): | |
last = ((last+i-1)*inv)%n | |
last = invert_right(last,27) | |
return last | |
s=[778501557,1167644902] | |
s228=backtrace(s) | |
print(bin(s228)) | |
seed=(recover(s228)) | |
print(seed) |
得到 seed 直接可以求解
seed=1668245885 | |
class mt73991: | |
def __init__(self, seed): | |
self.state = [seed] + [0] * 232 | |
self.flag = 0 | |
self.srand() | |
self.generate() | |
def srand(self): | |
for i in range(232): | |
self.state[i + 1] = 1812433253 * (self.state[i] ^ (self.state[i] >> 27)) - i | |
self.state[i + 1] &= 0xffffffff | |
def generate(self): | |
for i in range(233): | |
y = (self.state[i] & 0x80000000) | (self.state[(i + 1) % 233] & 0x7fffffff) | |
temp = y >> 1 | |
temp ^= self.state[(i + 130) % 233] | |
if y & 1: | |
temp ^= 0x9908f23f | |
self.state[i] = temp | |
def getramdanbits(self): | |
if self.flag == 233: | |
self.generate() | |
self.flag = 0 | |
bits = self.Next(self.state[self.flag]).to_bytes(4, 'big') | |
self.flag += 1 | |
return bits | |
def Next(self, tmp): | |
tmp ^= (tmp >> 11) | |
tmp ^= (tmp << 7) & 0x9ddf4680 | |
tmp ^= (tmp << 15) & 0xefc65400 | |
tmp ^= (tmp >> 18) & 0x34adf670 | |
return tmp | |
c='cef4876036ee8b55aa59bca043725bf350a5e491debdef7ef7d63e9609a288ca1e2c82a7fe566bd8709e73c8d495ea504a486ed11189faf8e6fb35617e47d2d1ad5e4783e96afeaae9f7104ec477fb39fe4ec619bf58289709e15c4449f03fc51cba918cd0ebfdc12376b41e7815406482733b3b200826b6c78d86563edaea94dccf459a4291517a4b8367d7b4a53aeecd7e0accf661bfc726f5ba62e1c0e04100108ad32e7d5711f780185cba5cf31d328bee84066be4ab9582cf9d4bfe3c6f96a7732e1c37d800c90fd46277147f0a26c149dcd5eeb0f2df0c075627bc220be5eefdd67186056ac28c21e155a7f247664aaecdb498134de274df10114d1f06f84dd21820f150d69c9439d909dec0f5ccfeab61b62db2ea91d31bc8163ff16c7f458006bd5ac4a5f5bfae2770b23ccfb7195b76aa0a9aa146831667a7b9fe08c19e691afadccb3ca5169ef3fabaa3dad47d536e89ed4cee6f788bc969c3ad3137850ebfc46a73af2b0c036c3da4b4a16506f499445c604dd73eeb846a52f881515a3ad0ab448b4f9ed3e0ab1fffac60b223dde6450ba6198e90e14de107aaf2' | |
table=string.printable | |
random=mt73991(seed) | |
for i in range(0,len(c),32): | |
tmp=c[i:i+32] | |
key=b''.join([random.getramdanbits() for _ in range(4)]) | |
key=bytes_to_long(key) | |
md=hex(eval('0x'+tmp)^key)[2:].zfill(32) | |
for j in table: | |
if(md5(j.encode()).hexdigest()==md): | |
print(j,end='') | |
break |
主要是字节,hex,字符串转换有点混乱。。