Hydra

Hydra throws username and password combinations at a live login service (SSH, FTP, an HTTP form, a database, SMB) and tells you which pair the service accepts. Reach for it once you have found a login and a sensible user and password list, to answer one narrow question: does this service take weak or reused credentials? Every try is a real, logged authentication attempt, so scope, rate, and lockout awareness are the whole game.

Mental model: hydra [-l user | -L users] [-p pass | -P passes] service://TARGET. Everything else tunes who, how fast, and when to stop.

New to online login testing? Core concepts
  • Login (-l / -L): one username, or a file of them
  • Password (-p / -P): one password, or a wordlist; -C reads a user:pass combo file instead
  • Service module: the protocol you attack (ssh, ftp, http-post-form, …), chosen by service://TARGET or a trailing module name
  • Tasks (-t): parallel connections; default is 16, lower is gentler and lockout-safer
  • Condition string: for web forms, an F= (fail) or S= (success) marker Hydra matches in the response to decide a hit
  • Placeholders ^USER^ / ^PASS^: where Hydra substitutes each candidate inside a form body
  • Stop on success (-f / -F): quit after the first valid pair for this host / for any host
  • Lockout: too many failures can disable an account; low -t and spraying avoid it
When to reach for Hydra (and when not)

Reach for it to prove a live service accepts weak or reused credentials: a deliberately weak CTF SSH/FTP/web login, a password-policy or lockout gap on an authorized assessment, or a defensive check that your own lockout and alerting actually fire.

Reach for something else when you already have password hashes (crack them offline with john or hashcat, faster, silent, no lockouts), when a web login uses CSRF tokens or a multi-step flow (Burp Intruder handles per-request tokens), when you are spraying Active Directory at scale (netexec / crackmapexec), or when you only need to test one or two known creds (just log in).

Install, update, verify#

sudo apt install -y hydra                  # Debian / Ubuntu / Kali
sudo dnf install -y hydra                  # Fedora / RHEL
sudo apt install --only-upgrade -y hydra   # update

hydra -h | less             # options, plus the compiled-in service list at the bottom
command -v hydra            # confirm it is on PATH
hydra -U http-post-form     # usage help for one module

Kali ships Hydra. The package includes the common protocol modules; build from source only if you need a module your distro left out. seclists (sudo apt install -y seclists) gives you sane user and password lists to point it at.

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-l / -LSingle user / user-list file-sSet a non-default port
-p / -PSingle password / password-list file-tParallel tasks per target (default 16)
-CColon combo file user:pass-TParallel tasks across all targets (-M)
-e nsrAlso try empty, login-as-pass, reversed-w / -WResponse timeout / delay between tries
-x MIN:MAX:CHARSGenerate passwords instead of a list-f / -FStop on first hit (this host / any host)
-uLoop users per password (spray order)-o / -bSave output / output format (json)
-MTarget-list file (many hosts)-V / -dShow each attempt / debug
-SConnect with SSL/TLS-RRestore an aborted session

Cheat sheet#

Every block starts simple and adds one flag at a time, so you can stop at the line that does the job.

# Build up an SSH test: stop at the line you need
hydra -l admin -P PASSLIST ssh://TARGET_IP                       # 1. all passwords for admin
hydra -l admin -P PASSLIST ssh://TARGET_IP -t 4                  # 2. + slow to 4 tasks
hydra -l admin -P PASSLIST ssh://TARGET_IP -t 4 -f               # 3. + stop at first valid pair
hydra -l admin -P PASSLIST ssh://TARGET_IP -t 4 -f -V            # 4. + print every attempt
hydra -l admin -P PASSLIST ssh://TARGET_IP -t 4 -f -o hits.txt   # 5. + save hits to a file

# Pick who and what to try
hydra -l admin -P PASSLIST ssh://TARGET_IP        # one user, a password list
hydra -L USERLIST -p 'Winter2026!' ssh://TARGET_IP  # many users, one password (spray)
hydra -L USERLIST -P PASSLIST ssh://TARGET_IP     # every user x every password
hydra -C combos.txt ssh://TARGET_IP               # a user:pass combo file
hydra -l admin -P PASSLIST -e nsr ssh://TARGET_IP # also try empty, login-as-pass, reversed
hydra -l admin -x 4:6:a1 ssh://TARGET_IP          # generate passwords, no list

# Common services (swap the module, keep the flags)
hydra -L USERLIST -P PASSLIST ftp://TARGET_IP
hydra -L USERLIST -P PASSLIST smb://TARGET_IP
hydra -l administrator -P PASSLIST rdp://TARGET_IP
hydra -l root -P PASSLIST mysql://TARGET_IP
hydra -l postgres -P PASSLIST postgres://TARGET_IP
hydra -l admin -P PASSLIST telnet://TARGET_IP
hydra -P PASSLIST vnc://TARGET_IP                 # VNC often has no username
hydra -P community.txt snmp://TARGET_IP           # brute SNMP community strings

