Official description
The MegaSecure company provides a secure server allowing users to compute operations while controlling its energy consumption.
The server allows to execute commands in a secure way. Indeed, it relies on a secure element in order to verify the signature of any command received before executing it.
The Python code equivalent to the signature process is:
1 2
def sign(self, m): return pow(int(sha256(m), 16), self.d, self.N)
and the verification process is equivalent to:
1 2
def verif(self, m, s): return int(sha256(m), 16) == pow(int(s),self.e,self.N)
However, only two commands (
ls -la flag.txt
andcat flag.txt
) are available. Moreover, it has been noticed that the server server presents strange behaviors in some configurations.Server:
nc challenges.france-cybersecurity-challenge.fr 2253
Exploration
Let’s connect to the remote server using Netcat, we are greeted with the following prompt:
Welcome on SecureGreenServer, you can execute your commands in a secure environment while taking care of the environment by tuning the power consumption of the server !
Commands are:
|-> sV <V>: Set voltage to V (in V)
For instance: sV 2.3
|-> sF <F>: Set frequency to F (in Hz)
For instance: sF 100e6
|-> x <c> <s>: Verify the signature s of the command c then execute it
For instance: x 'ls -la flag.txt' 1196573330896637376934838787607181055155508231931404<snip>
|-> i: Show server information
|-> u: Show this usage information
|-> q: Quit
>>>
Let’s show the server information:
>>> i
{'voltage': 1.3, 'frequency': 50000000.0, 'max_voltage': 5, 'min_voltage': 1.3, 'max_frequency': 2000000000.0, 'min_frequency': 50000000.0}
Let’s run the example command:
>>> x 'ls -la flag.txt' 11965733308966373769348387876071810<snip>
-r-------- 1 guest users 174 Apr 30 13:27 flag.txt
0
Let’s rerun the command with a wrong signature:
>>> x 'ls -la flag.txt' 4242
Signature verification failed with following parameters:
e = 65537
N = 163010048473163556319676198036699833424003296096470611101<snip>
Proposed solution
The goal is to sign and run cat flag.txt
on the remote server.
We notice that at each connection, the signature of the ls -la flag.txt
command changes. This means that we have to guess the secret key and sign the
command in the same session.
In the following sections we are going to explore how to crash the server, then
how to find parameters in which the computation is faulted without crashing.
Finally we factorize the faulted RSA \(N\) modulo to recover the secret and sign
the cat flag.txt
command.
Crashing the server
We have control over server frequency \(f\) and voltage \(V\) such as:
- \(1.3 \leq V \leq 5\) (in volt),
- \(50 \leq f \leq 2000\) (in MHz).
Higher processors frequencies will cause more transistors switching per second, which means more current consumption due to switching losses. It also reduces the time window in which processor signals propagate. If the voltage is too low, then the processor may become unstable.
Let’s put the maximum frequency and the minimum voltage:
>>> sF 2000000000
>>> sV 1.3
>>> x 'ls -la flag.txt' 1
Command not done, the system had to reboot for some reason
Oops, the system detected the fault and rebooted. This raises the following question: Are there sets of parameters causing faults without rebooting the server?
Faulting the server
Let’s run the ls -la flag.txt
given in example many times at \(f=2\) GHz and
with \(V\) varying from 1.3 V to 5 V. We write the following Python script:
|
|
Running this Python script, we get:
fault! b'Signature verification failed with following parameters:
e = 65537
N = 171664987635353322221796683199868132096341862323194726615<snip>
2.650793650793651
n_reboot=23, n_good=40
We run this script another time with \(V\) fixed at 2.65 V. All responses are now giving faulted \(N\) values.
RSA moduli factorization
Let’s go back to the given sign
and verif
functions:
|
|
We recognize the RSA signature and verification scheme using SHA256 hash function. \(m\) is signed using the secret \(d\). The moduli \(N\), and \(e\) are public.
\(d\) and \(N\) are derived from two large prime numbers \(p\) and \(q\). RSA security relies on the fact that factorizing \(N=p\times q\) is hard. When the moduli \(N\) is faulted, it might no longer be hard to factorize.
“Using Fault Injection to weaken RSA public key verification” by Ivo van der Elzen explains in section 4 how to attack faulted \(N\) moduli. We are going to implement this attack.
Be careful when writing the key generation. If \(N=\prod_i p_i^{k_i}\) has more than 2 prime factors, then \(\phi=\prod_i p_i^{k_i-1}(p_i-1)\), with \(k_i\) the power of the prime factor. It took me quite some time to realize this.
|
|
After some CTRL-C and a bit of wait, we get the flag and a reference to the CLKSCREW paper:
Congrats! You did this: https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-tang.pdf
FCSC{e0e31e5f46a1488159b16e2f6b58fc1135c0a77ef0a423c5fa0d1ee74836b5
ef}
0
During this challenge, I learned:
- How to exploit RSA using moduli factorization, and discovering that \(\phi\) has a more generic formula than just \((p-1)(q-1)\),
- How to compute inverse modulo using Python
pow
, - To remember to look for repetitions (this is also a good advice for
X-Factor 2/2
).