OMG RNG!
Description
Gifts for everyone!
nc lab-1.ctf.standcon.n0h4ts.com 7903
Overview
Starting an instance and connecting to the server, the server would run chal.py and send you gifts
and a secret
containing the flag that is encrypted.
We have to figure out a way to decrypt the secret
retrieved from the server using the gifts
.
TL;DR
Use
gcd
to find value ofm
Get the previous state by solving
linear congruence
betweenm
and firstgift
received.Get previous state until it reaches the first state.
Perform
XOR
ofsecret
with first 2 states.Got flag
Solution
Analysis
It's a pretty short code, so let's quickly break it down.
Analyzing chal.py
The MLFG
class gets initialized first.
When the MLFG
class gets initialized, it grabs a large random number and stores it as m
in the class. Then it randomly generates 2 other numbers smaller than m
, and stores it in s0
and s1
in the class. curr_state
is then assigned with the value 0
.
The next()
function is pretty self explanatory.
Searching up MLFG
, it stands for Multiplicative Lagged Fibonacci Generator
. I tried to search for any writeups on such challenges but couldn't find any. The key takeaway from looking it up is that this algorithm is a Pseudo-Random Number Generator
(PRNG).
According to the MLFG
class, we can see how it performs as a PRNG
:
It first generates a random number as a modulus (
m
)It then generates 2 random numbers to start with (
s0
ands1
)To get a random value, it multiplies both
s0
ands1
and modm
and returns it (this is what makes itMultiplicative
as the name of the class suggest). Socurr_state
would be your random value.It then stores the value of
s1
intos0
, and the current value (curr_state
) generated intos1
(this is what makes itLagged Fibonacci
).
Here are some images to help you visualize it. (assume f() is a function (self.s0 * self.s1) % self.m
. Note that the numbers in the picture are just an example.)
See why it is related to Fibonacci
?
Now, the last few lines of code is to produce the secret
where the flag is encrypted through xor
. We can easily find the flag if we find the values it got xored with (goal is to find the original value of s0
and s1
).
You then get to retrieve the value of secret
and the next few random values generated.
Finding value of 'm'
We will first have to grab the next few states of MLFG
. I went to grab 4 states and then the secret
.
We will be focusing on this line of code:
Since we have grabbed 4 consecutive states of MLFG
, we have something like this:
gift0
= (unknown s0
*unknown s1
) %m
gift1
= (unknown s1
*gift0
) %m
gift2
= (gift0
*gift1
) %m
gift3
= (gift1
*gift2
) %m
One thing to note, modulus equations can also be expressed in another way that is true if you think about it:
(xy mod m = z) === (xy - n1m = z), where n1 is a random integer
Similarly,
(gift2 = (gift0 * gift1) % m) === (gift2 = (gift0 * gift1) - n1m)
By rearranging the equation...
(gift0 * gift1) - gift2 = n1m
We then have the following equations...
(gift0 * gift1) - gift2 = n1m
(gift1 * gift2) - gift3 = n2m
So now we have n1m and n2m, we can find the value of m
by finding the gcd
of both of them since m
would be larger than n1 and n2!
m acquired!
Finding previous state
Let's focus on this equation as we want to find the previous state:
gift1
= (unknown s1
*gift0
) %m
We basically have the value of everything except for the previous state (unknown s1
)
I had a little trouble solving this due to my lack of knowledge on modular arithmetic
, but searching it up gives us something called Linear Congruence. The linear congruence
problem is to find the value of X
given the equation aX (mod m) = b
, which is exactly what we were looking for!
I managed to find and use a linear congruence
function implemented in Python https://stackoverflow.com/questions/48252234/how-to-solve-a-congruence-system-in-python.
Now we are able to find the previous state! To find previous state of the previous state, we will just have to use the value of the previous state we got.
Example:
We can then just keep finding the previous states until we find the flag!
Note that the linear congruence
function may return errors at times because some of the values provided may not have any solutions (this happens as the the gcd of the modulus and the starting number does not equally divide the result, so 2x mod 8 = 51 have no solution because gcd(2,8)=2, and 51%2 != 0)
Solve.py
Last updated