Tag Archives: Ssh

Bruteforcing SSH Known_Hosts Files


(Source: www.motion-twin.com)

OpenSSH is a common tool for most of network and system administrators. It is used daily to open remote sessions on hosts to perform administrative tasks. But, it is also used to automate tasks between trusted hosts. Based on public/private key pairs, hosts can exchange data or execute commands via a safe (encrypted) pipe. When you ssh to a remote server, your ssh client records the hostname, IP address and public key of the remote server in a flat file called “known_hosts“. The next time you start a ssh session, the ssh client compares the server information with the one saved in the “known_hosts” file. If they differ, an error message is displayed. The primary goal of this mechanism is to block MITM (“Man-In-The-Middle“) attacks.

But, this file (stored by default in your “$HOME/.ssh” directory) introduces security risks. If an attacker has access to your home directory, he will have access to the file which may contains hundreds of hosts on which you also have an access. Did you ever eared about “Island Hopping” attack? Wikipedia defines this attack as following:

In computer security, for example in intrusion detection and penetration testing, island hopping is the act of entering a secured system through a weak link and then “hopping” around on the computer nodes within the internal systems. In this field, island hopping is also known as pivoting.

A potential worm could take advantage of the information stored in the file to spread across multiple hosts. OpenSSH introduced a countermeasure against this attack since the version 4.0. The ssh client is able to store the host information in a hash format. The old format was:

  host.rootshell.be, ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0ei6KvTUHnmCjdsEwpCCaOHZWvjS \
  jytm/5/Vv1Dc6ToaxTnqJ7ocBb7NI/HUQEc23eUYjFrZQDS0JRml3RnsG0UzvtIfAPDP1x7h6HHy4ixjAP7slXgqj3c \
  fOV5ThNjYI0mEbIh1ezGWovwoy0IxRK9Lq29CacqQH8407b1jEj/zfOzUi3FgRlsKZTsc3UIoWSY0KPSSPlcSTInviG \
  oNi+9gC8eqXHURsvOWyQMH5K5isvc/Wp1DiMxXSQ+uchBl6AoqSj6FTkRAQ9oAe8p1GekxuLh2PJ+dMDIuhGeZ60fIh \

With the version 4.0, hosts are stored in this new format:

  |1|U8gOHG/S5rH9uRH3cXgdUNF13F4=|cNimv6148Swl6QcwqBOjgRnHnKs= ssh-rsa AAAAB3NzaC1yc2EAAAABIw \
  AAAQEAvAtd04lhxzzqW57464mhkubDixZpy+qxvXBVodNmbM8culkfYtmq0Ynd+1G1s3hcBSEa8XHhNdcxTx51MbIjO \
  dCbFyx6rbvTIU/5T2z0/TMjeQyL3SZttbYWM2U0agKp/86FdaQF6V87loNcDq/26JLBSaZgViZS4gKZbflZCdD6aB2s \
  2sqEV4k7zU2OMHPy7W6ghNQzEu+Ep/44w4RCdI5OYFfids9B0JSUefR9eiumjRwyI0dCPyq9jrQZy47AI7oiQJqSjvu \

As you can see, the hostname is not readable anymore. To achieve this result, a new configuration directive has been added in version 4.0 and above: “HashKnownHosts [Yes|No]“. Note that this feature is not enabled by default. Some Linux (or other UNIX flavors) enable it by default. Check your configuration. If you switch the hashing feature on, do not forget to hash your existing known_hosts file:

  $ ssh-keygen -H -f $HOME/.ssh/known_hosts

Hashing ssh keys is definitively the right way to go but introduce problems. First, the good guys cannot easily manage their SSH hosts! How to perform a cleanup? (My “known_hosts” file has 239 entries!). In case of security incident management or forensics investigations, it can be useful to know the list of hosts where the user connected. It’s also an issue for pentesters. If you have access to a file containing hashed SSH hosts, it can be interesting to discover the hostnames or IP addresses and use the server to “jump” to another target. Remember: people are weak and re-use the same passwords on multiple servers.

By looking into the OpenSSH client source code (more precisely in “hostfile.c“), I found how are hashed the hostnames. Here is an example:


“|1|” is the HASH_MAGIC. The first part between the separators “|” is the salt encoded in Base64. When a new host is added, the salt is generated randomly. The second one is the hostname HMAC (“Hash-based Message Authentication Code“) generated via SHA1 using the decoded salt and then encoded in Base64. Once the hashing performed, it’s not possible to decode it. Like UNIX passwords, the only way to find back a hostname is to apply the same hash function and compare the results.

I wrote a Perl script to bruteforce the “known_hosts” file. It generates hostnames or IP addresses, hash them and compare the results with the information stored in the SSH file. The script syntax is:

  $ ./known_hosts_bruteforcer.pl -h
  Usage: known_hosts_bruteforcer.pl [options]
   -d <domain>   Specify a domain name to append to hostnames (default: none)
   -f <file>     Specify the known_hosts file to bruteforce (default: /.ssh/known_hosts)
   -i            Bruteforce IP addresses (default: hostnames)
   -l <integer>  Specify the hostname maximum length (default: 8 )
   -s <string>   Specify an initial IP address or password (default: none)
   -v            Verbose output
   -h            Print this help, then exit

