The app uses AES to encrypt messages, with keys generated from a Linear Congruential Generator (LCG). If we are able to find previously-generated values from the LCG, then we could find the keys used to encrypt the flag.
LCGs can be quite easily broken, allowing us to find the values of a, c and m. We need to submit two messages, so that we get a pair of keys and their corresponding LCG-generated values. This is sufficient for us to find the LCG parameters.
X = []for _ inrange(7): X = [344919848,133572217,3837144844,602813605,3658183952,3608054065,2853669428,2349514525]Det_X = []Det_X.append(calc_det(1, 2, X))Det_X.append(calc_det(2, 3, X))Det_X.append(calc_det(3, 4, X))Det_X.append(calc_det(4, 5, X))found_p =reduce(GCD, Det_X)mod_inv_a =modInverse((X[2]-X[3]), found_p)found_a = ((X[3]- X[4])*mod_inv_a) % found_pfound_c = (X[4]- found_a*X[3]) % found_pprint("Found: %d as P, %d as a and %d as c"% (found_p, found_a, found_c))P, a, c = found_p, found_a, found_c
Now, we need to reverse the LCG to find previously-generated values.
To do this, note that we can reorder the LCG next-state equation and apply the modular inverse of a to find the previous value of x.
x ≡ a * prevx + c (mod m)
x - c ≡ a * prevx (mod m)
ainverse * (x - c) ≡ ainverse * a * prevx (mod m)
ainverse * (x - c) ≡ prevx (mod m)
Starting from the most recently-generated value, we can work backwards to the first 4 generated values, which would be the key used to encrypt the flag.
x =2349514525x = (a * x + c) % Pfor i inrange(1411):print('------------------'+str(1411- i)) x = (modinv(a, P)* (x - c)) % Pprint(x) x = (modinv(a, P)* (x - c)) % Pprint(x) x = (modinv(a, P)* (x - c)) % Pprint(x) x = (modinv(a, P)* (x - c)) % Pprint(x)