[NPUCTF2020]babyLCG
from Crypto.Util.number import *
from Crypto.Cipher import AES
from secret import flag
class LCG:
def __init__(self, bit_length):
m = getPrime(bit_length)
a = getRandomRange(2, m)
b = getRandomRange(2, m)
seed = getRandomRange(2, m)
self._key = {'a': a, 'b': b, 'm': m}
self._state = seed
def next(self):
self._state = (self._key['a'] * self._state + self._key['b']) % self._key['m']
return self._state
def export_key(self):
return self._key
def gen_lcg():
rand_iter = LCG(128)
key = rand_iter.export_key()
f = open("key", "w")
f.write(str(key))
return rand_iter
def leak_data(rand_iter):
f = open("old", "w")
for i in range(20):
f.write(str(rand_iter.next() >> 64) + "\n")
return rand_iter
def encrypt(rand_iter):
f = open("ct", "wb")
key = rand_iter.next() >> 64
key = (key << 64) + (rand_iter.next() >> 64)
key = long_to_bytes(key).ljust(16, b'\x00')
iv = long_to_bytes(rand_iter.next()).ljust(16, b'\x00')
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = flag + (16 - len(flag) % 16) * chr(16 - len(flag) % 16)
ct = cipher.encrypt(pt.encode())
f.write(ct)
def main():
rand_iter = gen_lcg()
rand_iter = leak_data(rand_iter)
encrypt(rand_iter)
if __name__ == "__main__":
main()
分析源码,我们可以知道,这道题给了我们20个LCG的输出,但只是输出的高位,后面的加密部分则是继续调用LCG,作为AES的参数。所以我们只需要通过给出的LCG高位输出,求出其中一个状态,就可以得到flag了。
我们对第一组和第二组输出进行分析,把每个输出划分成高位
通过化简,我们得到了最后一行式子,其中
我们需要补出两个恒等式,同时,需要在右边的结果中再补上
这是还需要一个恒等式,而且要让0和
a=107763262682494809191803026213015101802
b=153582801876235638173762045261195852087
m=226649634126248141841388712969771891297
h1=7800489346663478448<<64
h2=11267068470666042741<<64
A=a
B=a*h1+b-h2
M=matrix([[A,1,0],
[B,0,m],
[m,0,0]])
M.LLL()
[-11530711086177576267 -7584688652922652412 0]
[-12162326968771299382 11656017549012022539 0]
[1510343526611795913 6524632084338656352 226649634126248141841388712969771891297]
那么可以看到,最后一行得到的才是我们构造出来的结果。
显然这种方法有一点局限,毕竟给了20组数,我们只用了两组。所以我们继续推导关系,把所有未知低位,表示成第一组输出的未知低位。
于是我们就可以通过递推的关系,知道
需要注意的是,这里我们的最后一列补的是
最后贴一下完整代码
b=153582801876235638173762045261195852087
a=107763262682494809191803026213015101802
m=226649634126248141841388712969771891297
l=[]
A=[a]
B=[]
f=open('old','r')
for line in f:
l.append(int(line.strip())<<64)
B.append((a*l[0]+b-l[1])%m)
for i in range(1,19):
A.append(a*A[-1] % m)
B.append((a*l[i]+a*B[i-1]+b-l[i+1])%m)
M=matrix(ZZ,21,21)
for i in range(19):
M[i,i]=m
M[19,i]=A[i]
M[20,i]=B[i]
M[19,19]=1
M[20,20]=2^64
#1和m都可以
M.LLL().str()
v=M.LLL()[0]
print(v)
l1 = v[-2]
s1=l[0]+l1
seed = (s1-b)*inverse_mod(a,m)%m
print(seed)
from Crypto.Util.number import *
from Crypto.Cipher import AES
class LCG:
def __init__(self):
b = 153582801876235638173762045261195852087
a = 107763262682494809191803026213015101802
m = 226649634126248141841388712969771891297
seed = 73991202721494681711496408225724067994
self._key = {'a': a, 'b': b, 'm': m}
self._state = seed
def next(self):
self._state = (self._key['a'] * self._state + self._key['b']) % self._key['m']
return self._state
def export_key(self):
return self._key
rand_iter = LCG()
for i in range(20):
rand_iter.next()
key = rand_iter.next() >> 64
key = (key << 64) + (rand_iter.next() >> 64)
key = long_to_bytes(key).ljust(16, b'\x00')
iv = long_to_bytes(rand_iter.next()).ljust(16, b'\x00')
cipher = AES.new(key, AES.MODE_CBC, iv)
f = open("ct", "rb")
c = f.read()
m = cipher.decrypt(c)
print(m)
Comments NOTHING