HITB 2015 Write-up - Crypto 300
Introduction
The crypto 300 challenge was about RSA with a special generation of the prime numbers $p$ and $q$. We were given a mail mail.msg which has been encrypted with RSA and whose the public key is in the hitbctf.crt certificate.
RSA’s Parameters Construction
The modulus $N$ is built by choosing randomly a first prime number $p$, the second prime number $q$ is constructed in the following way:
$$\alpha \cdot (p - 1) \equiv 1 \pmod{e}$$
$$q = (p\alpha \bmod e) + k\cdot e$$
$k$ is a positive integer such as $q$ is a prime number and $e$ is the public exponent which is also a random prime number.
The following code is the implementation in Python:
def gen_rsa_parameters():
r = os.urandom(63)
e = int(r.encode('hex'), 16)
e = next_prime(e)
r = os.urandom(64)
p = int(r.encode('hex'), 16)
p = next_prime(p)
q = (p*modinv(p-1, e)%e)
while not is_prime(q):
q += e
N = p*q
phi = (p-1)*(q-1)
d = modinv(e,phi)
return N,e,d,p,q
Theoretical attack
Let’s $N^{\prime} = N \bmod{e}$. So we have
\begin{eqnarray}
N^{\prime} & \equiv & p \cdot q \pmod{e} \\\
& \equiv & p \cdot ((p\alpha \bmod e) + k\cdot e) \pmod{e} \\\
& \equiv & p^2\alpha \pmod{e} \\\
\end{eqnarray}
We have $\alpha$ in the equation so we can introduce $p - 1$ to remove $\alpha$
\begin{eqnarray}
N^{\prime} & \equiv & (p - 1 + 1)^2\alpha \pmod{e} \\\
& \equiv & (p - 1)^2\alpha + 2(p - 1)\alpha + \alpha \pmod{e} \\\
& \equiv & (p - 1) + 2 + \alpha \pmod{e} \\\
(p - 1)N^{\prime} & \equiv & (p - 1)^2 + 2(p - 1) + 1 \pmod{e}
\end{eqnarray}
$$\boxed{(p - 1)^2 - (N^{\prime} - 2)(p - 1) + 1 \equiv 0 \pmod{e}}$$
Now we have a quadratic equation which only depends on $p$.
Let’s $X = p - 1$ and suppose that $N^{\prime} - 2$ is even and $N^{\prime} - 2 = 2b$.
\begin{eqnarray}
X^2 - 2bX + 1 & \equiv & 0 & \pmod{e} \\\
(X - b)^2 - b^2 + 1 & \equiv & 0 & \pmod{e}\\\
(X - b)^2 & \equiv & b^2 - 1 & \pmod{e}
\end{eqnarray}
by using
quadratic residue we can find a solution. We can also use SAGE and the sqrt()
function:
Np = N % e
b = (Np - 2) / 2
p = Mod(pow(b, 2) - 1, e).sqrt() + b + 1
At this point, we find $p \bmod{e}$ but not $p$ !
I tried to find $p$ by adding some $e$ but the distance between $p$ and $p \bmod{e}$ is huge. So I had to find another way.
By knowing $p \bmod{e}$ we can compute $\alpha$. Remember $$\alpha \cdot (p - 1) \equiv 1 \pmod{e}$$ and by having $\alpha$ and $p \bmod{e}$ we can brute force $q$ by adding $e$ until $(p\alpha \bmod e) + k\cdot e$ is prime and it divide $N$.
We did the assumption that $N^{\prime} - 2$ has to be even (so $N^{\prime}$ must be even) and in the certificate $N^{\prime}$ is even so everything is right.
Pratical Attack
First we have to extract the modulus $N$ and the public key $e$ from the certificate:
$ openssl x509 -in hitbctf.crt -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 18379438180976429416 (0xff10e1a5ac5a0968)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=NL, ST=Noord-Holland, L=Amsterdam, O=HITB, OU=CTF
Validity
Not Before: May 24 09:58:26 2015 GMT
Not After : May 23 09:58:26 2016 GMT
Subject: C=NL, ST=Noord-Holland, L=Amsterdam, O=HITB, OU=CTF
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:e6:eb:89:c1:8d:49:c9:a2:02:2b:e0:b4:65:14:
6e:0f:90:45:1e:a3:4c:6b:60:56:00:4e:bd:15:59:
55:b1:35:96:c2:d6:83:ad:2f:23:6b:0b:2c:0e:0b:
88:83:b5:d6:cb:8a:0b:4f:f9:b7:eb:64:8c:95:2b:
6b:ef:5a:6f:04:f5:64:17:f5:1c:a9:14:d9:ea:73:
e7:dd:c5:f2:0d:ce:c3:9c:e8:4b:72:2a:0c:f3:d8:
5e:80:ce:78:64:63:e1:44:f6:1d:b5:9c:cf:45:ff:
0e:d3:7f:d0:ce:bd:37:a5:8d:8a:4b:08:33:9e:a3:
2c:bc:ab:61:64:03:fd:2c:c5
Exponent:
69:60:2d:93:8a:81:5f:14:cf:9f:b8:36:c2:e0:4d:
4d:De:82:ba:fc:8d:56:c2:6d:8c:89:ef:3c:40:69:
5d:d5:d4:ef:a7:36:36:43:15:14:95:f3:8c:bf:24:
ae:94:30:92:40:79:12:00:1b:17:f5:53:33:9e:92:
70:70:49
Signature Algorithm: sha1WithRSAEncryption
17:2b:ea:be:90:ad:98:f2:2b:ff:f5:61:d3:ea:af:fb:35:3a:
67:10:91:13:db:60:55:d9:09:8b:c2:1a:cf:6b:c6:1f:f2:10:
7a:d1:7b:9d:ff:10:f2:f2:c0:a9:f5:aa:2e:09:93:40:88:92:
7d:98:ff:e1:cb:dc:db:35:8d:e0:4b:21:99:76:bf:db:04:a2:
62:a4:18:4e:fc:bb:a7:53:be:6a:a1:ef:ec:15:86:c1:f1:1e:
87:6a:e9:af:fe:d1:08:eb:de:22:28:c4:5e:be:f1:41:0a:ca:
cf:cf:da:63:b1:c1:56:e8:0c:8e:56:7f:08:94:0d:2b:2a:08:
N = 1621575882314321757502664197090844942567381491984167028188381926885851
995558397927547394115469298694885747314992315746872071523931715177680193273
386465775883129725436206653605910592810579794603402792446164893148622893121
957048204358672599654432857497196823273138934901636721473789115585263150131
66594183212229
e = 21558488234539889837938770635971330903489839146766895224490179041465516
1931455822669631548838317075220811407344210520390992334648372016602816069805
30249
With SAGE:
sage: Np = N % e
sage: b = (Np - 2) / 2
sage: pp = int(Mod(pow(b, 2) - 1, e).sqrt()) + b + 1
sage: alpha = inverse_mod(int(X), int(e))
sage: q = (pp * alpha) % e
sage: while not is_prime(q) and N % q != 0:
....: q += e
sage: p = N / q
sage: p
13317713478157317654574552532079837937895228108820477140030796245493222349714497856652987583926206280627498615972491072112647669795345566943409669535038641
sage: q
12176083266650126897170100375931110708350668494730113414987801764299563774952801449439933220072280766145748279998832962142839152786620322097065894585706069
We can now generate the private key by using rsatool:
$ ./rsatools.py -o private.pem \
-e 21558488234539889837938770635971330903489839146766895224490179041465516193145582266963154883831707522081140734421052039099233464837201660281606980530249 \
-p 13317713478157317654574552532079837937895228108820477140030796245493222349714497856652987583926206280627498615972491072112647669795345566943409669535038641 \
-q 12176083266650126897170100375931110708350668494730113414987801764299563774952801449439933220072280766145748279998832962142839152786620322097065894585706069
Finally, we can decrypt the message:
openssl smime -decrypt -in mail.msg -inkey private.pem
hitb{0b21cc2025534dbd2965390d2bcef45d}
The sources are available here