david wong

Hey! I'm David, cofounder of zkSecurity and the author of the Real-World Cryptography book. I was previously a crypto architect at O(1) Labs (working on the Mina cryptocurrency), before that I was the security lead for Diem (formerly Libra) at Novi (Facebook), and a security consultant for the Cryptography Services of NCC Group. This is my blog about cryptography and security and other related topics that I find interesting.

Hacking Week 2015 : Crypto 4 Write-Up posted June 2015

The Hacking Week ended 2 weeks ago and EISTI got the victory.

I'm the proud creator of the crypto challenge number 4, still available here, which was solved 12 times.

crypto4

I also wrote a Proof of Solvableness, reading it should teach you about a simple and elegant crypto attack on RSA: the Same Modulus Attack.

(Note that I wrote that back in January)

Let's start

We are presentend with 4 files:

  • alice.pub
  • irc.log
  • mykey.pem
  • secret

the irc.log reads like this:

Session Start: Thu Feb 05 20:49:04 2015
Session Ident: #mastercsi
[20:49] * Now talking in #mastercsi
[20:49] * Topic is 'http://www.math.u-bordeaux1.fr/CSI/ |||| http://www.youtube.com/watch?v=zuDtACzKGRs   "das boot, ouh, ja" ||| http://www.koreus.com/video/chat-saut-balcon.html ||| http://blog.cryptographyengineering.com/ ||| http://www.youtube.com/watch?v=K1LZ60eMpiw ||| petit chat http://www.youtube.com/watch?v=eu2kVcWKvRo ||| sun : t'as le droit de boire quand même va'
[20:49] * Set by Jiss!~Jiss@2001:41d0:52:100::65d on Sat Nov 22 00:06:50
[20:49] <asdf> et donc j'ai chopé une vieille clé rsa qu'alice utilisait
[20:49] <qwer> alice la alice? tu te fous de moi ?
[20:49] <asdf> haha non
[20:49] <asdf> mais le truc est corrompu, ça a l'air de marcher pour chiffrer mais la moitié de la clé a disparu
[20:49] <qwer> attend j'ai sa clé publique qui traine quelque part, et même un fichier chiffré avec. me suis toujours demandé ce que c'était...
[20:50] <asdf> je t'ai envoyé le truc, mais ça m'étonnerait que ça soit la même clé non ?
[21:22] * Disconnected
Session Close: Thu Feb 05 21:22:11 2015

so alice.pub seems to be alice public rsa key. secret seems to be the file encrypted under this key and mykey.pem should be the partial key which was found.

Private-Key: (1024 bit)
modulus:
    00:c6:c8:35:29:a2:38:8f:14:63:65:c5:f5:fd:4b:
    0d:88:89:61:b9:5d:e1:0f:fa:88:53:a3:c2:cb:ed:
    75:0e:99:59:bd:0f:f8:72:c2:23:2f:6b:ad:32:62:
    4f:35:6a:82:d0:62:75:5e:1e:4f:ed:ae:54:e8:ca:
    24:71:fc:8d:13:ac:70:0e:e2:57:20:d4:d9:08:9f:
    d6:fb:d4:2f:12:e6:a4:1e:1c:1d:e8:1f:57:8c:32:
    13:2a:d0:85:94:e8:51:84:1d:02:39:cd:41:0d:ef:
    11:d1:c1:5e:e7:5b:92:f8:6a:04:f7:c6:c7:f3:6b:
    90:46:b8:fb:2f:e2:95:65:b1
publicExponent: 3 (0x3)
privateExponent:
    00:84:85:78:c6:6c:25:b4:b8:42:43:d9:4e:a8:dc:
    b3:b0:5b:96:7b:93:eb:5f:fc:5a:e2:6d:2c:87:f3:
    a3:5f:10:e6:7e:0a:a5:a1:d6:c2:1f:9d:1e:21:96:
    df:78:f1:ac:8a:ec:4e:3e:be:df:f3:c9:8d:f0:86:
    c2:f6:a8:5e:0b:ef:c0:ca:19:c5:e2:49:55:49:fe:
    e5:2e:51:3e:7b:e9:f2:22:07:d2:4b:84:7f:bb:0c:
    b5:ba:b7:95:c6:90:05:3e:65:2d:11:53:9a:2d:96:
    0f:ea:de:cb:9b:17:54:87:00:0f:78:12:ce:ac:f5:
    db:83:30:16:06:cc:35:7d:a3
prime1: 245 (0xf5)
prime2: 207 (0xcf)
exponent1: 163 (0xa3)
exponent2: 138 (0x8a)
coefficient: 189 (0xbd)

It looks like prime1, prime2 and some other stuff are pretty short. I guess this is what he meant by "half the key" is messed up.

By the way this is what a RSA PrivateKey should look like:

> RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER,  -- n
    publicExponent    INTEGER,  -- e
    privateExponent   INTEGER,  -- d
    prime1            INTEGER,  -- p
    prime2            INTEGER,  -- q
    exponent1         INTEGER,  -- d mod (p-1)
    exponent2         INTEGER,  -- d mod (q-1)
    coefficient       INTEGER,  -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

So this is what exponent1, exponent2 and coefficient are. Just additional information so that computations are faster thanks to CRT.

Let's ignore that for the moment.

$ openssl rsa -pubin -in alice.pub -modulus -noout
Modulus=C6C83529A2388F146365C5F5FD4B0D888961B95DE10FFA8853A3C2CBED750E9959BD0FF872C2232F6BAD32624F356A82D062755E1E4FEDAE54E8CA2471FC8D13AC700EE25720D4D9089FD6FBD42F12E6A41E1C1DE81F578C32132AD08594E851841D0239CD410DEF11D1C15EE75B92F86A04F7C6C7F36B9046B8FB2FE29565B1
$ openssl rsa -in mykey.pem -modulus -noout
Modulus=C6C83529A2388F146365C5F5FD4B0D888961B95DE10FFA8853A3C2CBED750E9959BD0FF872C2232F6BAD32624F356A82D062755E1E4FEDAE54E8CA2471FC8D13AC700EE25720D4D9089FD6FBD42F12E6A41E1C1DE81F578C32132AD08594E851841D0239CD410DEF11D1C15EE75B92F86A04F7C6C7F36B9046B8FB2FE29565B1

the partial key and alice public key seems to share the same modulus. this is vulnerable. If our public/private exponents are not messed up, this means we can factor the modulus and thus inverse Alice's public key.

Let's retrieve all the info we have and put them in a file:

openssl rsa -pubin -in alice.pub -modulus -noout | sed 's/Modulus=//' | xclip -selection c

Here's the modulus. We know that our public key is 3, let's get the private key in the clipboard as well

openssl asn1parse -in mykey.pem | grep 129 | tail -n1 | awk '{ print $7}' | sed 's/://' | xclip -selection c

here I parse mykey.pem with openssl. I select the lines I want with grep. It returns two results, the modulus and the private key. I select only the second line with tail. I select only the 7th column with awk. I remove the : with sed. And now I have a beautiful integer in my clipboard.

Okay so let's do a bit of Sage now:

# let's write the info we have
modulus = int(0xC6C83529A2388F146365C5F5FD4B0D888961B95DE10FFA8853A3C2CBED750E9959BD0FF872C2232F6BAD32624F356A82D062755E1E4FEDAE54E8CA2471FC8D13AC700EE25720D4D9089FD6FBD42F12E6A41E1C1DE81F578C32132AD08594E851841D0239CD410DEF11D1C15EE75B92F86A04F7C6C7F36B9046B8FB2FE29565B1)

public = 3
private = int(0x848578C66C25B4B84243D94EA8DCB3B05B967B93EB5FFC5AE26D2C87F3A35F10E67E0AA5A1D6C21F9D1E2196DF78F1AC8AEC4E3EBEDFF3C98DF086C2F6A85E0BEFC0CA19C5E2495549FEE52E513E7BE9F22207D24B847FBB0CB5BAB795C690053E652D11539A2D960FEADECB9B175487000F7812CEACF5DB83301606CC357DA3)

