picoCTF
Sra [400 pts]
Challenge Description:
I just recently learnt about the SRA public key cryptosystem… or wait, was it supposed to be RSA? Hmmm, I should probably check…
Connect to the program on our server: nc saturn.picoctf.net [port #]
Download the program: chal.py
We’re given a file, chal.py
, and service to connect to.
Here’s chal.py
:
Seems like they replaced the standard RSA variable names with some of the 7 deadly sins. Let’s change it back to the standard RSA variable names.
Okay, now we can start analysis.
This seems like standard RSA, except for the fact that we are given the private exponent d instead of n. Well, typically, in RSA, the original message m=cd(modn) when decrypting. However, there’s a problem. We’re missing n. How can we figure out what n is?
Immediately, I took a look at the size of p and q. They are both 128 bit primes. Rather small for RSA, but not so small that we can naively brute force it.
Well, what does knowing d do for us? What is d anyways?
In RSA, d is defined such that it is the modular inverse of e with respect to a modulus of ϕ(n), i.e. Euler’s totient function.
(At least in this context, as ϕ(n)=(p−1)(q−1) in RSA, and we can clearly see that this is used as the modulus in the program, and not Carmichael’s totient function, the alternative to Euler’s).
Since d is the modular inverse of e, ed=1(modϕ(n)).
This can be rewritten as ed=1+kϕ(n)=1+k(p−1)(q−1).
Therefore, ed−1=k(p−1)(q−1).
This leads to the natural thought that perhaps we can prime factorize ed−1 (there are various available functions for this like sympy.ntheory.factorint
) and subsequently brute force k,(p−1),(q−1).
Well, how can we brute force this?
First, we can place an upper bound on the value of k. We know that p,q are 128-bit primes, i.e. 127<log2p<128 and similarly for q. Therefore, based on log2(ed−1), we set constraints on k.
log2(ed−1k)=log2[(p−1)(q−1)]=log2(p−1)+log2(q−1)<256Since log2p<128 and log2q<128.
Another way you could think about it is, if k is too large, we won’t be able to properly separate the result of ed−1k into two 128-bit numbers.
Simultaneously, however, k cannot be too small. See the following:
log2(ed−1k)=log2[(p−1)(q−1)]=log2(p−1)+log2(q−1)>254Since 127<log2p and 127<log2q.
Now that we have the bounds of k, we can ascertain all possible values of k.
We can first eliminate all factors that are too large and result in log2(ed−1factor)<256.
Once we have all factors that are small enough, we can iterate through all subsets of the factors (I did so with bitwise operators) and to find the possible products of these factors. These products are the possible k values. Of these possible k values, only consider all k’s such that 256>log2(ed−1k)>254.
See the following implementation:
Note that I slightly expanded my range for possible k’s in my code in case I had messed up with my math.
Once we have our possible values for k, all that’s left is to iterate through each possible value of k and test whether or not we have a valid pair of p,q. Here’s a short outline of the process:
- For each possible k
- Make a copy of the list of all factors and remove all factors of k from that list to ensure we don’t repeat their usage.
- Iterate through all possible subsets of these factors (again, I used bitwise operators) and find the possible products of these factors. Let each product be p−1.
- For each p−1, check if it is a 128-bit number, i.e. log2(p−1)==127. If it isn’t, continue onto the next possible product.
- If it is, check if your values for p and q are prime. q can be easily computed as q=ed−1k(p−1)+1. If they are not prime, continue to the next possible product.
- If it is prime, the answer has been found!
Here is the implementation of the above:
Once you have found p and q, all that’s left is to compute n, decrypt the message with m=cd (mod n), and send in the result.
Only, don’t waste over an hour like I did :( and make sure to convert your final value to bytes before sending it using Python’s Crypto module’s long_to_bytes function.
Once you send it in, open the program in interactive mode to get the flag!
picoCTF{7h053_51n5_4r3_n0_m0r3_b2f9b414}
Here is my full implementation: