Lab 01
SSH attack logs
Hunting SSH attacks in real server logs with SQL.
I took about four hours of real SSH logs from a server under attack, loaded them into a database, and used SQL to answer the questions a security analyst would ask: who attacked, what they tried, how fast, whether anyone got in, and when it peaked.
The data
2,000 lines of real OpenSSH server logs from the Loghub collection, a public log dataset published for research. It covers one server on 10 December, from 06:55 to 11:04.
Findings
One IP did over half the damage.
There were 519 failed password attempts from 23 different IPs. 286 of them came from a single address, 183.62.140.253.
Root was the main target, inside a wider dictionary attack.
Attackers tried 62 different usernames (admin, support, oracle, test, guest and so on), but 370 of the 519 failures (about 71%) were guesses at
root, the account with full control of the server. Most of the other names don't exist on this server, so those guesses could never work.rootdoes exist, which is what made it the real risk.It was brute force: automated, and fast.
Grouping failures per IP per minute showed 183.62.140.253 hitting up to 30 failed passwords a minute. All 286 of its attempts landed in about ten minutes (10:54 to 11:04), roughly one guess every two seconds. No person types that fast, so this was a tool.
Nobody broke in.
There was exactly one successful login: user
fztufrom 119.137.62.142 at 09:32. That IP had no failed attempts at all. It got the password right first time, stayed about 13 minutes, and disconnected normally. That looks like a legitimate user, not a cracked account, and none of the attacking IPs ever got in.Caveat: a stolen password (as opposed to a guessed one) would look exactly the same, so in a real investigation I'd confirm with the user or check the IP against known locations before closing it.
The attack was getting worse when the log ended.
The attacks came in waves from different IPs through the morning. The 11:00 hour looks similar to 10:00, but the log stops at 11:04, so those 146 failures happened in under five minutes. The attack was at full speed when the record ends.
Failed password attempts, by hour The log ends at 11:04.
SELECT strftime('%H', ts) AS hour, COUNT(*) AS failures FROM ssh_logs WHERE event_type = 'failed_password' GROUP BY hour ORDER BY hour;
Data note. The server sometimes collapses repeated lines into one ("message repeated 5 times"). Counting rows gives 519 failures; adding up the repeats gives 527. Small here, but on a busy server, counting rows instead of events would undercount an attack.
What would have stopped this
- Disable root login over SSH (
PermitRootLogin no). That alone makes 71% of this attack pointless; admins log in as themselves and usesudo. - Block repeat offenders automatically with fail2ban. A rule like "5 failures in 10 minutes = ban" would have stopped 183.62.140.253 after a handful of guesses instead of 286.
- Use SSH keys instead of passwords (
PasswordAuthentication no). With no password to guess, a dictionary attack has nothing to work with. - Alert on the pattern, not just the total: the per-minute query is effectively a detection rule.
What I learned
LIMITon raw rows shows the first rows, not the top ones. Count first, then sort.- Text values in SQL need exact spelling and single quotes. When a query returns nothing, check the real values with
SELECT DISTINCT. - Grouping by two columns (IP and minute) turns "how much" into "how fast".
- Not every alert is an attack, but you verify before you dismiss it.