# now let's factor the modulus
k = (private * public - 1)//2
carre = 1
g = 2
while carre == 1 or carre == modulus - 1:
    g += 1
    carre = power_mod(g, k, modulus)

p = gcd(carre - 1, modulus) 
print(p)

This does not work. This should work.

Let's re-do the maths:

We know that our private and public keys cancel out. This is RSA:

private * public = 1 mod phi(N)

so we have private * public - 1 = 0 mod phi(N)

So for any g in our ring, we should have g^(private * public - 1) = 1 mod N

This is how RSA works.

Let's write it like that: private * public - 1 = k with k a multiple of phi(n). And we know that phi(n) = (p-1)(q-1) is even. So it could be written like this: k = 2^t * r with r an odd number.

Now if we take a random g mod N and we do g^(k/2) it should be the square root of a 1.

The Chinese Remainder Theorem tells us that there are 4 square roots mod N:

  • 1 mod p
  • -1 mod p
  • 1 mod q
  • -1 mod q

and two of them should be 1 mod N and -1 mod N. The 2 others should be different from 1 and -1 mod N. That's what I was trying to find in my code.

Once we have found this x mod N which is a square root of 1 mod N, we know that it is either x = 1 mod p or x = -1 mod p. If we are in the first case, we shoudl have x - 1 = 0 mod p which translates into x - 1 is a multiple of p. Doing gcd(x - 1, N) should give us p the first prime. If you don't understand it maybe check Dan Boneh's explanation (proof end of page 3) which should be clearer than mine.

With p it's easy to get q the other prime.

But it doesn't work...

Ah! I forgot that g^(k/2) could equal 1 all the time if k/2 were to be a multiple of phi(n). So let's code a loop that divides k by 2 and tries any g^k until it is giving us something else than 1. Then we know how many times we have to divide k by 2 so it's not a multiple of phi(n) anymore.

It turns out we just have to do it 3 times. And then it magically works. A bit more of Sage gives us the primes:

# p and q our primes
p = gcd(carre - 1, modulus) 
q = modulus // p

# now that we have factored N let's find alice decryption key
public = 65537
phi = (p - 1) * (q - 1)
private = inverse_mod(public, phi)

Now that we have Alice's private key there are two ways to decrypt our secret:

  • recreate a valid rsa key with those values and use openssl rsautl
  • figure out how openssl rsautl works to do it ourselves

Let's do the first one. We'll modify our mykey.pem for this:

openssl rsa -in  mykey.pem -outform DER -out newkey.bin
xxd -p newkey.bin > newkey.hex

we get this:

3082012202010002818100c6c83529a2388f146365c5f5fd4b0d888961b9
5de10ffa8853a3c2cbed750e9959bd0ff872c2232f6bad32624f356a82d0
62755e1e4fedae54e8ca2471fc8d13ac700ee25720d4d9089fd6fbd42f12
e6a41e1c1de81f578c32132ad08594e851841d0239cd410def11d1c15ee7
5b92f86a04f7c6c7f36b9046b8fb2fe29565b102010302818100848578c6
6c25b4b84243d94ea8dcb3b05b967b93eb5ffc5ae26d2c87f3a35f10e67e
0aa5a1d6c21f9d1e2196df78f1ac8aec4e3ebedff3c98df086c2f6a85e0b
efc0ca19c5e2495549fee52e513e7be9f22207d24b847fbb0cb5bab795c6
90053e652d11539a2d960feadecb9b175487000f7812ceacf5db83301606
cc357da3020200f5020200cf020200a30202008a020200bd

This is a DER encoding. One particular encoding from the ASN.1 family. It is a TLV kind of encoding (Type Lenght Value).

For example in:

