1. The Persistent Danger of SSH Login Attacks
Within 52 seconds of spinning up an exposed Linux server on the public internet, automated bots begin hammering port 22. That's not speculation — security researchers at AlientVault and various honeypot operators have documented this window repeatedly. SSH brute force is not an exotic attack reserved for nation-state actors. It's industrial-scale background noise that claims thousands of servers daily.
The threat is structural. SSH (Secure Shell) is the administrative backbone of virtually every Linux and Unix-based server on the planet. Its ubiquity makes port 22 the single most probed attack surface on the internet. Attackers don't need to be skilled — they need a credential list and a script.
What makes this particularly dangerous is the asymmetry: defenders must block every attempt; attackers only need one success. And when they succeed, the compromise isn't just a hijacked server — it's a pivot point into internal networks, a ransomware deployment platform, or a cryptomining node quietly draining your resources.
2. Behind the Scenes: Coordinated SSH Brute Force Techniques
Modern SSH brute force is not a single attacker manually guessing passwords. It's a coordinated, distributed operation. Here's what's actually happening behind the connection attempts:
Credential Stuffing vs. Pure Brute Force: Most campaigns today use credential stuffing — feeding known username/password pairs leaked from previous breaches — rather than pure dictionary attacks. According to Have I Been Pwned, over 12 billion breached credential records are publicly indexed. Attackers don't guess; they recycle.
Distributed Botnet Architecture: To bypass IP-based rate limiting and geoblocking, attacks are distributed across thousands of compromised hosts. Each node fires only a handful of attempts, staying under detection thresholds. Your firewall logs might show 50 different IPs each making 3 attempts — individually innocent-looking, collectively systematic.
Username Enumeration: Before brute-forcing passwords, sophisticated campaigns probe for valid usernames. Timing differences in SSH's response to valid vs. invalid usernames were historically exploitable (CVE-2018-15473). Many unpatched servers remain vulnerable to this enumeration phase.
Common tools used in these campaigns:
| Tool | Primary Use | Speed | Evasion Capability |
|---|---|---|---|
| Hydra | Parallel brute force, multi-protocol | Very High | Low (single-source by default) |
| Medusa | Modular credential testing | High | Low |
| Patator | Smart brute force with fine-grained control | Medium | Medium |
| Custom Botnets | Distributed campaigns across thousands of IPs | Variable | High |
The critical insight: rate limiting alone fails against distributed attacks because the "rate" is spread across thousands of source IPs. Defenses must operate at multiple layers simultaneously.
3. Spotting the Signs: Detection and Audit for SSH Compromise
Detection splits into two phases: catching an active attack, and auditing for a compromise that already happened.
Active attack indicators: Run this on any Linux server to see live failed SSH attempts:
# View recent failed SSH authentication attempts
grep "Failed password" /var/log/auth.log | tail -50
# Count unique attacking IPs
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head -20
# For systemd-based systems (CentOS, RHEL, newer Debian)
journalctl -u sshd --since "1 hour ago" | grep "Failed"
If you're seeing more than a few hundred failed attempts per hour from rotating IPs, you're under active distributed brute force. This is normal background noise on any publicly exposed server — but crossing the threshold of successful authentication is the actual breach event.
Post-compromise forensic indicators:
- Unfamiliar entries in
~/.ssh/authorized_keys - New user accounts in
/etc/passwdwith UID 0 (root-level) - Unexpected cron jobs:
crontab -landcat /etc/cron.d/* - Outbound connections to unknown IPs:
netstat -tulnporss -tulnp - Modified SSH daemon binary: compare hash against a known-good baseline
According to CISA Advisory AA21-048A, attackers who successfully brute-force SSH access typically establish persistence within minutes — often by injecting their own public key into authorized_keys and creating a backdoor user before you even notice the breach.
4. Bulletproofing Your Server: SSH Brute Force Mitigation & Recovery
Mitigation is not a single action. It's a hardening stack. Apply these in sequence — each layer compensates for the gaps in the one before it.
Step 1: Disable password authentication entirely. This is the single highest-leverage action. If SSH only accepts key-based authentication, brute force against passwords becomes irrelevant.
# Edit SSH daemon configuration
sudo nano /etc/ssh/sshd_config
# Set these values:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Restart SSH (do NOT close your current session first)
sudo systemctl restart sshd
Step 2: Install and configure Fail2Ban. Fail2Ban reads your auth logs and dynamically bans IPs after a configurable number of failures. It's not bulletproof against distributed attacks, but it eliminates opportunistic low-effort campaigns:
# Install Fail2Ban
sudo apt install fail2ban -y
# Create local jail config
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit /etc/fail2ban/jail.local — set for SSH:
[sshd]
enabled = true
port = ssh
maxretry = 3
findtime = 300
bantime = 3600
sudo systemctl enable fail2ban && sudo systemctl start fail2ban
Step 3: Move SSH off port 22. This is security through obscurity — not a defense in itself, but it eliminates 90%+ of automated scanners that only probe default ports. Change the port in /etc/ssh/sshd_config and update your firewall rules accordingly.
Step 4: Enforce MFA on SSH. Using libpam-google-authenticator or libpam-duo, you can require a time-based OTP in addition to your private key. Even a stolen key becomes useless without the second factor. NIST SP 800-63B explicitly recommends phishing-resistant MFA for any privileged remote access.
Step 5: Restrict access with allowlists. If your administrative IPs are predictable, use AllowUsers and firewall rules (UFW or iptables) to whitelist only known CIDR ranges. Combine with a VPN requirement for administrative access where possible.
Recovery after confirmed compromise: Assume the worst. Revoke all existing keys, rotate all credentials, audit authorized_keys for every user, review cron jobs, check for rootkits with rkhunter, and snapshot the disk for forensics before wiping. Do not simply change the password on a compromised server and call it done — persistence mechanisms are usually already in place.
The honest trade-off: Key-based authentication with MFA is not frictionless. Teams with rotating members, CI/CD pipelines, and multiple server fleets will face real operational complexity managing key distribution and rotation at scale. Solutions like HashiCorp Vault SSH secrets engine or certificate-based SSH (using short-lived signed certificates instead of static keys) address this, but they introduce their own infrastructure overhead. The security is sound — the operational discipline required to maintain it is where most organizations actually fail. Hardening SSH is the easy part. Keeping it hardened six months later, after three new engineers joined and two pipelines were added, is the real challenge.
Sources:
- Have I Been Pwned
- CISA Advisory AA21-048A — Exploitation of Pulse Connect Secure Vulnerabilities
- NIST SP 800-63B — Digital Identity Guidelines



