Ten thousand failed logins matter less than one success
The scenario is fictional (RFC 5737 IPs). Detection only. A public SSH server gets thousands of failed logins a day. Alerting on all of them burns out analysts. Hidden in that noise is one line you must never miss:
The scenario is fictional (RFC 5737 IPs). Detection only.
A public SSH server gets thousands of failed logins a day. Alerting on all of them burns out analysts. Hidden in that noise is one line you must never miss:
Accepted password for root from 203.0.113.77
What you'll learn
- How brute force and spraying differ, and what each needs
- Picking only IPs that failed then succeeded
- Detecting spraying by distinct target accounts
- The Windows equivalents
The story at a glance
02:18 failed root logins (btmp), 02:20 root success (wtmp), 02:22 payload fetched.
Three attack shapes
| Type | Shape | Detection axis |
|---|---|---|
| Brute force | many passwords, one account, fast | failures per source IP |
| Spraying | few passwords, many accounts, slow | distinct target accounts per IP |
| Credential stuffing | leaked pairs reused | known leaks, success from unknown devices |
Failure followed by success
LOG=/var/log/auth.log; THRESHOLD=20
for ip in $(grep 'Failed password' $LOG | awk '{for(i=1;i<=NF;i++)if($i=="from")print $(i+1)}' | sort -u); do
fails=$(grep -c "Failed password.*from ${ip} " "$LOG")
succ=$(grep -c "Accepted .*from ${ip} " "$LOG")
[ "$fails" -ge "$THRESHOLD" ] && [ "$succ" -ge 1 ] && echo "[SUSPECT] ip=${ip} fails=${fails} success=${succ}"
done
On a synthetic log it printed only [SUSPECT] ip=203.0.113.77 fails=30 success=1; an IP with 50 failures and no success was ignored.
Spraying
awk '/Failed password/ {u="";ip=""; for(i=1;i<=NF;i++){if($i=="for")u=$(i+1); if($i=="invalid")u=$(i+2); if($i=="from")ip=$(i+1)} if(ip!=""&&u!="")print ip,u}' /var/log/auth.log \
| sort -u | awk '{c[$1]++} END{for(ip in c)print c[ip],ip}' | sort -rn | head
198.51.100.9, with 12 accounts failed once each, topped the list. Rule of thumb from the book: 10+ distinct accounts from one IP suggests spraying.
What to alert on
(1) brute force with a success, (2) spraying, (3) failures from internal segments.
Auth method hint
A pile of Failed password means that host allows password auth at all. Public-key-only hosts stay nearly silent.
Windows mapping
Failed password = 4625, Accepted = 4624, Invalid user = SubStatus 0xC0000064, wrong password = 0xC000006A (book; not tested here).
Next: Golden and Silver tickets, spotted by logs that aren't there.
Adapted from chapter 22 of my Japanese book on server defense.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.