02 8181 00c6c83529a2388f146365c5f5fd4b0d888961b95de10ffa8853
a3c2cbed750e9959bd0ff872c2232f6bad32624f356a82d062755e1e4fed
ae54e8ca2471fc8d13ac700ee25720d4d9089fd6fbd42f12e6a41e1c1de8
1f578c32132ad08594e851841d0239cd410def11d1c15ee75b92f86a04f7
c6c7f36b9046b8fb2fe29565b1

first is coded the type 02 (integer), then the length (81 repeated twice because the value block is bigger than 127bits, so we set the first byte to 81 (10000001, the first bit means it is a long way of defining the length, the 7 following bits are the number of byte it will take to define the length, in our case only one and it will be the next one) and the second byte to the actual size), then there is our modulo in hexadecimal. Note that the most significant bit of our value has to be zero if it is a positive integer, that's why we use 41 instead of 40 and lead the payload with 00.

So let's take the time and break this apart:

3082 // some header
0122 // the length of everything that follows (in byte)
0201 // integer of size 1
00 
028181 // integer of size 0x81 (our modulus)
00c6c83529a2388f146365c5f5fd4b0d888961b95de10ffa8853a3c2cbed750e9959bd0ff872c2232f6bad32624f356a82d062755e1e4fedae54e8ca2471fc8d13ac700ee25720d4d9089fd6fbd42f12e6a41e1c1de81f578c32132ad08594e851841d0239cd410def11d1c15ee75b92f86a04f7c6c7f36b9046b8fb2fe29565b1
0201 // integer of size 1 (our public key)
03
028181 // integer of size 0x81 (our private key)
00848578c66c25b4b84243d94ea8dcb3b05b967b93eb5ffc5ae26d2c87f3a35f10e67e
0aa5a1d6c21f9d1e2196df78f1ac8aec4e3ebedff3c98df086c2f6a85e0b
efc0ca19c5e2495549fee52e513e7be9f22207d24b847fbb0cb5bab795c6
90053e652d11539a2d960feadecb9b175487000f7812ceacf5db83301606
cc357da3
0202 // integer of size 2 (prime 1)
00f5
0202 // integer of size 2 (prime 2)
00cf
0202 // integer of size 2 (exponent 1)
00a3
0202 // integer of size 2 (exponent 2)
008a
0202 // integer of size 2 (coefficient)
00bd

Now let's remove everything which is after the modulus and let's refill the file with our own values. Let's go back in Sage to calculate them:

public = 65537
phi = (p - 1) * (q - 1)
private = inverse_mod(public, phi)
exponent1 = inverse_mod(private, p - 1)
exponent2 = inverse_mod(private, q - 1)
coefficient = inverse_mod(q, p)

After filling and modifying the header's length accordingly, we obtain a nice hexadecimal file that we can transform back to binary:

xxd -r -p new_key.hex | openssl asn1parse -inform DER

It works! So now let's decrypt with is shall we?

xxd -r -p new_key.hex | openssl rsa -inform DER -outform PEM -out newkey.pem
openssl rsautl -decrypt -in secret -inkey newkey.pem

We have our secret :)!

Well done! You've reached the end of my post. Now you can leave a comment or read something else.

Comments

ddddavidee

Salut, pourrais tu mettre la clé new_key.hex sur ce post?
je ne sais pas où je me plante mais j'arrive pas au bon format DER.

merci!

david

euh, je n'ai plus les fichiers et j'ai fait ce challenge il y a un bon bout de temps :) mais je peux t'aider si tu es bloque a une etape, tu n'arrives pas a convertir mykey.pem en DER? le command tool `openssl` te permets de le faire, en faisant -outform DER ou quelque chose comme ca.

ddddavidee

nah, c'est le contraire. J'ai toutes les valeurs et je dois les transformer en DER. je pense que j'ai raté quelques passages quand j'ai "corrigé" le file newkey.hex avec les nouvelles valeurs.
(si j'ai encore une copie du file je te l'envoie et tu peux y donner un coup d'oeuil)

leave a comment...