# HTTP logins
hydra -L USERLIST -P PASSLIST TARGET_IP http-get /protected/    # HTTP basic auth
hydra -l admin -P PASSLIST TARGET_IP http-post-form \
  "/login.php:user=^USER^&pass=^PASS^:F=Login failed"          # form, fail marker
hydra -l admin -P PASSLIST TARGET_IP http-post-form \
  "/login:user=^USER^&pass=^PASS^:S=Logout"                    # form, success marker
hydra -l admin -P PASSLIST TARGET_IP http-post-form \
  "/login:user=^USER^&pass=^PASS^:C=/login:F=Invalid"          # grab a cookie first
hydra -l admin -P PASSLIST TARGET_IP https-post-form \
  "/login:user=^USER^&pass=^PASS^:F=Invalid"                   # over TLS

# Rate and lockout control
hydra -l admin -P PASSLIST ssh://TARGET_IP -t 4    # fewer tasks (default 16 is aggressive)
hydra -L USERLIST -p 'Winter2026!' ssh://TARGET_IP -t 1 -u  # one at a time, spray order
hydra -l admin -P PASSLIST ssh://TARGET_IP -W 3    # wait 3s between attempts per task
hydra -l admin -P PASSLIST ssh://TARGET_IP -w 10   # allow 10s per response (slow service)
hydra -l admin -P PASSLIST ssh://TARGET_IP -s 2222 # non-default port

# Output, sessions, many hosts
hydra -l admin -P PASSLIST ssh://TARGET_IP -o hits.txt          # write valid pairs to a file
hydra -l admin -P PASSLIST ssh://TARGET_IP -o hits.json -b json # ... as JSON
hydra -L USERLIST -P PASSLIST -M targets.txt ssh -T 8           # many targets, 8 tasks total
hydra -R                                                        # resume the last aborted run

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Test SSH for a weak password#

hydra -l admin -P /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt ssh://TARGET_IP -t 4 -f -o notes/ssh.txt

Tries each listed password for admin. -f stops at the first valid pair, -t 4 keeps it slow enough to dodge lockouts, -o saves the hit. A green login: admin password: ... line is a candidate; 0 valid passwords found is the normal miss.

Spray one password across many users (lockout-safe)#

hydra -L users.txt -p 'Winter2026!' ssh://TARGET_IP -t 1 -u

One likely password against every user. -u loops users per password so you never stack failures on one account, and -t 1 sends them one at a time. This is the gentlest thing to run against lockout policies.

Try empty, username-as-password, and reversed logins#

hydra -l admin -P passwords-small.txt -e nsr ssh://TARGET_IP -t 4

-e nsr adds three cheap extra guesses per user on top of the list: n empty password, s login used as the password, r the login reversed. Common wins on lab and default accounts.

Brute-force an FTP login#

hydra -L users.txt -P passwords-small.txt ftp://TARGET_IP -t 4 -V

-V prints every [ATTEMPT] line so you can confirm the module is actually talking to the service. Swap ftp for smb, telnet, pop3, imap, or smtp with the same flag layout.

Brute-force an HTTP POST login form#

hydra -l admin -P passwords-small.txt TARGET_IP http-post-form \
  "/login.php:username=^USER^&password=^PASS^:F=Login failed" -t 4

Three colon-separated fields: the path, the POST body with ^USER^ / ^PASS^ placeholders, and the failure marker. F=Login failed means “this attempt failed if that string is in the response.” Capture a real failed login in Burp first to get the exact field names.

Match on a success string instead of a failure string#

hydra -l admin -P pw.txt TARGET_IP http-post-form \
  "/login:user=^USER^&pass=^PASS^:S=Logout" -t 4

S= flips the logic: a hit is when the marker appears. Pick a string that only shows up after login (a Logout link, a dashboard heading). Use S or F, whichever is reliably present on exactly one outcome.

hydra -l admin -P pw.txt TARGET_IP http-post-form \
  "/login:user=^USER^&pass=^PASS^:C=/login:H=X-Requested-With\: XMLHttpRequest:F=Invalid"

C=/login fetches that page first to pick up a fresh session cookie, and H= adds a header (here the AJAX header some apps demand). Append more H= fields as needed. This does not solve per-request CSRF tokens.

Crack HTTP basic authentication#

hydra -L users.txt -P pw.txt TARGET_IP http-get /protected/ -t 4

The http-get module handles the browser 401 / WWW-Authenticate popup, not a form. Point it at the protected path. Use https-get for a TLS site.

Test a login over HTTPS#

hydra -l admin -P pw.txt TARGET_IP https-post-form \
  "/login:user=^USER^&pass=^PASS^:F=Invalid"

