| name | brute-force-logon |
| description | Investigate whether an account experienced a burst of failed logons, particularly one followed by an eventual success, consistent with password brute forcing or credential stuffing. Use when given authentication logs and asked to check for brute force, credential stuffing, or password spraying against a single account. |
Brute Force
MITRE ATT&CK: T1110 (Brute Force) — commonly .001 Password Guessing or
.003 Password Spraying depending on whether one account or many are
targeted (see also user-account-guessing for the many-accounts variant).
1. Which question am I trying to answer?
Did account X rack up Y failed logon attempts within time window
Z, particularly (though not necessarily) followed by an eventual
success — consistent with an automated password-guessing attack against
that single account?
2. Which direct evidence do I need?
Per authentication event, scoped to a single (user, destination) pair:
Time, Source, Destination, User, Success/Failure, Logon Type. The
signal is failure density over time for one account against one target,
not the absolute count alone.
3. Where do I get that data?
- Windows Security.evtx: Event ID
4625 (failure) — check the Status/
Sub Status fields to distinguish bad password from account-locked/
expired, which changes interpretation. 4624 for the eventual success, if
any.
- Linux:
Failed password / Accepted password lines in
/var/log/auth.log or /var/log/secure; also check faillog/pam_tally2
state if available.
- Application/SaaS auth logs (O365/Entra sign-in logs, VPN, SSH
bastion) — brute force increasingly targets external-facing SaaS and VPN
endpoints rather than only on-prem AD.
- Account lockout events (
4740) are a strong corroborating signal —
a real brute force against a policy-enforced account often produces a
lockout before (or instead of) a success.
4. How do I analyze that data to answer the question?
Normalize into the shared schema (timestamp, user, source, destination, success, logon_type), then run:
python3 scripts/analyze.py --input auth_events.csv \
--window-minutes 10 --min-failures 5
Logic: group events by (user, destination), then slide a --window- minutes window and count failures inside it. Any window with failures at or
above --min-failures is flagged; if a success occurs within the window (or
immediately after it), the flag is annotated as "followed by success" —
this is the higher-confidence case, but pure failure bursts with no
eventual success are still reported since a failed brute-force attempt is
still an attempt.
Tuning: 5 failures / 10 minutes is aggressive on purpose to catch
scripted attempts; raise --window-minutes (e.g. to 60) to also catch slow,
low-and-slow brute forcing designed to stay under per-minute alerting
thresholds, at the cost of needing a longer log retention window to see it.
5. What answer does the analysis provide?
A list of (user, destination, window, failure_count, followed_by_success)
findings. Interpretation:
- Failures followed by a success is the strongest signal — but check
the
Source on the successful attempt matches the Source of the failed
attempts. A success from a different source right after a failure burst
from another source is more consistent with the legitimate user simply
mistyping their password, then logging in normally from their own machine,
than with a successful brute force.
- Pure failure bursts with no success may indicate a mitigated attack
(lockout policy worked), a misconfigured service account/script hammering
with a stale credential, or a human user genuinely forgetting their
password — check
Sub Status codes and whether the source is a known
service host before treating this as attacker activity.
- Repeat offenders across many destinations (same user, many
destination values, each individually under threshold) suggest the
threshold is being deliberately evaded — consider running this alongside
user-account-guessing from the same source.
A confirmed brute force against a real account should drive an immediate
credential reset regardless of whether it "succeeded," since password
strength alone doesn't prove the guessed value wasn't reused elsewhere.