Cet article va aborder différentes techniques de cassage de clés privées SSH.

Une clé privée non chiffrée peut être utilisée par n’importe qui ayant accès au fichier.
Alors qu’une clé chiffrée, elle, ne sera utilisable que par les personnes connaissant le mot de passe permettant de déchiffrer la clé.
Ainsi, une clé privée chiffrée compromise n’a pas de valeur pour l’attaquant tant qu’il n’a pas réussi à la casser.

Dans une première partie, nous aborderons les différents types de clé disponibles et comment les générer. Dans une deuxième partie, nous verrons comment essayer de casser ces clés sommairement avec un script "naïf", John the Ripper et Hashcat.

Cet article est aussi disponible en anglais 🇬🇧.

Note 📝 : un lexique est disponible en fin de document.

Motivation

En tant qu’auditeur technique (ou apprenant sur un laboratoire virtuel), il est probable de tomber, tôt ou tard, nez à nez avec une clé privée SSH après avoir compromis une machine ou un compte utilisateur. À ce moment-là, il est intéressant de connaître les moyens à sa disposition pour pouvoir profiter de cette opportunité (déchiffrer la clé et l’utiliser). D’autant plus, qu’en cas de succès, cela permettrait de pivoter sur une des machines où elle est utilisable (voir comment casser un fichier known_hosts haché).

ssh-keygen

Général

Pour générer des clés de test, nous allons utiliser la commande ssh-keygen qui fait partie d’openssh.

Note 📝 : dropbear possède une commande dropbearkey mais ne supporte pas les clés chiffrées. Pas de bras clé chiffrée pas de chocolat cassage.

Pour installer openssh sur ArchLinux : pacman -Syu openssh. Pour les autres distributions, voir le nom du paquet sur repology.

Normalement, les clés privées générées seront stockées dans ~/.ssh/, c’est-à-dire dans le répertoire personnel de chaque utilisateur.

Cet article utilise ssh-keygen fourni avec OpenSSH 9.6 et reposant sur OpenSSL 3.2.1.

➜ ssh -V
OpenSSH_9.6p1, OpenSSL 3.2.1 30 Jan 2024

La base

Il est possible de générer une clé avec la configuration par défaut sans spécifier aucune option.

➜ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/noraj/.ssh/id_ed25519): /tmp/clé_démo
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /tmp/clé_démo
Your public key has been saved in /tmp/clé_démo.pub
The key fingerprint is:
SHA256:Fh+BPhBLTUY8/9n1b/DQQTvHoSkPZ4N20wpiOivM6DE noraj@norarch
The key's randomart image is:
+--[ED25519 256]--+
|      o*+..      |
|     ..o=  .   o |
|      .o.o. . =.o|
|        o=.B O =+|
|        S.+.Oo+o=|
|       +    oo+ o|
|   E+   o      +.|
|   .o+ .        +|
|  ..  .        . |
+----[SHA256]-----+

Nous obtiendrons une clé privée de la sorte :

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBgxQkqMd
jyuADNv6HN31l5AAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBqcVNY4wFSlVOz+
EpJAa06Qtuv1uciGbBUCRHmHhbGoAAAAkNl+6oYEq7ZyEWuubCSBuATjTVf0if/QdNYWB6
e8NGSrpGEgoSCzaJOo2mnBp20P4G8hpT5RFs5skfEWBlItEyX85FO2bj8YCIlRtCruaegC
f40zSY7acP8Y2YE5v6RCPZ9TT3TckMERlKsMSWM6ksmvBkoYLZq/kR7Od2XuQTrsAXdxMb
cHXF72FPtfdiN1Pw==
-----END OPENSSH PRIVATE KEY-----

Dont la clé publique correspondante est :

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBqcVNY4wFSlVOz+EpJAa06Qtuv1uciGbBUCRHmHhbGo noraj@norarch

Par défaut, la clé est générée au format privé OpenSSH (les autres formats supportés sont PEM, PKCS8 et RFC4716), mais cela peut changer selon le type de clé ou l’option utilisée.

-m key_format

Specify a key format for key generation, the -i (import), -e (export) conversion options, and the -p change passphrase operation. The latter may be used to convert between OpenSSH private key and PEM private key formats. The supported key formats are: “RFC4716” (RFC 4716/SSH2 public or private key), “PKCS8” (PKCS8 public or private key) or “PEM” (PEM public key). By default OpenSSH will write newly-generated private keys in its own format, but when converting public keys for export the default format is “RFC4716”. Setting a format of “PEM” when generating or up‐dating a supported private key type will cause the key to be stored in the legacy PEM private key format.

Note 📝 : RFC4716 est un format spécifique aux clés publiques, il ne s’applique donc pas aux clés privées. Je ne sais pas pourquoi la page man de ssh-keygen en parle aussi pour les clés privées. D’ailleurs, exporter une clé privée en demandant le format RFC4716 donnera une clé privée au format OpenSSH.

