- 根据线性同余生成器lcg可得:lcg=(a*x+c)%m
- 根据题目,当输入数字时,可以得到多个连续随机数,根据伪随机数生成的机制,可以了解到,随机数的生成只要知道随机数种子就可以破解
- 类推到线性同余方程组,也就是说当知道a,c,m时即可知道随机数的整个序列
- 例如:假设我们知道lcg0,那么lcg1=(alcg0+c)%m,同理lcg2=(alcg1+c)%m
- 此时我们随意提交多次,可以从中得到连续的随机数,类比于lcg0,lcg1,lcg2的关系
- lcg0=32081 lcg1=23815 lcg2=1237 lcg3=2387
- 爆破模数module,每次moudle值改变时,都进行下面的操作:
lcg1=(a*lcg0+c)%m lcg2=(a*lcg1+c)%m
两式相减 左边为:lcg2-lcg1 右边为:(a*lcg1+c)-(a*lcg0+c)
化简为: [lcg2-lcg1=a*(lcg1-lcg0) ] %m
根据求余运算规则可知
[a=(lcg2-lcg1)*gmpy2.invert(lcg1-lcg0,m)]%m
此时代入lcg0,lcg1,lcg2之后可求得a
代入lcg1=(a*lcg0+c)%m,把c当作未知数放到等式左边后,式子为:
c=[lcg1-(a*lcg0)]%m
类推lcg3_guess=(a*lcg2+c)%m
将求得地lcg3_guess与lcg3比较,如果相等,证明求得的a,c,m为正确值
否则,m=m+1,继续验证
注意:
1、这个式子成立条件是两边同时对m求余
2、gmpy2.invert(x1,x2)是求与x1%x2同余的最小数,二者在求余操作后等价,因为余数相等,该式表示余数,类似7%5=2%5
- 求得正确的a,c,m之后,线性同余方程为 lcg[i]=(a*lcg[i-1]+c)%m
- 多次提交成功之后,获得flag
注意:下面的exp有失败的机率,多次运行得到flag
失败原因:在得到四个随机数的时候,如果恰好四个随机数都比较小,那么很有可能得到错误的三个参数。
exp
import requests
import gmpy2
from bs4 import BeautifulSoup
# LCG solver
def lcg(m, a, c, x=0):
return (a * x + c) % m
def rlcg(m, a, c, x=0):
ainv = gmpy2.invert(a, m)
return ainv * (x - c) % m
def solve_a(lcg0, lcg1, lcg2, m):
a = (lcg2 - lcg1) * gmpy2.invert(lcg1 - lcg0, m) % m
return a
def solve_c(lcg0, lcg1, m, a):
c = (lcg1 - a * lcg0) % m
return c
session = requests.session()
# API functions
def guess(num=0):
url = f'http://192.168.233.128/?guess={num}'
res = session.get(url=url)
soup = BeautifulSoup(res.text, 'html.parser')
for paragraph in soup.select('form > p'):
paragraph = paragraph.get_text(strip=True, separator=" ")
if 'The Monolith desired:' in paragraph:
desired = paragraph.split('The Monolith desired: ', 1)[1].split(' ', 1)[0]
desired = int(desired.strip())
print('// ' + paragraph)
return desired
def main():
# Get lcg values original
lcg_0 = guess()
lcg_1 = guess()
lcg_2 = guess()
lcg_3 = guess()
print(f'Retrieved:')
print(f'>> lcg_0: {lcg_0}')
print(f'>> lcg_1: {lcg_1}')
print(f'>> lcg_2: {lcg_2}')
print(f'>> lcg_3: {lcg_3}')
print()
# Find the mod value
modulus = -1
for mod_value in reversed(range(30000, 2**16)):
try:
a = solve_a(lcg_0, lcg_1, lcg_2, mod_value)
c = solve_c(lcg_0, lcg_1, mod_value, a)
next = lcg(mod_value, a, c, lcg_2)
if next == lcg_3:
modulus = mod_value
break
except ZeroDivisionError:
pass
# Now we have all the parameters
print(f'Found parameters:')
print(f'>> mod: {modulus}')
print(f'>> a: {a}')
print(f'>> c: {c}')
print()
print(f'Doing guesses:')
lcg_last = lcg_3
for x in range(30):
lcg_last = lcg(mod_value, a, c, lcg_last)
print(f'>> next {x}: {lcg_last}')
guess(lcg_last)
if __name__ == '__main__':
main()