Il existe deux formats pour stocker une clé SSH privée chiffrée. L'un d'eux repose sur une clé de chiffrement symétrique dérivée de la phrase de passe via la fonction cryptographique MD5 utilisée avec un sel, ce qui peut être compromis par une attaque par force brute ou par dictionnaire.
Il n'y a pas lieu de s'alarmer car la faiblesse de ce format de stockage d'une clé SSH, connue depuis plus de 6 ans, est très théorique, car :
Note : si t'as une clé SSH ed25519, alors tu utilises forcément le nouveau (2013 tout de même) format de stockage, qui n'est pas vulnérable (car il repose sur bcrypt et 16 itérations). C’est le man ssh-keygen
qui le dit.
The private key is an ASN.1 data structure, serialized to a byte string using DER, and then Base64-encoded.
[…]
cat test_rsa_key
[…]We’ve gained two header lines, and if you try to parse that Base64 text, you’ll find it’s no longer valid ASN.1. That’s because the entire ASN.1 structure we saw above has been encrypted, and the Base64-encoded text is the output of the encryption. The header tells us the encryption algorithm that was used: AES-128 in CBC mode. The 128-bit hex string in the DEK-Info header is the initialization vector (IV) for the cipher. This is pretty standard stuff; all common crypto libraries can handle it.
But how do you get from the passphrase to the AES encryption key? I couldn’t find it documented anywhere, so I had to dig through the OpenSSL source to find it:
- Append the first 8 bytes of the IV to the passphrase, without a separator (serves as a salt).
- Take the MD5 hash of the resulting string (once).
[…]
The digest algorithm is hard-coded to be MD5, which means that without changing the format, it’s not possible to upgrade to another hash function (e.g. SHA-1). This could be a problem if MD5 turns out not to be good enough.
grep 'BEGIN OPENSSH' $cheminVersLaCleÀTester
Si cette commande retourne rien : la clé utilise le nouveau format. Si elle retourne une ligne, la clé utilise l'ancien format qui est vulnérable.
Alors, alors, alors, comment on casse la passphrase d'une clé privée SSH ? J'actualise ce tutoriel : Recover Your GPG Passphrase using 'John the Ripper' trouvé via How to bruteforce an RSA private-key's passphrase?.
On installe la version jumbo de John The Ripper. Il s'agit d'une version améliorée par la communauté : « "Community enhanced" -jumbo versions add support for many more password hash types, including […] lots of other hash types, as well as many non-hashes such as OpenSSH private keys, S/Key skeykeys files, Kerberos TGTs, PDF files, ZIP (classic PKZIP and WinZip/AES) and RAR archives. » :
$ git clone https://github.com/magnumripper/JohnTheRipper
$ cd JohnTheRipper/src
$ ./configure
$ make -s clean && make -sj4
$ cd ..
run/ssh2john.py ~/.ssh/id_rsa > maclessh.hash
;On lance la recherche de la passphrase (ici, j'utilise la technique lente de la force brute, mais il est possible d'utiliser une attaque par dictionnaire de la manière habituelle avec John) :
$ run/john --incremental maclessh.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
tooree (/home/guigui/.ssh/id_rsa)
En utilisant le nouveau format de stockage qui repose sur bcrypt() et 16 itérations. Cela se fait en utilisant la commande suivante : ssh-keygen -p -o -f <fichier_clé_privée_actuelle>
.
Via https://twitter.com/aeris22/status/1025769363935252481 .