Voici la liste des formats supportés par ssh-keygen selon le type de clé :

Type ⬇️ Format ➡️ Défaut PEM PKCS8 OpenSSH
DSA OpenSSH
RSA OpenSSH
ecdsa OpenSSH
ed25519 OpenSSH

Attention : Par exemple, ssh-keygen -t ed25519 -f clé_ed25519 -m PKCS8 fournira une clé au format OpenSSH et pas PKCS8 (solution de repli silencieuse). Si le format demandé n’est pas disponible, il sera remplacé par le format privé OpenSSH.

Pour pouvoir lire la clé privée dans un format compréhensible par un humain, il faut utiliser l’une des commandes ci-dessous selon le type de clé et le format.

# DSA
openssl dsa -in clé_dsa_pem -text -noout
openssl dsa -in clé_dsa_pkcs8 -text -noout
# RSA
openssl rsa -in clé_rsa_pem -text -noout
openssl rsa -in clé_rsa_pkcs8 -text -noout
# ecdsa
openssl ec -in clé_ecdsa_pem -text -noout
openssl ec -in clé_ecdsa_pkcs8 -text -noout

Pour les clés ed25519, qui ne peuvent être générées par ssh-keygen qu’au format OpenSSH, il faudra les convertir en amont, par exemple à l’aide de la commande sshpk-conv fourni dans le paquet npm sshpk.

# ed25519
npm i -g sshpk
sshpk-conv clé_ed25519_openssh -t pem -p -o clé_ed25519_pem
sshpk-conv clé_ed25519_openssh -t pkcs8 -p -o clé_ed25519_pkcs8

Cependant, même au format PEM ou PKCS8, il sera impossible de les lire directement avec OpenSSL.

The only Elliptic Curve algorithms that OpenSSL currently supports are Elliptic Curve Diffie Hellman (ECDH) for key agreement and Elliptic Curve Digital Signature Algorithm (ECDSA) for signing/verifying.

x25519, ed25519 and ed448 aren’t standard EC curves so you can’t use ecparams or ec subcommands to work with them. If you need to generate x25519 or ed25519 keys then see the genpkey subcommand.

_Source_

En effet, Ed25519 est le système de signature EdDSA utilisant SHA-512 (SHA-2) et Curve25519 (cas particulier d’EdDSA), il n’y aurait de toute façon pas grand-chose à afficher.

Types de clés

Voici les différents types de clés supportés par ssh-keygen :

  • DSA
  • RSA
  • ecdsa
  • ecdsa-sk
  • ed25519
  • ed25519-sk

Les deux types terminant par MARKDOWN_HASHdcf8c5b65ddef54be59c5ece40fffecdMARKDOWNHASH (Security Key_) sont des variantes spécifiquement réservées aux appareils d’authentification à deux facteurs matériels supportant FIDO/U2F (ex : YubiKey).

Pour spécifier le type de clé à générer, il faut utiliser l’option -t.

Types de chiffrements

Les différents algorithmes de chiffrement sont :

  • 3DES CBC
  • AES 128/192/256 CBC/CTR/GCM
  • chacha20-poly1305

La liste peut potentiellement différer selon la version d’OpenSSL installée. La commande ci-dessous permet de lister ceux qui sont supportés :

➜ ssh -Q cipher
3des-cbc
aes128-cbc
aes192-cbc
aes256-cbc
aes128-ctr
aes192-ctr
aes256-ctr
aes128-gcm@openssh.com
aes256-gcm@openssh.com
chacha20-poly1305@openssh.com

Pour spécifier l’algorithme de chiffrement, il faut utiliser l’option -Z.

Exemples de génération

Voici quelques scripts ZSH pour générer des clés SSH.

Générer une clé de chaque type de clé :

#!/usr/bin/env zsh

for key_type in dsa rsa ecdsa ed25519 # ecdsa-sk ed25519-sk
do
  ssh-keygen -N '123soleil' -t ${key_type} -f /tmp/all-keys/clé_${key_type}_openssh
done

Générer une clé pour chaque algorithme de chiffrement :

#!/usr/bin/env zsh

for cipher in $(ssh -Q cipher)
do
  ssh-keygen -N '123soleil' -t ed25519 -Z $cipher -f /tmp/all-ciphers/clé_ed25519_${cipher}_openssh
done

Générer une clé pour chaque type de clé avec chaque algorithme de chiffrement :

#!/usr/bin/env zsh

for key_type in dsa rsa ecdsa ed25519 # ecdsa-sk ed25519-sk
do
  for cipher in $(ssh -Q cipher)
  do
    ssh-keygen -N '123soleil' -t $key_type -Z $cipher -f /tmp/all-keys-ciphers/clé_${key_type}_${cipher}_openssh
  done
