HIGH Linux audit log (auditd) Credential Stuffing LTR-0027 ✓ validated

Detect SSH Brute Force Authentication Failures in Linux auditd Logs

Published

MITRE ATT&CK: T1110.001T1110

Sigma Rule

title: SSH Brute Force Authentication Failures in auditd
id: 319cdc1d-145d-46c9-8976-d0cd9bcbb1c7
status: experimental
description: >
  Detects a burst of failed SSH authentication attempts recorded by auditd's
  PAM instrumentation for sshd. A single connection attempt that fails
  produces one or more audit records (USER_LOGIN, USER_ERR, USER_AUTH)
  carrying exe="/usr/sbin/sshd" and res=failed; a sustained run of these
  from one source address in a short window is the signature of an
  automated SSH brute-force campaign.
references:
  - https://attack.mitre.org/techniques/T1110/001/
  - https://man7.org/linux/man-pages/man8/auditd.8.html
author: LogTriage
date: 2026/10/26
logsource:
  product: linux
  service: auditd
detection:
  selection:
    exe: '/usr/sbin/sshd'
    res: 'failed'
  timeframe: 5m
  condition: selection | count() by addr >= 10
falsepositives:
  - Misconfigured client or expired keypair producing legitimate failure bursts
  - Authentication testing by vulnerability scanners or monitoring tools
level: high
tags:
  - attack.credential_access
  - attack.t1110.001
How the rule decides

1 · Each event is compared with one selection. Every field in a selection must match; listed values are alternatives.

selection
  • exe is /usr/sbin/sshd
  • and res is failed

2 · An event counts when

selection

3 · Then

Count matching events for each addr within 5 minutes; it alerts at at least 10.

Alert high Checked on every deploy: fires on auditd_ssh_bruteforce.log, silent on auditd_normal.log — both in the public sample dataset ↗︎

What this rule detects

A failed SSH login against a Linux host with auditd enabled does not produce a single log line — it produces a chain of PAM-instrumented audit records: USER_LOGIN when the login attempt is made, USER_ERR with op=PAM:bad_ident when the client’s identity can’t be resolved, and USER_AUTH with op=PAM:authentication when a password is actually checked and rejected. All three carry exe="/usr/sbin/sshd" and res=failed.

A brute-force campaign shows up as a dense run of these records from one source address — far more than the occasional mistyped password produces, and compressed into seconds rather than spread across a normal workday. This rule fires when a single source address accumulates 10 or more failed sshd authentication records within 5 minutes.

Detection logic

The selection is deliberately narrow: exe: '/usr/sbin/sshd' and res: 'failed', with no restriction on type. That is intentional — the three record types sshd failures can land in (USER_LOGIN, USER_ERR, USER_AUTH) are all the same underlying event (a rejected SSH authentication attempt) surfacing at different PAM stages, not separate signals. Restricting to one record type would miss attempts that fail before reaching that stage.

The count groups by addr, the source IP address recorded on each of these records, with a timeframe: 5m. Grouping by address rather than by account name matters here: early attempts in a brute-force run often never resolve to a username at all (acct="(unknown)"), so a per-account count would undercount the campaign.

Validated against a real sample

Validated against auditd_ssh_bruteforce.log (shipped with LogTriage), which shows 26 failed sshd authentication records — 8 USER_LOGIN/USER_ERR pairs with acct="(unknown)", 8 more USER_AUTH records with acct="root", and a final USER_LOGIN failure also for root — all carrying addr=203.0.113.45, spanning roughly 45 seconds. The rule’s count() by addr >= 10 condition is satisfied well before the run ends.

The sample continues past the brute force: a USER_AUTH record with res=success for root from the same address, followed by a bash reverse shell to 203.0.113.45, a useradd call, and auditctl -D removing the host’s own audit rules. This rule does not fire on those later events — they are a separate behavior (post-compromise activity) and belong in separate rules.

It stays silent on auditd_normal.log. That log’s sshd records (USER_ACCT, CRED_ACQ, USER_START, USER_LOGIN, USER_END, USER_LOGOUT) are all res=success; the only res=failed record in the file is a USER_AUTH for sudo (exe="/usr/bin/sudo"), which does not carry exe="/usr/sbin/sshd". No benign record matches both selection fields.

False positives

A client retrying with a stale or rotated SSH key against one host can produce a short burst of legitimate res=failed records from a single address. So can a vulnerability scanner or monitoring tool configured to test SSH authentication paths. Both are distinguishable from a real brute force by checking whether the source address is a known, authorized host and whether the burst stops — a single host correcting a bad key fails a handful of times and then succeeds or stops; an attacker keeps trying.

Frequently Asked Questions

Why match on exe and res instead of a single audit record type?
A single rejected SSH connection does not produce one tidy audit event. auditd's PAM instrumentation can emit USER_LOGIN (op=login), USER_ERR (op=PAM:bad_ident) and USER_AUTH (op=PAM:authentication) for the same attempt, depending on how far it got before failing. All three carry exe="/usr/sbin/sshd" and res=failed, so matching on those two fields catches a failed attempt regardless of which record type it landed in, instead of missing attempts that never reach the PAM:authentication stage.
Why count by addr instead of by the account name?
In the sample, the first nine attempts never resolve a username — acct is literally "(unknown)" or "?" because the client fails PAM identification (bad_ident) before a name is even checked. Only the later attempts carry acct="root". Grouping by the source address (addr) captures the whole campaign; grouping by account would undercount it, since the attacker's early guesses have no account name to group on at all.
Why a count threshold instead of alerting on one failed login?
A single failed SSH login is routine — a mistyped password, a stale key, a script with an old credential. It only becomes a security event when the same source racks up many failures quickly. The 10-in-5-minutes threshold filters out the noise and keeps the alert for sustained, automated attempts.
Does a later successful login change how this rule should be read?
This rule only detects the failure burst, not what happens after a success. If the same source address shows a `res=success` PAM:authentication record following the burst, treat the account as compromised — in the sample, acct="root" succeeds immediately after the failed run, and is followed by a reverse shell launched via bash, a new user being added with useradd, and the host's own audit rules being removed with `auditctl -D`. Those are separate behaviors from separate rules; this rule's job is to catch the brute force before or as it succeeds.

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.