known_hosts hash cracking with hashcat
The OpenSSH client uses a file called known_hosts to track the fingerprint for previously used ssh servers. This can help the SSH client detect when a man in the middle attack is taking place. If an attacker was to try this attack, the user’s client would show a serious warning and refuse to connect. This is done to stop the user from connecting to an attacker’s system. In my experience, Ubuntu and Debian servers seem to have the “HashKnownHosts” setting on by default. This setting does not seem to be the defacto default setting, so it may be disabled by the OS or vendor.
in 2005 a team from MIT wrote a paper about the potential threat of an SSH-based worm. Their theoretical SSH worm would replicate by locating other SSH servers through the known_hosts file. To defend against this threat the OpenSSH team added a new option “HashKnownHosts” in version 4.0. This effectively features hashes the IP and domains that are stored in the known_hosts file and effectively killed off the threat they had described. However, they did not foresee the GPU password cracking abilities that we have today.
Bruce Schneier also had a blog post about the issue and their paper.
Example known_hosts hashing configuration enabled in /etc/ssh/ssh_config:
The known_hosts file can help Red Teams
During an engagement, I ran into a server and network that had detailed network monitoring. I had a shell and I had located a users SSH key. Normally you can read the .bash_history for a given user and see which hosts the user has used ssh to log into, but in this case, it seemed the .bash_history file had rolled over. Since there weren’t very may ssh commands I decided to try to find a way to crack the hashes in the known_hosts file. Having a list of IP addresses with other SSH servers would be helpful to have, as I wouldn’t need to do a noisy network scan to locate them. Also, if an attacker only logs into servers that the admin has already used, there is a good chance this won’t raise an alarm and you can stay undetected.
known_hosts file format
A non hashed known_hosts file example:
a hashed known_hosts example:
This next example known_hosts file with a single IP address. These commands will explain a little bit more on how these hashes are created (these commands were taken from this post):
First, let’s see what is in the known_hosts file:
The first base64 encoded string is used as the salt:
The second string is the IP address of the server.
My first approach at cracking the known_hosts hashes
I wanted to start off by clarifying that I am not the first to do this attack. I’ve seen at a small handful of posts that describe the technique. This attack also only seems to work if the user logged into the SSH server with the IP address vs. using the domain name.
My first approach with this was to create a cracking dictionary with every possible internal IP address in it ex: 10.0.0.0/8. The file was big, but not unmanageable. the 10.0.0.0/8 private block is around 16 million IP addresses. However, the entire internet would be around 4.3 billion. My first approach worked fairly well, but I found a better way after doing some googling.
The optimized approach, mask attacks
I found the following post on the hashcat forums with information about using a mask attack for brute-forcing hashes for all IPv4 addresses. Hashcat mask attacks are used often as they can be better tuned for cracking vs. the brute force method. In our case, it is easier to use a mask attack as you don’t need to generate a 4.3 billion IP address dictionary file. The IPv4 hcmask file included in this repository was originally downloaded from this pastebin post, but I’ve included it here to save you time.