done

Générer une clé pour chaque type de clé avec chaque format d’export :

#!/usr/bin/env zsh

for key_type in dsa rsa ecdsa ed25519 # ecdsa-sk ed25519-sk
do
  for format in OpenSSH PEM PKCS8 RFC4716
  do
    if [[ $format = 'OpenSSH' ]]
    then
      ssh-keygen -N '123soleil' -t $key_type -f /tmp/all-keys-formats/clé_${key_type}_${format}
    else
      ssh-keygen -N '123soleil' -t $key_type -f /tmp/all-keys-formats/clé_${key_type}_${format} -m ${format}
    fi
  done
done

Générer un fichier contenant le pseudo-haché pour chaque type de clé avec chaque algorithme de chiffrement au format OpenSSH et PEM (ce sera utile pour plus tard) :

#!/usr/bin/env zsh

for key_type in dsa rsa ecdsa ed25519 # ecdsa-sk ed25519-sk
do
  for cipher in $(ssh -Q cipher)
  do
    ssh2john /tmp/all-keys-ciphers/clé_${key_type}_${cipher}_openssh > /tmp/all-keys-ciphers/clé_${key_type}_${cipher}_openssh.jtr
    ssh2john /tmp/all-keys-ciphers/clé_${key_type}_${cipher}_PEM > /tmp/all-keys-ciphers/clé_${key_type}_${cipher}_PEM.jtr
  done
done

Approches

Nous allons maintenant essayer de casser ces clés sommairement avec trois méthodes :

  • script "naïf",
  • John the Ripper,
  • Hashcat.

Par soucis de simplicité, pour chaque méthode, nous considérons uniquement l’attaque par dictionnaire.

Les temps indicatifs d’exécution sont donnés pour un processeur Intel i5-1145G7 @ 2.6 GHz (script, John the Ripper) doté d’un iGPU Intel® Iris® Xe Graphics (Hashcat) fonctionnant avec OpenCL 3.0 NEO.

Naïve (script)

Ce script Ruby, dit naïf, va lire un dictionnaire de mots de passe et exécuter une commande ssh-keygen avec le mot de passe candidat jusqu’à ce que ssh-keygen renvoie autre chose qu’une erreur. Ce qui indiquera alors que le bon mot de passe a été trouvé.

require 'open3'

if ARGV.size == 2
  password_found = false

  File.readlines(ARGV[1], chomp: true).each do |password|
    Open3.popen3("ssh-keygen -y -f #{ARGV[0]} -P '#{password}'") { |i,o,e,t|
      error = e.read.chomp
      if error.empty?
        puts "\nThe password is: #{password}"
        password_found = true
      elsif /incorrect passphrase supplied to decrypt private key/.match?(error)
        print '.'
      else
        puts "Error: #{t.value}"
        puts error
      end
    }
    break if password_found
  end
else
  puts "Usage  : ruby #{__FILE__} SSH_KEY WORDLIST"
  puts "Example: ruby #{__FILE__} ~/.ssh/id_ed25519_crack /usr/share/wordlists/passwords/richelieu-french-top20000.txt"
end

Note 📝 : l’utilisation de l’option -y ne fonctionne que pour le format OpenSSH. Pour une clé au format PEM, il faudra une commande tentant de changer le mot de passe (ou supprimer le chiffrement) de la clé : ssh-keygen -f /chemin/clé -m pem -p -P tentative_mdp. Mais n’effectuez pas cette opération dans le cadre d’un audit sans l’autorisation du client, ou alors copiez la clé localement avant.

Ce script s’est exécuté en 89 secondes (ou 80 sans l’affichage de . quand le mot de passe n’est pas bon).

Sortie d’affichage :

➜ time ruby ssh-bf.rb ~/.ssh/id_ed25519_crack /usr/share/wordlists/passwords/richelieu-french-top20000.txt
.....................................................................................................................................................................................................................
.....................................................................................................................................................................................................................
.........................................................................................................................
The password is: fripouille
ruby ssh-bf.rb ~/.ssh/id_ed25519_crack   87,56s user 1,28s system 99% cpu 1:29,02 total

Ce script est notamment dit "naïf" car il n’est pas multifil (pas d’utilisation de fil d’exécution ou processus fils), ce qui est extrêmement peu performant.

Par contre, l’avantage de cette méthode est de fonctionner pour tous les formats de fichiers, algorithmes de chiffrement ou types de clé supportés, car elle utilise directement ssh-keygen.

John the Ripper

Comme une très grande durée sépare souvent deux versions de JtR, il existe souvent de nombreux bogues corrigés ou nouvelles fonctionnalités dans la branche principale du dépôt git qui ne le sont pas dans la dernière version disponible. Ainsi, je recommande d’installer la version git de JtR (ex : john-git pour ArchLinux).

Tous les tests seront effectués avec la version ci-dessous :

