- Download the problem file from http://research.samsung.com/sctf2018 or https://www.dropbox.com/s/xx6tnhzrgpdxvd8/LCG.py?dl=0
It is quite simple PRNG with the equation (t = 0xdeadbeef): $x_i = (k_1 - t) x_{i-1} + k_1 t x_{i-2} + k_2 (mod\ k_3)$
We can define $y_i$ as $y_i = x_i + t x_{i-1}$, then $y_i = k_1 y_{i-1} + k_2 (mod\ k_3)$. So, it’s just same as the normal LCG.
I used the method to break LCG described in this link, and the solver is here.
from pwn import *
def xgcd(b, a):
x0, x1, y0, y1 = 1, 0, 0, 1
while a != 0:
q, b, a = b // a, a, b % a
x0, x1 = x1, x0 - q * x1
y0, y1 = y1, y0 - q * y1
return b, x0, y0
def mulinv(b, n):
g, x, _ = xgcd(b, n)
if g == 1:
return x % n
r = remote('lcg.eatpwnnosleep.com', 12345)
inputs = []
for i in range(16):
r.sendline('1')
inputs.append( int(r.recvuntil('\n')) )
print(inputs)
k = 0xdeadbeef
arr = []
for i in range(15):
arr.append(inputs[i+1] + k*inputs[i])
# http://sandeepmore.com/blog/2012/03/23/breaking-linear-congruential-generator/
def d(i, j):
t1 = arr[i] - arr[0]
t2 = arr[i+1] - arr[1]
t3 = arr[j] - arr[0]
t4 = arr[j+1] - arr[1]
return t1 * t4 - t2 * t3
t = d(2, 3)
for i in range(3, 13):
t, _, _ = xgcd( t, d(i, i+1) )
k3 = t if t > 0 else -t
print("k3: ", k3)
g0_inv = mulinv( (arr[1] - arr[0]) % k3, k3)
k1 = ((arr[2] - arr[1]) * g0_inv) % k3
k2 = (inputs[2] - (k1 - k) * inputs[1] - k1 * k * inputs[0]) % k3
print("k1: ", k1)
print("k2: ", k2)
s0, s1 = inputs[-2], inputs[-1]
for i in range(16):
s0, s1 = s1, ( (k1 - k) * s1 + k1 * k * s0 + k2 ) % k3
r.sendline( str(s1) )
r.interactive()
The flag is SCTF{LCG_is_too_simple_to_be_a_good_CSPRNG}
.