Every failed SAP logon in SM20 looks the same at a glance — an AU2 record, a user, a timestamp. But a brute-force attempt, an employee hammering an account they already know is locked, and a saved password that’s gone stale all produce failures, and only the timing tells them apart.
Judge by raw count and you’ll either miss a real attack hidden in a small, patient cluster or raise a false alarm over a locked-out employee. This prompt clusters every failure by user, IP, and terminal, then classifies each cluster by its interval pattern — not its size — and rebuilds the full lock-to-unlock chain so you can see who got locked, how, and who unlocked them.
It’s free. No form, no login. Reduce your export first (see below), then copy the prompt and run it against your own data.
How this works
- Export the audit log. Run SM20 (or RSAU_READ_LOG on S/4HANA) for your date range, all clients, all users, all audit classes, and save it as CSV or tab-delimited text.
- Reduce the export. This analysis needs timestamps, so keep the detail file: run the free sm20_reduce.py script (or the Excel filter) to cut a raw export down to the authentication-relevant rows before pasting anything into an LLM.
- Run the failed-logon prompt. Copy the prompt below into your company-approved LLM — a commercial assistant or a model running on your own infrastructure — and attach the reduced signal file (or the raw export, if you have not reduced it yet).
- Read the clusters and lock chains. Check Table A for each cluster’s verdict and reasoning, then Table B to see who was locked, who unlocked them, and whether they still failed to log on afterward.
Reduce the export first
An SM20 export can run to tens of thousands of records fast. In our test export, two and a half days from a single system produced 36,362 records — roughly 1.8 million tokens at an approximate 50 tokens per row. Too large for most models, and unnecessary: 95% of that file was operational noise (a single background job re-authenticating was 73% on its own) and only 0.57% of records carried security signal. You would be paying to load 36,362 rows in order to read 209.
Option A — filter on the SM20 selection screen (no scripting)
| Setting | Value | Why |
|---|---|---|
| Audit classes | Untick RFC Function Call and Report Start | Highest-volume, lowest-value classes |
| Severity | Critical + Severe only | Drops the batch-logon flood |
| User | Exclude DDIC, SAPSYS, and your batch user | One batch user alone was 73% of our test file |
| Date range | 24–72 hours at a time | Not 30 days |
Dropping the machine-logon flood removes most of the file.
Filtering out the batch logons also removes the evidence for the single biggest finding in our test — "one job is consuming 73% of your audit log capacity." If you want that finding, use Option B.
Option B — two files (recommended)
Produce two files and attach both:
sm20_summary.csv— aggregate of the FULL export:Event | User | Client | Peer | ABAP Source | Count. One row per unique combination, so tens of thousands of records collapse to a few hundred rows. Preserves the volume picture. No timestamps.sm20_signal.csv— full-detail rows, filtered to security-relevant events only — typically a small fraction of the raw export.
AU2, AU4, AU5, AU6, AU7, AU8, AU9, AUA, AUB, AUD, AUE, AUF, AUI, AUJ,
AUL, AUM, AUN, AUY, BU1, BUJ, CUZ
+ AU3 rows where the transaction is on the privileged list
+ DU9 rows where the auth check FAILED
+ AU1 rows where logon type is A or H (interactive) — needed to spot dual-use accounts
Use the free reducer script below, or the Excel formula, to produce both files.
Excel filter for sm20_signal.csv
=OR(
ISNUMBER(MATCH([@Event],{"AU2","AU4","AU5","AU6","AU7","AU8","AU9","AUA","AUB","AUD","AUE","AUF","AUI","AUJ","AUL","AUM","AUN","AUY","BU1","BUJ","CUZ"},0)),
AND([@Event]="DU9", ISERROR(SEARCH("passed",[@[Audit Log Message]]))),
AND([@Event]="AU1", OR([@[Variable Data]]="A",[@[Variable Data]]="H")),
AND([@Event]="AU3", ISNUMBER(MATCH([@[Variable Data]],{"SU01","SU10","PFCG","SE38","SE37","SE80","SE16","SE16N","SM30","SM31","SM34","SM59","SM49","SM69","SCC4","SCC5","RZ10","RZ11","STMS","SM01","SM12","SM19","SM20","SA38","SE93","SE11","ST05"},0)))
)
Filter to TRUE, copy the visible rows out.
Free download — sm20_reduce.py
Run the reducer script to produce both files automatically, or use the Excel formula above.
Download sm20_reduce.py See the full script and how it works →
What you'll need
| Column | Source |
|---|---|
SAP System | SID |
AS Instance | Application server instance |
Date / Time | Event timestamp |
Cl. | Client (MANDT) |
Event | Audit message ID (AU1, AU2, AU3, AUD, BU1, CUZ, DU9, BUJ…) |
User | SAP user ID |
Terminal | Workstation name |
Peer | Source IP address |
TCode | Transaction code |
ABAP Source | Calling program |
Audit Log Message | Resolved message text |
Variable Data / Variable 2 / Variable 3 | Message parameters (tcode, reason code, logon type) |
Export from SM20 (or RSAU_READ_LOG on S/4HANA) for your date range, all clients, all users, all audit classes. Save as a CSV with the columns above.
The prompt
Copy this verbatim into your LLM of choice, then attach the CSV described above.
You are an SAP security analyst. You are given an SM20 / RSAU Security Audit Log
export. Analyse all authentication activity and determine, for each cluster of
failures, WHAT ACTUALLY HAPPENED. Do not label anything a brute-force attack
unless the TIMING supports it.
Source columns:
SAP System, AS Instance, Date, Time, Cl., Event, User, Terminal, Peer, TCode,
ABAP Source, Audit Log Message, Variable Data, Variable 2, Variable 3
===========================================================================
STEP 0 - INPUT HANDLING
===========================================================================
This analysis REQUIRES timestamps. If you are given only an aggregated summary
file (a Count column, no Time column), STOP and say so: cluster timing cannot be
reconstructed from counts. Ask for the detail file.
If given a raw unfiltered export, first extract ONLY the authentication events
listed in STEP 1 and work from those. State how many records you filtered down
from.
===========================================================================
THE NULL-EVIDENCE RULE
===========================================================================
The absence of a logon record is NOT evidence that a logon was hidden, bypassed,
or concealed. SM20 records only what the audit config was told to record;
successful RFC, trusted-RFC, reused-session and internal logons may legitimately
produce no record. Never write "no logon event exists, therefore the audit trail
was bypassed." If an absence is material, report it as an open question and name
SM19 / RSAU_CONFIG as the check.
===========================================================================
STEP 1 - EXTRACT THE AUTHENTICATION EVENT SET
===========================================================================
AU1 Logon successful AU2 Logon failed
AU6 RFC/CPIC logon failed BU1 Password check failed
AU9 User locked AUA User unlocked
AUM User locked after password errors
AUN User unlocked after wrong password
AUD User master record changed
BUD / BUE Delayed or web-service logon
Ignore all other events.
===========================================================================
STEP 2 - DECODE TYPE AND REASON ON EVERY RECORD
===========================================================================
Logon type (Variable Data on AU1/AU2):
A = Dialog (SAPGUI) H = HTTP / Fiori / OData -> HUMAN
B = Batch F = Background E = Internal/system
G = RFC S = SAP service V = Web service -> MACHINE
Reason code (AU2):
1 = invalid credentials (unknown user or wrong password)
8 = logon attempted against an account that is ALREADY LOCKED
Unlisted value: print the raw code, mark it UNMAPPED, do not guess a meaning.
===========================================================================
STEP 3 - CLUSTER THE FAILURES
===========================================================================
Group AU2 + BU1 by (User + Peer IP + Terminal). For each cluster compute:
total failures; failures by reason code; first and last failure; elapsed window;
MEAN INTERVAL between consecutive failures; MAX failures in any rolling 5-minute
window; whether an AU1 SUCCESS from the same User+IP follows, and when.
===========================================================================
STEP 4 - CLASSIFY. Timing decides the verdict, not the raw failure count.
===========================================================================
BRUTE FORCE / PASSWORD SPRAY - rate CRITICAL
Many failures in a short window, OR one IP failing against MANY DIFFERENT
user IDs. Reason 1 dominant. Short, regular intervals implying automation.
LOCKED-ACCOUNT HAMMERING - rate LOW
Reason 8 dominant, sometimes mixed with reason 1. ONE user, ONE workstation,
a few minutes. A frustrated human retrying an already-locked account. This is
a support ticket, not an attack. Say so plainly.
STALE CACHED CREDENTIAL - rate MEDIUM
A SMALL number of failures, ALWAYS the same user and same host, spread over
hours or days with LONG IRREGULAR gaps, never enough consecutive failures to
trip a lockout. This is a saved password gone out of date - a SAPGUI saved
logon, a script, a scheduled task, or an RFC destination on that host.
It is NOT brute force: the cadence is wrong. Name the host to investigate.
SERVICE / RFC ACCOUNT FAILURE - rate MEDIUM
AU6, or AU2 with a MACHINE logon type. A machine identity failing - a broken
interface, or a password rotated on one side only.
ISOLATED USER ERROR - rate INFORMATIONAL
One or two failures followed by a success from the same user and host.
===========================================================================
STEP 5 - REBUILD THE LOCK / UNLOCK CHAIN
===========================================================================
For every user with reason-8 failures, search the timeline for:
BEFORE the failures : AU9 (locked) or AUM (locked after password errors)
AFTER the failures : AUA (unlocked), AUN, or AUD (master record changed)
Report the chain: who was locked, when, HOW they got locked, who unlocked them
(the User field on the AUD/AUA record), from which Terminal, via which TCode.
Then flag these gaps explicitly:
- Reason-8 failures exist but NO AUM record -> the lock did NOT come from
failed-password auto-lock. It was administrative or a validity-date lock.
State this. Do not assume a lockout policy fired.
- The unlock is visible ONLY as AUD "user master record changed" -> SM20 proves
a change occurred but NOT what changed. A password reset, a lock removal and a
role grant are indistinguishable. CDHDR / CDPOS change documents are required
to attribute the action. This is an AUDIT TRAIL LIMITATION, not a user finding.
- MOST IMPORTANT: if a user's master record was changed and they STILL failed to
log on AFTERWARDS, say so loudly. The change was not the clean unlock it
appears to be, and the ticket is not closed.
===========================================================================
STEP 6 - SOURCE IP PROFILE
===========================================================================
For every distinct Peer IP: is it RFC1918 private, or public? List every user seen
from each IP, and every IP seen for each user. Flag:
- one user logging on from MANY public IPs -> credential sharing, or a mobile
user on a rotating carrier IP. Say which is more likely and why.
- one IP used by MANY users -> shared jump host, or a spray.
- a SUCCESSFUL logon from an IP that also produced FAILURES for OTHER users
-> the strongest single indicator of a compromised source. Rate HIGH.
If the Peer field is blank on a record, report it as "not recorded". Do not infer
an IP from a different record.
===========================================================================
STEP 7 - OUTPUT
===========================================================================
TABLE A - Failure Clusters:
Cluster ID | Verdict | Severity | User | Peer IP | Terminal | Total Failures |
Reason 1 Count | Reason 8 Count | First Failure | Last Failure | Mean Interval |
Max In 5 Minutes | Followed By Success (Y/N + time) | Reasoning (2-3 sentences:
why THIS verdict and not the others) | Recommended Action
TABLE B - Lock / Unlock Chains:
User Locked | Lock Evidence (AU9 / AUM / none found) | Lock Timestamp |
Failed Attempts While Locked | Unlock Evidence (AUA / AUD / none found) |
Unlock Timestamp | Unlocked By | Unlocked From (Terminal) | TCode Used |
Attribution Gap (Y/N) | Still Failing After Change? (Y/N)
Sort Table A by Severity, then Total Failures descending.
===========================================================================
RULES
===========================================================================
- Do not call something an attack because the failure count is high. Call it an
attack because the TIMING and the SPREAD say so.
- Always state what a cluster is NOT, if a reader would reasonably assume worse.
- If there is no evidence of attack, say so clearly and without hedging.
A clean result is a valid result.
- Never conclude an absence of logon records means the audit trail was bypassed.
Why timing decides the verdict
The failure count on its own tells you almost nothing — ten failures from a frustrated employee retyping a locked password look identical in volume to ten failures from an automated credential-stuffing pass. What separates them is the interval: short, regular, machine-fast gaps point to automation; long, irregular gaps spread over days point to a stale saved credential; a handful of attempts in a few minutes from one workstation is usually just a support ticket. This prompt clusters on user, IP, and terminal, then classifies on the timing pattern before it reaches for a verdict.
Beyond a one-time snapshot
These prompts are a one-time read of an exported log. Syntasec does it live and continuously across your entire SAP landscape — correlating SM20 with change documents, role assignments, and system parameters, so the questions these prompts have to leave open ("what actually changed?", "is this scanner sanctioned?") are already answered.
Want the SM20 reducer script and the privileged-transaction watchlist as a ready-to-run file?
Optional. The prompt above is fully readable and copyable whether or not you use this.
Frequently asked questions
How can I tell if failed SAP logons are a brute-force attack or just a locked-out user?
Timing, not the raw count. A burst of failures in a short window, or one IP failing against many different user IDs, points to automation. A handful of failures from one user and one workstation over a few minutes, mostly reason code 8 (already locked), is someone hammering an account they already know is locked — a support ticket, not an attack. This prompt clusters failures by user, IP, and terminal and classifies each cluster on its interval pattern before it labels anything.
What does AU2 reason code 8 mean in SM20?
In practice, reason code 8 indicates the account was already locked at the time — the failure isn't a credential guess, it's someone retrying access they don't currently have. Reason code 1 indicates invalid credentials (unknown user or wrong password) instead. Confusing the two turns routine lockout retries into false brute-force alarms.
How do I know if a failed logon is a stale saved password, not an attack?
Look at the cadence. A small number of failures for the same user and same host, spread over hours or days with long irregular gaps — never enough in a row to trip a lockout — is the signature of a saved credential that’s gone out of date: a SAPGUI saved logon, a script, a scheduled task, or an RFC destination configured on that host. Brute-force attempts are short and regular; stale credentials are sparse and irregular.
Who unlocked a locked SAP user, and can SM20 prove what changed?
SM20 can show that a user master record changed (AUD) and often who made the change and from where, but it can’t show what the change was. A password reset, a lock removal, and a role grant all produce the same AUD record. If you need to know exactly what changed, that requires the CDHDR/CDPOS change documents — SM20 alone gets you the who and when, not the what.
Can SM20 alone confirm a brute-force attack happened?
It can build a strong case, but "confirmed" and "requires correlation" are different confidence levels for a reason. A short, regular burst of failures against one or many accounts from one IP is a solid inferred pattern. Whether it’s an external attacker versus, say, a misconfigured integration retrying with a bad credential is something SM20’s IP and terminal fields can narrow down but not settle by themselves.
Is it safe to paste failed-logon data from SM20 into an AI model?
Not into a public one. Even a failed-logon extract carries user IDs, source IPs, and workstation names tied to real people's login attempts — personal data under GDPR and equivalent regimes. Anonymise those fields before using a public model, or run the analysis inside your own infrastructure. Syntasec runs entirely on the customer's own infrastructure and is LLM-agnostic, so this kind of analysis never requires the data to leave your network.