import random from secret import flag from fractions import Fraction def score(a,b): if abs(a-b)<1/2**10: # capping score to 1024 so you dont get extra lucky return Fraction(2**10) return Fraction(2**53,int(2**53*a)-int(2**53*b)) total_score = 0 for _ in range(2000): try: x = random.random() y = float(input('enter your guess:\n')) round_score = score(x,y) total_score+=float(round_score) print('total score: {:0.2f}, round score: {}'.format( total_score,round_score)) if total_score>10**6: print(flag) exit(0) except: print('Error, exiting') exit(1) else: print('Maybe better luck next time')
Mersenne Twister で値を予測する系だが、予測対象が
random.random
になっている。 pythonのrandom.randomの実装 は↓のように64bit生成して11bit落とす、という感じになっている。利用するのが53bitなのは IEEE 754 の倍精度のfraction(仮数部)がその値だからstatic PyObject * _random_Random_random_impl(RandomObject *self) /*[clinic end generated code: output=117ff99ee53d755c input=afb2a59cbbb00349]*/ { uint32_t a=genrand_uint32(self)>>5, b=genrand_uint32(self)>>6; return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); }
9007199254740992 == 2^53
,67108864 == 2^26
ということで
x
が得られた値としてx * 2^53
したあと下27bit、上28bitに分けるとそれぞれb >> 6
,a >> 5
が手に入る
さてそれでは Bit欠落したMersenne Twister を復元できるのかと言うと…… z3 power でできる(場合もある)
過去の MeePwn 2018 | StillOldSchool でも出題されていた