Intro
The crypto problems were not too hard and there were nothing new and special except the last crypto problem "ZKPoD". This was my first time to implement LSB oracle attack, and the code was pretty messy. I refactored it based on oalieno's tutorial.
ZKPoD
Description
The problem has two modules. The first module is an RSA with fixed 2048-bit \(N\) and \(e = 65537\), and the second one is a signing process with a 2048-bit prime \(p\) and \(g = 2\). The signing process works as follow:
- Randomly choose a \(k\) between \(0\) to \((p - 2)\). Let \(r = g^k \pmod p\)
- \(e\) is a 1024-bit hash of \(r\), and \(s = (k - me) \pmod{p - 1}\), where \(m\) is the message to be signed.
- The signature is \((r, s)\)
The problem first gives us the RSA-encrypted flag, then provide an oracle that decrypts the input and signs it.
1 | N = 0xbb00128c1c1555dc8fc1cd09b3bb2c3efeaae016fcb336491bd022f83a10a501175c044843dbec0290c5f75d9a93710246361e4822331348e9bf40fc6810d5fc7315f37eb8f06fb3f0c0e9f63c2c207c29f367dc45eef2e5962eff35634b7a29d913a59cd0918fa395571d3901f8abd83322bd17b60fd0180358b7e36271adcfc1f9105b41da6950a17dba536a2b600f2dc35e88c4a9dd208ad85340de4d3c6025d1bd6e03e9449f83afa28b9ff814bd5662018be9170b2205f38cf3b67ba5909c81267daa711fcdb8c7844bbc943506e33f5f72f603119526072efbc5ceae785f2af634e6c7d2dd0d51d6cfd34a3bc2b15a752918d4090d2ca253df4ef47b8b |
Solution
Recover LSB
The decryption oracle reminds us of the famous LSB oracle attack, but can we recover the least significant bit of \(m\) from the signature? The answer is yes. Since we know \(r\) and \(e\), we can calculate
\[g^{me} \equiv \left(\frac{r}{g^s}\right) \pmod p\]
if \(e\) is invertible modulo \((p - 1)\), then
\[g^m \equiv \left(g^{em}\right)^{e^{-1}} \pmod p\]
where \(e^{-1}\) is the inverse of \(e\) modulo \((p-1)\). Luckily, \(g\) is a primitive root of \(p\), so
\[g^m \text{ is a quadratic residue} \Leftrightarrow 2 \mid m\]
meaning we can recover the least significant bit of \(m\) easily by Euler's formula.
There are two important details:
- \(e\) is not always invertible. We may have to ask for the signature of the same message multiple times, until we get a signature with invertible \(e\).
- This only works when \(N < p\).
If \(p\) is small, the least
significant bit may be destroyed because of the modulo \((p - 1)\) operation. Formally, we use the
fact that
m % N % (p - 1) = m % N
.
LSB oracle attack
It is a well known attack, and below we provide the essential idea of the attack.
Let the encrypted flag and flag be \(c\) and \(f\), respectively. The crucial observation is that when we feed in the input \(2^ec\), the decrypted message will be \(dm = 2f \pmod N\). If \(2f \ge N\), then \(dm = 2f-N\), an odd number; otherwise, \(dm = 2f\), an even number. This observation implies that based on the least significant bit of \(dm\), we can know if \(f\) is in \([0, \frac{N}{2})\) or \([\frac{N}{2}, N)\). In the next round, we feed in \(4^ec\) and so on, and we are able to binary search the value of \(f\).
The script is as follow:
1 | import hashlib |