John the Ripper 1.9.0-jumbo-1+bleeding-173b5629e8 2024-01-18 00:08:42 +0100 MPI + OMP [linux-gnu 64-bit x86_64 AVX AC]

ssh2john permet de créer un pseudo-haché compréhensible par JtR à partir d’une clé privée SSH.

➜ ssh2john ~/.ssh/id_ed25519_crack > /tmp/hash_jtr.txt
/home/noraj/.ssh/id_ed25519_crack:$sshng$6$16$3843af13aef53d4c0906f2998b082b3d$274$6f70656e7373682d6b65792d7631000000000a6165733235362d6374720000000662637279707400000018000000103843af13aef53d4c0906f2998b082b3d0000001800000001000000330000000b7373682d6564323535313900000020785c404a9750e39a4cdb788e787f13fdf0d62ca91ea76e3034272722980c222d00000090a99a138ebcd23a3fac923d88fb2b42833e4fc29d409efe86d543f8224cd11263b511e6cc858919bb58692a07664fb56905915bfe8d4a31db398827a65070f33dc127c3ca7d2ad9d184922e7a5e657de10166ee6adfc0b4cc736567adaeb8b1a160d008b1e5bd0a0188be18152d8eecec7bbd9b35d8f551e059bc57fa7642b7535a4d1aad8bb616576b9fb6b2e62bb7e5$24$130

Cette fois-ci, le cassage prend 18 secondes, ce qui est beaucoup moins que l’approche naïve, grâce à l’exécution multifil.

➜ time john /tmp/hash_jtr.txt -w=/usr/share/wordlists/passwords/richelieu-french-top20000.txt --format=ssh
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
fripouille       (/home/noraj/.ssh/id_ed25519_crack)
1g 0:00:00:15 DONE (2024-01-24 17:23) 0.06601g/s 38.02p/s 38.02c/s 38.02C/s michael1..poiuyt
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
john /tmp/hash_jtr.txt  --format=ssh  136,21s user 0,04s system 719% cpu 18,947 total

Ce qui est aussi pratique avec JtR, c’est que peu importe le type de clé ou les algorithmes de chiffrement utilisés, la référence à spécifier est toujours la même : ssh.

Types de clés supportés par JtR et ssh2john :

Type JtR ssh2john
DSA
RSA
ecdsa
ed25519

Formats de clé supportés par ssh2john :

Format Support
OpenSSH
PEM
PKCS8
RFC4716 N/A

Algorithmes de chiffrement supportés par ssh2john (possiblement certains algorithmes non supportés par ssh2john le sont par john mais aucun outil ne permet de générer le pseudo-haché) :

Algorithme ⬇️ Format ➡️ OpenSSH (tous) PEM (DSA, RSA, ecdsa) PEM (ed25519)
3des-cbc
aes128-cbc
aes128-ctr
aes128-gcm@openssh.com N/T
aes192-cbc
aes192-ctr
aes256-cbc
aes256-ctr
aes256-gcm@openssh.com N/T
chacha20-poly1305@openssh.com N/T

Note 📝 : Pour les clés de type ed25519, sshpk a été utiliser pour les convertir au format PEM car ssh-keygen ne peut les exporter qu’au format OpenSSH. Les algorithmes @openssh.com n’ont pas pu être convertis par sshpk. sshpk a été utile dans le cadre de la rédaction de l’article, mais ne le sera pas pour un auditeur, car convertir la clé requiert de connaitre le mot de passe.

Algorithmes de chiffrement supportés par JtR :

Algorithme Support
3des-cbc
aes128-cbc
aes128-ctr
aes128-gcm@openssh.com
aes192-cbc
aes192-ctr
aes256-cbc
aes256-ctr
aes256-gcm@openssh.com
chacha20-poly1305@openssh.com

En conclusion, ssh2john ne fonctionne qu’avec 2 algorithmes lorsque la clé privée est au format OpenSSH, ce qui est vraiment dommage, car c’est le format par défaut de ssh-keygen. Au format PEM, tous les algorithmes sont supportés par ssh2john pour les clés de type DSA, RSA, ecdsa mais aucun pour les clés de type ed25519.

Hashcat

Comme pour JtR, une très grande durée sépare souvent deux versions de Hashcat. Pour avoir les corrections de bogues ou les nouvelles fonctionnalités, il faudra installer la version git utilisant la branche principale du dépôt. Ainsi, je recommande d’installer la version git de Hashcat (ex : hashcat-git pour ArchLinux).

Tous les tests seront effectués avec la version ci-dessous :

v6.2.6-846-g4d412c8e0+

Le format du pseudo-code de hachage SSH géré par Hashcat semble être celui généré par ssh2john. Hashcat ne fournit aucun outil à cet effet. Cependant, le format supporté semble être une ancienne version et ne supporte pas le nouveau format requis pour les algorithmes utilisant du Bcrypt PBKDF (bcrypt_pbkdf.3) introduit en 2013 dans OpenSSH.

Contrairement à JtR, Hashcat utilise différents modules et a donc différentes références selon les clés. La distinction est faite selon les numéros d’identification choisis arbitrairement par ssh2john.

Nous verrons par la suite à quoi correspondent ces numéros dans une section dédiée.

Toutefois, Hashcat ne supporte pas le « nouveau » format de sortie de ssh2john tel quel : /chemin/fichier:pseudo-haché. Il faudra donc supprimer le chemin du fichier de sortie. La commande ci-dessous permet de supprimer le chemin de tous les fichiers .jtr (extension arbitraire) pour une édition en masse.

find . -type f -name '*.jtr' -exec sed -i -E 's/.+://' {} \;

Pour un haché seul, la commande suivante devrait suffire :

ssh2john clé_rsa_aes256-cbc_PEM_demo | cut -d ':' -f 2 > clé_rsa_aes256-cbc_PEM_demo.hc

Types de clés supportés par Hashcat :

Type HC
DSA
RSA
ecdsa
ed25519

Il n’y a pas de formats de clé supportés à proprement parler, car Hashcat ne propose pas d’outil de conversion de clé en pseudo-haché, mais repose sur ssh2john. Les mêmes limitations s’appliqueront donc.

Algorithmes de chiffrement supportés par Hashcat :

Algorithme ⬇️ Type ➡️ DSA RSA ecdsa ed25519
3des-cbc N/A
aes128-cbc N/A
aes128-ctr N/A
aes128-gcm@openssh.com N/A
aes192-cbc N/A
aes192-ctr N/A
aes256-cbc
aes256-ctr
aes256-gcm@openssh.com N/A
chacha20-poly1305@openssh.com N/A

En conclusion, il est difficile de tester ed25519 car ssh2john a un faible support pour ce type de clé. À la sortie de ssh-keygen + ssh2john, seuls les numéros d’identification $1$, $2$, $3$ et $6$ sont sortis. $1$ et $3$ sont gérés par le même module 22931 et semble fonctionner. $1$ est géré par le module 22921, mais ne fonctionne jamais. Le $1$ (AES 256 bits CTR) est sorti uniquement au format OpenSSH pour tous les types de clé. $2$ n’est géré par aucun module Hashcat.
Finalement, le seul module qui est utilisable en pratique est donc le 22931, les autres correspondent soit à des combinaisons de clé qui ne sont jamais sorties par ssh-keygen (peut-être OpenSSL ? ou de très vieilles versions d’OpenSSH ?), soit qui ne sont pas gérés par ssh2john (il faudrait extraire les paramètres cryptographiques de la clé privée et créer un pseudo-haché à la main).

Note 📝 : avec HC impossible de casser la clé de référence ~/.ssh/id_ed25519_crack générée avec les paramètres par défaut de ssh-keygen.

Autres outils

À l’instar des archives chiffrées ou des conteneurs de mots de passe KDBX, il n’existe pas vraiment d’outils en dehors de JtR et HC pour casser les clés SSH chiffrées.

Il y a bien ssh-key-crack, outil en C, exécution monofil, abandonné depuis 16 ans, ne gérant que les clés du type RSA et DSA, au format PEM uniquement, directement sur la clé (sans format intermédiaire comme pour JtR et HC avec ssh2john).

Ou encore, sshPrivateKeyCrack, outil en python, archivé, abandonné depuis 2 ans, n’est qu’un script "naïf" faisant des appels système à ssh-keygen, supprime le chiffrement de la clé une fois le mot de passe trouvé, mais supporte l’exécution multifil.

Format ssh2john

Le format de pseudo-haché généré par ssh2john est arbitraire et ne correspond à aucun standard.

Il se décompose comme ci-dessous, avec le nom de fichier et le pseudo-haché séparé par : :

(fichier:)$sshng$alg_type$len_salt$salt$len_data$data($rounds$ciphertext_begin_offset)

Le pseudo-haché est composé d’une signature sshng en préfixe et de 5 ou 7 parties délimitées par des $.

Exemple de haché :

/home/noraj/.ssh/id_ed25519_crack:$sshng$6$16$3843af13aef53d4c0906f2998b082b3d$274$6f70656e7373682d6b65792d7631000000000a6165733235362d6374720000000662637279707400000018000000103843af13aef53d4c0906f2998b082b3d0000001800000001000000330000000b7373682d6564323535313900000020785c404a9750e39a4cdb788e787f13fdf0d62ca91ea76e3034272722980c222d00000090a99a138ebcd23a3fac923d88fb2b42833e4fc29d409efe86d543f8224cd11263b511e6cc858919bb58692a07664fb56905915bfe8d4a31db398827a65070f33dc127c3ca7d2ad9d184922e7a5e657de10166ee6adfc0b4cc736567adaeb8b1a160d008b1e5bd0a0188be18152d8eecec7bbd9b35d8f551e059bc57fa7642b7535a4d1aad8bb616576b9fb6b2e62bb7e5$24$130

La première partie après la signature, correspond à un numéro d’identification (arbitraire) associant type de clé + algorithme de chiffrement.

  • 0 : RSA/DSA + 3DES
  • 1 : RSA/DSA + AES-128
  • 2 : RSA/DSA/EC + Bcrypt PBKDF + AES-256-CBC
  • 3 : EC + AES-128
  • 4 : RSA/DSA + AES-192
  • 5 : RSA/DSA + AES-256
  • 6 : RSA/DSA/EC + Bcrypt PBKDF + AES-256-CTR

À noter que ssh2john génère quand même un haché avec $1$ pour une clé DSA + 3DES au lieu de $0$ car ssh2john génère un $0$ uniquement pour une "longueur de clé de 24" (pourquoi sachant que 16, 24 et 32 sont généralement des tailles de clé AES là où 3DES utilise plutôt 112 ou 168 bits ?).

Les parties suivantes correspondent à

  • la longueur du sel
  • le sel
  • la longueur des données
  • les données
  • optionnellement, le nombre de passes pour bcrypt_pbkdf
  • optionnellement, ciphertext_begin_offset pour bcrypt_pbkdf

Étude comparée

Prenons la clé suivante :

  • Type : RSA
  • Algorithme de chiffrement : AES 256 bits avec mode CBC
  • Format : PEM
  • Taille : 4096 bits
  • Mot de passe : test12345 (position 385742 dans rockyout.txt)
➜ ssh-keygen -N test12345 -t rsa -Z aes256-cbc -m PEM -f clé_rsa_aes256-cbc_PEM_demo -b 4096
Generating public/private rsa key pair.
Your identification has been saved in clé_rsa_aes256-cbc_PEM_demo
Your public key has been saved in clé_rsa_aes256-cbc_PEM_demo.pub
The key fingerprint is:
SHA256:zys3VyMJ6r9laWTGgo/QsrsCbaxV1tu3iRR4GlKW80E noraj@norarch
The key's randomart image is:
+---[RSA 4096]----+
|         oE      |
|        = .      |
|       + + .     |
|      +.+.=.     |
|   o oo.SB.o=.   |
|  . =  ++=o=+.o  |
|   =  ....+o== . |
|  . .  .o +=+    |
|     .o. +++     |
+----[SHA256]-----+

➜ ssh2john clé_rsa_aes256-cbc_PEM_demo
clé_rsa_aes256-cbc_PEM_demo:$sshng$1$16$31C54C9A4E9873B4DD6C58DC79B80A6C$2352$7189892c8e079116a1115d3496eb8e17e789e65ce7cbc1b6e0bc03e69034a6bf0f9bfb0d52be414942ae19691c43084ddcf2d3acda121bee07f64e980666c695a2dd713621129cb3490b85369c5233c0613f409c52cc6f9e82eae37dd7113fc3f17d63b6ac09f400bf55a1fd7739e483f02f3c1b6b2c8a13ea22250b0cfff832f7efd309501c5a7188f47d4654372e5ab0191a303765cbb592aaf97f07186edbfcb1b4f9eb0798a395448a64dec5d38eb9db616f2eed80aa94261c513e79b87a495d458a5da75efbc292e7c8c721a41b10f4e775e7d97e43716225bc6162a0665624acee70962d3e8b4eb3dc7d87db653345b1ef6da2beb5f35efc3724d9eac03a94065f6a96c44cd344529a3295e1efec7b218d179780e16f9c49bc8976ae1390e0eb9a885f0245cb9ad8aa4530a343afbdfafd7ae3e04ed5a122abfeedd1860e8ae82a6b3f728472b650bbfe3a7536296af24540c67f79bb13b8b497b1ffea30373d665202e3d80223c5e3f7d19b982a567c6b545392d559f5f42947e0aaec6b16cfb44a53f2cea559a87f0ea6b15bbf25fb3cc7139b66822bd21e91972d4db8d0e894b6472da4246f454d28fe3e05a898d6e1ef4141afaaa52e3b1691eba3be9ebc372262f7c2e2db0b44aa3dbefbc93d85bc69b70456447c06b029809e3c7c7be5511a83be0164d510ebc93bd0e75e971dea579eac03197a9e25f061c075b7934b3a3e8942c9a52d25d260365c9990c0dc7c79d64e868570b9033fdc322cffaa753b4526c7403a47667443942ebe99798a2eb7b228bf00526679856bd2bd0895dd1bb70064c0c8f06cb31d9c95e246cb781f9f701aafcefb6e454e2ffe1e68dbdb1202ca000e0d379be7f9f59ab87dd255f53a0e69e13215fb47bec6158e2cb5341f3ce6c4c51475634150467316b62f5fd9fbfef6ee0d891f595f38cd29724af3efa543ccf16cea9d0c1da5ce430e2112e811ae1a50c6de930722bb0425221fb6ea1f4404fccdda0824043111170c81c0c7311774a577a44fd3529dd848c6bf24920d74f23d4b9bad9a7a7baaab99efcfe35a46bf8234b546bb4fd122876de2a0b61d01b32e05f975f4bd1df0d56c6fc4e64c354f6329908e038899a97f00dd2f0d2aafe57195faa35f3c4943e59757e7262336fdd1972183927225e4097642c3be4152be32c149d20a07f3a07860c6adc0da3a9dd8454362adbeacf296abeb9e9a38ac923235f9c32b6f926e302ba9ed07744602875400e8d0dede22c3232f1a5d7a7c989f55dbba073eff87ac1ef00b2e1e686553c35bd9dc8aff32b1cdc25bf0b99bdeb561b1f39f3812ba33be6b94997edc89a44fe44e728290f9a8bbcdcac05426de57ec85c93e6825cc44aa92c8192485c3300f36fc08cee81babb9778a8653ecd01155018e6e3310629cf21f42b7aa9dbd6ba88bf80bd896c81ad0f426b162e70398dadc610d116decb0b71cc59468a2d14aa678f3e4d33e6fab715a195b2e61ef243a8ddbff50096948f8fc249aba75bde37a2abc9dff3d05a1e8a651f0efe9e24e22441499e7b0068109e3d16073b46dfa1eca94344feabc0959644ae56c330084cd8d05a089beced712da5c33bd3764129433ec1403a4e73d2b3a34173068bd6f5b84bf1e2e53eef9e89a2da217ada0bbd2d13fc4e1d9c24a16973cf0f5c79cc0eb3469716e7b584d8bcb3808aa05376c3a8334b53c5afca68d371688bf7607768c93abca882bd50feef2180c1fb5127ba4d1b9e7fcf0443b058115032b7bf896ad8b43136f124857d518a184e8c69e94b70982797db721fade1686574eb3f9f3d02de8bfd4ed56a43b4f16cc21adac5e517b5e7b15fecf921df9b294a374a4205638a41cc0aafbfe2324b4b862ffeb918bcf65228c736cdb5e9f1b5c440e39732e815ca7e589aa72f77c4b81d94d7e647f01e81022ad25e7d8fb0bcfcec2e01ce2a01c35ee8abfb56fd4c299184d0d0fba1ae08e1284d7f9de90c56616a0304705a9face5503338f0b44621be71643375b0d5b0fcce1f415238332e37dede2115faff839cfef64d2886ec030955d2db7300ac48896334d48ce9fc69285d3558ab2e7fbc12948445ba6ca11c320b3bf7abc937e12ec0f524144cf4a908fff0a6b496c9e23fd03da7f9b87ddd797cec432ac6ba8a48bda0b35559002c04557e7512c7592a2a27274000421ec9822544cb510d63a8d190af9f8ae973e0dcc319d4d34ddfdf6c2d6cc90329192722c78005aa1abe0f8729356d615abfb08daabae633e61eed4c3831869e7d4859ef3a67d1cfb24e8ddd14e5e3e75aaec1eb1c148776e7705533b3188f161fef4ece79a2af9ffa72f0e01c75a47cbe9c276c30eea386db17829c76cdf28fca3dec9a6b1c6bd3776905486b409cce506c6ef080bbc8601981d71a185dfc9fd332522c96b52fbc90d8a76e7dfca06b7d92248bf1aee8e72f115c8d6f519f6361b90cb3b52e8de05322121fb27735b9f1bae76e696aae0a905d41316b05959dd0d8ba5ae1dd5a1b867ea2182d113174f6963e7c83437af8c0da98167795c2725bca59d5cc726e01be0e0d43ae78fbc7cb9d370a26b80a1bf3136e9328752e750d241df3c9973d486e51ce054d0378b834973e22aa21887500a94d3769ac833d1d303236bed07958568b1a5fa8b1efd6eb6f942253feae44b36a1ca55643a2f0469f4997dd0a548bd4f8b24225209e5d7f14ffc2e4f6e995f8ae138015265b8ce28794ac756289900b14049a92c4ba3ceb098d3d50c113911a0f5266c6a9f9d1f55a1fefd6c187127a4f94bc5efdb9812492fbfad0241b4ba3c1f4cb78fd6212bf1718eaf22f509354a8bab7ac05d5e74b2bb640bc4d375b664cd834ffa84301b027126fade04c4dad8c6f9aca3a740ea5206572a9b68558ef8dc8b7f141a5ac2dd6fee7f4237cdfd481ebe6da1298f037ad05fe760ff82fdca30d3d738d8646b7bcfc1ba4db1f26451a29a882b67674495d521a69b7e3ca73d6170778ba168d073f8876ff04f63cee50a240b77b7902c46e489e75bf4c9169eb78906a2b23f3238c5f3f14a0f3a244aab6a65888e1c50c178e3d4a7cfbbb9dcc57dbc3432aacb2fe7e522ffbb11556086646619dd7b8f9ce1a6f3ad8ed37ab633047294a4905f117dad4f0e9d3f21d78dba9647854d252fb923c7065a25ce63cb8a337d38242eca70fff909f5546b80cc2318bde243781d44812918a7f1c1a3c5965e601d219bd62c484762900721f04ff031075068efbca0b02c3e624b2f956a63902f01e04f901bff7a099094c34c61b9abc570faa1962c07ae05ce7372a6f97eb5f46b1b

Voici le temps d’exécution pour retrouver la clé avec les différentes méthodes.

Note 📝 : time est la version intégrée de ZSH.

Pour le script de l’approche naïve séquentielle, le temps d’exécution est de 32 minutes :

➜ time ruby scripts/ssh-bf.rb /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo /usr/share/wordlists/passwords/rockyou.txt
...
ruby scripts/ssh-bf.rb /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo   1340,63s user 621,71s system 101% cpu 32:21,10 total

Pour le script de l’approche naïve multifil (avec sshPrivateKeyCrack), le temps d’exécution est d’environ 8 minutes (4 fois moins de temps en parallèle (sur 8 fils) qu’en monofil) :

➜ time python sshCrack.py -f /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo.jtr -w /usr/share/wordlists/passwords/rockyou.txt
Starting with 8 processes...
Working...

✓ test12345 ✓

CRACKED:
/tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo:test12345
python sshCrack.py -f /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo -w   2119,32s user 1008,62s system 656% cpu 7:56,77 total

Pour l’approche JtR (processeur), le temps d’exécution est d’environ 5 secondes :

➜ time john /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo.jtr -w=/usr/share/wordlists/passwords/rockyou.txt --format=ssh
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 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 8 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
test12345        (clé_rsa_aes256-cbc_PEM_demo)
1g 0:00:00:00 DONE (2024-02-14 16:04) 12.50g/s 4822Kp/s 4822Kc/s 4822KC/s teubesk..terminator3
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
john /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo.jtr  --format=ssh  18,71s user 0,07s system 371% cpu 5,050 total

Pour l’approche HC (unité graphique), le temps d’exécution est d’environ 3 secondes :

➜ time hashcat -m 22931 /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo.hc /usr/share/wordlists/passwords/rockyou.txt
...
hashcat -m 22931 /tmp/all-keys-ciphers/clé_rsa_aes256-cbc_PEM_demo.hc   0,50s user 1,15s system 47% cpu 3,476 total

Pour conclure, les approches optimisées de JtR et HC sont extrêmement plus rapides de plusieurs ordres de grandeur que les approches naïves faisant des appels à la commande ssh-keygen. Cependant, ces dernières sont tout de même intéressantes pour les types de clé non supportés par JtR, ssh2john et HC.

La taquinerie du chef

Pour enquiquiner au maximum les attaquants qui voudraient du mal à la confidentialité de votre clé privée SSH, il serait de bon ton d’avoir une clé de type ed25519 chiffrée avec l’algorithme chacha20-poly1305@openssh.com. Bonus ? Ce sont le type de clé et l’algorithme de chiffrement les plus robustes disponibles via ssh-keygen.

ssh-keygen -t ed25519 -Z chacha20-poly1305@openssh.com

Alternativement, cela peut être de stocker sa clé au format PKCS8 avec le type de clé ecdsa.

ssh-keygen -t ecdsa -Z chacha20-poly1305@openssh.com -m PKCS8

Si toutefois votre clé est de type rsa, chiffrée avec aes256-cbc et stockée au format pem, sachez qu’en tant qu’auditeur je vous remercie et vous souhaite bonne chance.

Plus sérieusement, voici quelques vraies recommandations :

  • chiffrez la clé privée,
  • positionnez des permissions système en 600 (-rw-------, vous seul devez pouvoir lire la clé),
  • le mot de passe de déchiffrement doit être :
    • robuste,
    • stocké dans un gestionnaire de mot de passe,
    • différent de celui de l’utilisateur.

Lexique

Traductions

🇫🇷 🇬🇧
multifil multithread
fil d’exécution thread
processus fils fork
haché hash

Acronymes

  • JtR : John the Ripper
  • HC : Hashcat
  • N/A : non applicable
  • N/T : non testé

À propos de l’auteur 📝

Article écrit par Alexandre ZANNI alias noraj, Ingénieur en Test d’Intrusion chez ACCEIS.