HIGH Linux auth.log / secure Credential Stuffing LTR-0011 ✓ validated

Detect SSH Brute Force and User Enumeration in Linux auth.log

MITRE ATT&CK: T1110.001T1110.003T1078

Sigma Rule

title: SSH Brute Force / User Enumeration in auth.log
id: 3f9c1d47-6b28-4a95-8e13-7d40c2a5b6f1
status: experimental
description: >
  Detects SSH password brute-forcing and username enumeration against a Linux
  host. A burst of "Failed password" and "Invalid user" events from a single
  source IP in a short window is the signature of an automated attack.
  Triage pivot: check for an "Accepted password" from the same source IP right
  after the burst, which confirms the brute force succeeded. That is
  deliberately kept out of the condition — successful password logins are
  normal in isolation, so matching them here would fire on benign activity.
references:
  - https://attack.mitre.org/techniques/T1110/001/
  - https://attack.mitre.org/techniques/T1110/003/
author: LogTriage
date: 2026/08/04
logsource:
  product: linux
  service: sshd
detection:
  failed_password:
    message|contains: 'Failed password for'
  invalid_user:
    message|contains: 'Invalid user'
  timeframe: 5m
  condition: (failed_password or invalid_user) | count() by src_ip >= 10
falsepositives:
  - Stale key or expired password retried in a loop by one account
  - Expired credential on a monitoring or backup agent (allowlist its IP)
  - Background internet scanning on an exposed host with no fail2ban
level: high
tags:
  - attack.credential_access
  - attack.t1110.001
  - attack.t1110.003

What this rule detects

SSH brute force is the oldest attack on this list and still one of the most common, because it works: an exposed port, a weak password on a service account, and no rate limiting. In auth.log it produces an unmistakable shape — a rapid run of Failed password for and Invalid user lines, all from one source IP, cycling through usernames.

This rule fires when a single source produces 10 or more failed or invalid-user events within 5 minutes.

Detection logic

Two selections do the work, and they detect different things:

  • Failed password for <user> — the account exists, the password was wrong. This is password guessing against a known-good username.
  • Invalid user <user> — the account does not exist. This is username enumeration; the attacker is working through a wordlist.

A genuine automated attack generates both, which is why the rule counts them together rather than treating them as separate detections.

The confirmation pivot, and why it is not in the condition

Once this rule fires, the next thing to check is whether any Accepted password for arrived from the same source IP right after the burst. That single line separates a failed attempt from an active intrusion.

It is deliberately not part of the detection condition. Successful password logins are entirely normal in isolation — the benign sample shipped with LogTriage contains one — so matching on it here would fire on ordinary activity and undermine the rule. Correlating the failure burst with the follow-on success is a job for the analysis layer, not a static signature. Accepted publickey is likewise ignored: key-based auth is the normal path and cannot be brute-forced this way.

Validated against a real sample

Validated against authlog_brute_force.log (shipped with LogTriage): a single source, 185.220.101.45, generating Failed password attempts against root, admin, and postgres interleaved with Invalid user events for pi, ubuntu, oracle, testuser, and deploy — textbook enumeration against default and service account names.

The rule fires on that sample and stays silent on the matching benign log, which contains only Accepted publickey logins and sudo activity from an internal address — zero occurrences of either detection literal.

False positives

The realistic ones are all low-variety: a deploy script with a stale key, or a monitoring agent whose password expired, retrying in a loop. Both hit a single username from a constant source, where an attack sprays many usernames. If you see one account and one IP, allowlist it rather than raising the threshold.

The genuine noise problem is an internet-exposed SSH port with no fail2ban, which will trip any count-based threshold from background scanning alone. Scope the rule to internal ranges, or raise the count — and treat the username variety as the real signal.

Frequently Asked Questions

Why combine 'Failed password' and 'Invalid user' in one rule?
They are two halves of the same attack. 'Invalid user' means the account does not exist — the attacker is enumerating usernames. 'Failed password' means the account does exist but the password was wrong. A real brute force produces both as the tool works through its list, so counting them together gives a truer picture of the attempt volume than either alone.
How do I know the brute force actually succeeded?
Look for 'Accepted password for' from the same source IP immediately after the failure burst — that single line turns a failed attack into an active intrusion. It is deliberately excluded from the rule's condition, because a successful password login is completely normal on its own and matching it would fire on benign activity. Treat it as the triage pivot after the rule fires, not as part of the signature. 'Accepted publickey' is normal too and cannot be brute-forced this way.
Is 10 attempts in 5 minutes the right threshold?
It is deliberately conservative for an internal host. An SSH port exposed to the internet without fail2ban will see continuous background scanning that easily exceeds it, so raise the threshold or scope the rule to internal ranges. The more durable signal is not the raw count but the mix: many distinct usernames from one source in a short window is enumeration regardless of volume.
Does this work on /var/log/secure as well as /var/log/auth.log?
Yes. Debian and Ubuntu write sshd events to /var/log/auth.log; RHEL, CentOS, and Fedora write the same events to /var/log/secure. The message formats are identical because they come from the same sshd binary, so the rule applies unchanged to both.

Related Resources

See this detection run on a real report

Try the live demo with a pre-loaded malicious log set — no signup required — or upload your own log file and get a full AI-reviewed threat report in minutes.