Without arguments, the script will bruteforce your $HOME/.ssh/known_hosts by generating hostnames with a maximum length of 8 characters. If a match is found, the hostname is displayed with the corresponding line in the file. If your hosts are FQDN, a domain can be specify using the flag “-d“. It will be automatically appended to all generated hostnames. By using the “-i” flag, the script generates IP addresses instead of hostnames. To spread the log across multiple computers or if you know the first letters of the used hostnames or the first bytes of the IP addresses, you can specify an initial value with the “-s” flag.

Examples: If your server names are based on the template “srvxxx” and belongs to the rootshell.be domain, use the following syntax:

  $ ./known_hosts_bruteforcer.pl -d rootshell.be -s srv000

If your DMZ uses IP addresses in the range, use the following syntax:

  $ ./known_hosts_bruteforcer.pl -i -s

When hosts are found, there are displayed as below:

  $ ./known_hosts_bruteforcer.pl -i -s
  *** Found host: (line 31) ***
  *** Found host: (line 165) ***
  *** Found host: (line 69) ***
  *** Found host: (line 28) ***
  *** Found host: (line 56) ***
  *** Found host: (line 51) ***

My first idea was to bruteforce using a dictionary. Unfortunately, hostnames are sometimes based on templates like “svr000” or “dmzsrv-000” which make the dictionary unreliable. And about the performance? I’m not a developer and my code could for sure be optimized. The performance is directly related to the size of your “known_hosts” file. Be patient! The script is available here. Comments are always welcome.

Usual disclaimer: this code is provided “as is” without any warranty or support. It is provided for educational or personal use only. I’ll not be held responsible for any illegal activity performed with this code.

OpenSSH New Feature: “Netcat mode”

The new version (5.4) of OpenSSH has been released early this morning. OpenSSH is THE free implementation of the SSH protocol available on common devices and operating systems.

The primary goal of OpenSSH is to allow remote access to hosts for management purpose. But many other features make OpenSSH a real Swiss-army knife for all network and system administrators:

  • Multiple encryption methods,
  • Files transfer using SCP or SFTP,
  • Port forwarding,
  • SOCKS proxy server,
  • VPN (tunneling),
  • X11 forwarding,
  • Multiple authentication methods and single sign-on (via the agent-forwarding).

Some OpenSSH release just fix bugs or introduces light changes. But today, the release 5.4 comes with a new exciting feature looking very interesting to me: the “netcat mode”. Quoted from the release notes:

Added a ‘netcat mode’ to ssh(1): “ssh -W host:port …” This connects stdio on the client to a single port forward on the server. This allows, for example, using ssh as a ProxyCommand to route connections via intermediate servers.

Netcat can also be compared to a Swiss-army knife at TCP/IP level. It is a multi-purpose tool which allows to read and write data across network connections. Originally, it was a tool available on UNIX flavors but Netcat (or “nc”) is  also available for Windows [Note: the Windows version is often detected as suspicious by common anti-virus softwares]. Netcat lacks of … encryption (if your original data are in clear text) but now, with the brand new OpenSSH, we can pipe data safely!

Example #1: I’m connected via a wild WiFi network or your Internet Service Prodiver does not allow  outgoing traffic via the SMTP port (TCP/25): [Note: In this example and all others below, we assume that both server and client are running version 5.4]

$ ssh -p 443 -W gmail-smtp-in.l.google.com:25 xavier@server.acme.org
xavier@server.acme.org's password:
220 mx.google.com ESMTP a10si9247793bky.86
helo rootshell.be
250 mx.google.com at your service
221 2.0.0 closing connection a10si9247793bky.86

Example #2: Let’s open a telnet session to an old switch without SSH support (this time, I’m authenticated using a key pair):

$ ssh -p 443 -W xavier@server.acme.org
Connected to
Escape character is '^]'.

User Name : ^]
telnet> close

But you will ask: “What’s the difference with the “-L” option available for years?”. Indeed, for a while, OpenSSH allows port forwarding as in this xample:

$ ssh -p 443 -L 2300: xavier@server.acme.org

The SSH client will bind to port 2300 on the loopback and forward any incoming packet to the remote IP But they are two constraints with the classic port forwarding method:

  • Once the SSH session has been opened and the port successfully binded, you need to use a native client to connect on this port. And such command can be disabled by a local security policy or not installed at all! A common usage is to create a tunnel for POP3 traffic (TCP/110) but a POP3 client will be required and reconfigured to use the localhost/port defined at the command line level.
  • SSH must bind a port to the localhost. To bind a port below 1024, you must have root privileges! This can be a problem if the client cannot change the default port. And even if the chosen port is above 1024, traffic can be blocked by local firewall rules.

As OpenSSH connects STDIO to the remote host/port, you are free to type your commands or data, copy/paste them or pipe them to the SSH process.

Example #3: You need to transfer a binary file to a remote server which runs Netcat in listening mode but you don’t have Netcat available on the current computer:

cat binary.jpg | ssh -W netcathost:4000 xavier@server.acme.org

This new “netcat mode” is IMHO a killer feature in this new release of OpenSSH but it should not shadow other changes such as:

  • SSH Version 1 support is now disabled by default! After 10-years!
  • A ‘read-only’ mode is available to sftp-server.

For a complete review of changes and bug fixes, have a look at the official announce.