Skip to main content
Back to all investigations
SOC-019·Linux·Severity: MediumClassification: True PositiveEnvironment: Personal Home Lab

Triage of Distributed SSH Authentication Failures

Investigation of over 12,000 failed SSH authentication attempts on an internet-facing Linux bastion host, identifying dictionary brute-force patterns and misconfigured firewall rules.

EnvironmentUbuntu 24.04 LTS (Cloud VPS Bastion)
Toolsrsyslog, grep/awk/sed, Fail2ban, GeoIP CLI, Wireshark
MITRE ATT&CKT1110.001, T1046
Published2026-09-04
Safety & Simulation Disclosure

Environment: Personal Home Lab — This investigation was performed in an isolated personal research and bastion environment. Indicators, systems, accounts, and other data shown here are simulated or sanitized unless otherwise stated.

Executive Summary

An automated monitoring threshold triggered on host srv-bastion-01 due to 12,480 failed SSH login attempts within a 35-minute window. Log analysis of /var/log/auth.log confirmed coordinated credential stuffing targeting root and generic service usernames (admin, postgres, deploy). Zero successful logins occurred. The Fail2ban daemon had silently stopped due to an iptables lock conflict, permitting traffic to flood the SSH daemon.

Scenario & Trigger

Public IP 203.0.113.88 hosted an experimental SSH bastion. The SOC was alerted when external auth failure events exceeded 500 per 5-minute bucket in the centralized syslog stream.

Excessive SSH Authentication Failures (>500 in 5m)2026-09-04 03:15:22 UTC
Source: Central Log Server (Alert: LINUX_AUTH_FAILURE_SPIKE)
HOST=srv-bastion-01 FACILITY=auth PRIV=info COUNT=12480 MESSAGE="Failed password for invalid user from 198.51.100.42 port 48210 ssh2"

Initial Evidence

  • Target Server: srv-bastion-01 (Ubuntu 24.04 LTS, IP: 203.0.113.88)
  • Time Window: 2026-09-04 02:45:00 UTC to 03:20:00 UTC
  • Total Failed Attempts: 12,480
  • Primary Source IPs: 198.51.100.42 (7,890 attempts), 198.51.100.89 (4,590 attempts)
  • Top Usernames Target: root (45%), admin (28%), ubuntu (12%), postgres (8%), guest (7%)

Investigation Methodology & Narrative

Step 1: Queried /var/log/auth.log using awk to aggregate failed attempts by source IP address and target account names.

Step 2: Inspected systemd service status for Fail2ban (systemctl status fail2ban). Observed the service was in a failed state due to an iptables xtables.lock resource contention following an uncoordinated apt-upgrade.

Step 3: Verified whether any authentication succeeded during the attack window using: grep "Accepted publickey\|Accepted password" /var/log/auth.log. Zero successful logins occurred.

Step 4: Inspected /etc/ssh/sshd_config to verify SSH authentication policies. Verified PermitRootLogin was explicitly set to "no" and PasswordAuthentication was set to "no" (publickey only).

Step 5: Restarted fail2ban and implemented immediate ufw drop rules for the offending IP subnets.

Incident Timeline

Time (UTC)EventTelemetry SourceAnalyst Note
02:45:12 UTCFirst burst of invalid user login attempts from 198.51.100.42auth.logAutomated scanner cycling through standard wordlists
02:50:00 UTCSecondary source 198.51.100.89 joined the attack streamauth.logCoordinated spray from adjacent IP range
03:15:22 UTCSIEM threshold alert generated for analyst triageAlerting EngineAnalyst initiated active log review and containment
03:22:10 UTCAnalyst verified no successful sessions were initiatedauth.logAll attempts were rejected with "Connection closed by authenticating user"
03:28:45 UTCufw block rules applied and fail2ban service restoredufw / systemdImmediate drop of incoming packets from attacking CIDR

Indicators of Compromise (IOCs)

TypeObserved ValueContextReputation
IPv4198.51.100.42Source IP for primary dictionary sprayMalicious
IPv4198.51.100.89Secondary source IP for brute-force attemptsMalicious

Log Analysis & Telemetry Dissection

Auth Log Extract (/var/log/auth.log)srv-bastion-01 /var/log/auth.log
Sep  4 02:45:12 srv-bastion-01 sshd[18412]: Failed password for invalid user admin from 198.51.100.42 port 48210 ssh2
Sep  4 02:45:14 srv-bastion-01 sshd[18415]: Failed password for invalid user test from 198.51.100.42 port 48214 ssh2
Sep  4 02:45:16 srv-bastion-01 sshd[18418]: Failed password for root from 198.51.100.42 port 48218 ssh2
Sep  4 02:45:18 srv-bastion-01 sshd[18422]: Connection closed by authenticating user root 198.51.100.42 port 48222 [preauth]

Analysis Note: Typical automated dictionary attack cadence occurring every 2 seconds.

Aggregation Command & OutputBash Terminal
$ grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -n 5
   7890 198.51.100.42
   4590 198.51.100.89

Analysis Note: Shows volume distribution between the two attacking hosts.

MITRE ATT&CK Mapping

IDTechniqueTacticObserved Evidence
T1110.001 Brute Force: Password GuessingCredential AccessHigh-volume dictionary attempts against standard account names
T1046 Network Service DiscoveryDiscoveryAdversary targeting publicly reachable TCP port 22

Findings & Final Classification

True Positive brute-force reconnaissance and credential guessing. The attack was completely unsuccessful due to enforced SSH key-based authentication and disabled password logins. However, the failure of fail2ban exposed the SSH daemon to unnecessary resource consumption.

Recommended SOC Response & Hardening

  • •Maintain strict key-only authentication; verify PasswordAuthentication remains "no".
  • •Change default SSH listening port from 22 to a non-standard high port or restrict access via VPN/WireGuard.
  • •Configure watchdog monitoring for fail2ban to trigger an alert if the daemon process terminates.
  • •Implement Cloudflare Spectrum or cloud security group restrictions allowing SSH only from authorized management IPs.

Analyst Reflection: What I Learned

• Configuration hardening (disabling password auth) protects systems even when detection/prevention daemons like fail2ban fail.

• Verifying the absence of "Accepted" log entries is the single most critical step when triaging authentication spikes.

• System log parsing with simple Unix tools (grep, awk, sort) is often faster than waiting for index query jobs during live triage.

References & Standards