The https- module variants speak TLS, so you do not need -S. Reach for -S only when you are forcing SSL onto a plain module like ftp or smtp.

Target a service on a non-standard port#

hydra -l admin -P pw.txt ssh://TARGET_IP -s 2222

-s overrides the module’s default port. Confirm the real port with Nmap first; a wrong port shows up as instant connection errors, not misses.

Test SMB or RDP credentials#

hydra -L users.txt -P pw.txt smb://TARGET_IP -t 1
hydra -l administrator -P pw.txt rdp://TARGET_IP -t 1

Keep -t 1 on Windows auth: SMB and RDP lock accounts fast and generate loud failed-logon events. For broad AD spraying, netexec is the better tool.

Test a database login (MySQL, Postgres)#

hydra -l root -P pw.txt mysql://TARGET_IP
hydra -l postgres -P pw.txt postgres://TARGET_IP

Database modules brute the DB account directly. root (MySQL) and postgres (Postgres) are the usual default supers. mssql works the same way for SQL Server.

Use a colon-separated user:pass combo list#

hydra -C combos.txt ssh://TARGET_IP -t 4

-C reads one user:pass per line and tries them as fixed pairs, instead of the full -L x -P product. Use it for leaked-credential lists where the pairing matters.

Generate passwords on the fly instead of a wordlist#

hydra -l admin -x 4:6:aA1 ssh://TARGET_IP -t 4

-x MIN:MAX:CHARSET builds passwords itself (a lowercase, A upper, 1 digits, or literal chars). Here: length 4 to 6, mixed alnum. It replaces -p / -P and blows up fast, so keep the length and charset tight.

Save hits and resume an interrupted run#

hydra -l admin -P pw.txt ssh://TARGET_IP -t 4 -o "notes/hydra-$(date +%Y%m%d-%H%M).txt"
hydra -R   # after a crash or Ctrl-C, resume from ./hydra.restore

-o keeps timestamped evidence. If a run dies, Hydra leaves a hydra.restore file in the working directory; hydra -R picks it back up (run it from the same directory).

Reading the output#

  • [22][ssh] host: TARGET_IP login: admin password: hunter2 - a candidate hit (port, module, then the pair). Always confirm with a real manual login before you trust or report it.
  • 0 valid passwords found - nothing on the list worked at this rate. A normal, common result, not an error.
  • [ATTEMPT] ... - N of M (with -V) - live progress; the N of M count tells you how far a run has to go.
  • [ERROR] ... could not connect repeated - wrong port or service, the target rate-limited you, or the service is down. Not the same as a clean miss.
  • Every attempt reads as a hit, or none ever do - your F= / S= marker is wrong. Fix the marker, do not trust the run.

Troubleshooting#

ProblemCauseFix
Every attempt “valid”Marker matches on all responsesPick an F= / S= string unique to one outcome
All attempts fail instantlyWrong path or field namesRecheck the POST body and URL against a captured request
could not connectWrong port or serviceConfirm with Nmap; set -s PORT
Account lockouts-t too highDrop to -t 1 / -t 2, add -u, spray instead
Very slowGiant list or slow serviceTrim the list; raise -t cautiously; -w for timeouts
Could not resolve / no routeBad host or out of scopeVerify IP, route, and firewall/scope
Module not foundNot compiled into the packageUse a build that includes it; hydra -h lists what you have

Gotchas#

  • The default -t 16 is aggressive. Against SSH, SMB, RDP, or any lockout-protected login, drop it to -t 4 or lower before you disable an account and blow your scope.
  • F= / S= is a plain substring match on the response body. If your marker also appears on the login page itself (the word “Login”), detection is meaningless. Choose a string present on exactly one outcome.
  • -f is per-host and finishes in-flight tasks. You may see a couple of extra attempts after the hit, and across multiple targets (-M) you need -F to stop everywhere.
  • CSRF tokens and one-time nonces defeat a static form string. Hydra cannot parse a fresh token per request; C= only handles cookies. Use Burp Intruder or a script when the body carries a per-request token.
  • A “hit” is only the condition matching, not a proven session. A wrong marker gives false positives and false negatives alike, so a manual login is mandatory before you believe it.
  • Attempt counts multiply. -L users x -P pass x -e s grows quickly; a 100-user, 10k-password run is a million real, logged authentication events.

Pairs with#

nmap finds which login services are open and on what ports before you point Hydra at one. When you already hold hashes, john and hashcat crack them offline: faster, silent, and no lockouts, so that is the right call whenever hashes exist. seclists supplies the curated user and password lists, and Burp or the browser dev tools capture a real login request so your http-post-form fields and markers are exact. For spraying Active Directory or complex token-driven web logins, netexec and Burp Intruder go where Hydra cannot; medusa and ncrack are close alternatives with different module coverage.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop