The defaults are strict — precision ≥ 0.90, recall = 1.00, FP rate ≤ 0.10. Anything more
lenient is rejected at load time unless it carries a written justification. 16
of 20 rules currently run on a justified lenient threshold.
| rule | precision | recall | FP rate | declared bar |
entra_audit_app_credential_added | 0.60 | 1.00 | 0.40 | p≥0.55 · r≥1.00 · fp≤0.40 |
| Why the bar is where it is: Two benign cases are unwinnable at the Sigma level. One is a provisioning service principal doing legitimately what an attacker does maliciously, which is an allow-list problem. The other is the InitiatedBy serialisation shape described in the rule: separating it needs `tostring(InitiatedBy.app.appId)`, which is platform syntax Sigma cannot express portably. Both are measured here rather than assumed away. |
entra_audit_illicit_consent_grant | 0.80 | 1.00 | 0.25 | p≥0.75 · r≥1.00 · fp≤0.25 |
| Why the bar is where it is: One benign case is an approved mail-integration onboarding, which requests exactly the scopes an attacker's app requests. Nothing in the event separates a sanctioned Mail.Read grant from a phished one — that needs the application object ID checked against an approved list, which is enrichment the rule cannot perform on its own. |
entra_audit_privileged_role_assigned | 1.00 | 1.00 | 0.00 | p≥0.90 · r≥1.00 · fp≤0.10 |
entra_signin_legacy_auth_success | 0.80 | 1.00 | 0.20 | p≥0.80 · r≥1.00 · fp≤0.20 |
| Why the bar is where it is: One benign case is deliberately unwinnable: a multifunction printer relaying over Authenticated SMTP is indistinguishable from an attacker using the same protocol with a stolen service-account password. That is an allow-list problem, not a logic problem, and the rule says so in its falsepositives. |
posh_ps_defender_tamper | 0.71 | 1.00 | 0.40 | p≥0.65 · r≥1.00 · fp≤0.45 |
| Why the bar is where it is: Sanctioned exclusion management is genuinely indistinguishable from an attacker carving out a staging directory when all you have is the 4104 event. Separating them needs the author identity and the excluded path checked against an approved list, which is enrichment the rule cannot do alone. |
posh_ps_obfuscated_payload_execution | 0.83 | 1.00 | 0.11 | p≥0.80 · r≥1.00 · fp≤0.15 |
| Why the bar is where it is: One benign case fires and is expected to: a packaging script that decodes a URL from base64 and then fetches it is, at the level of the script block, the same behaviour as a stager. Separating them needs context this telemetry does not carry (script path reputation, destination host), so the cost is one declared false positive per baselined bootstrap script rather than a weaker rule. Measured FP rate is 0.11 against the sibling rule's 0.67 on the same benign events. |
posh_ps_susp_encoded_powershell_scriptblock | 0.50 | 1.00 | 0.67 | p≥0.45 · r≥1.00 · fp≤0.70 |
| Why the bar is where it is: A token-frequency heuristic, not a detection. -join and [Convert]::ToInt are ordinary PowerShell, so two thirds of benign administrative script blocks fire. Demoted to informational and kept as a hunting query: useful for ranking script blocks by suspiciousness, unusable as a page. The alerting tier is posh_ps_obfuscated_payload_execution, whose benign half is this rule's four false alarms — it scores 0.11 FP rate on them by requiring an execution or ingress sink alongside the primitive. |
proc_creation_win_certutil_download_decode | 0.67 | 1.00 | 0.33 | p≥0.60 · r≥1.00 · fp≤0.40 |
| Why the bar is where it is: -encode and -decode are legitimate certutil verbs used by PKI enrolment scripts and management agents. Dropping them would raise precision but lose payload-staging coverage, which is the more valuable half of this rule. Keeping them and allow-listing the known callers is the right trade. |
proc_creation_win_encoded_powershell | 0.83 | 1.00 | 0.14 | p≥0.80 · r≥1.00 · fp≤0.20 |
| Why the bar is where it is: A deliberate trade, measured both ways. The previous substring version scored precision 1.00 / FP 0.00 but recall 0.80, because it listed -enc/-encodedcommand/-ec and PowerShell accepts any unambiguous abbreviation of -EncodedCommand — so `powershell -e <base64>` walked straight past it. The regex closes that evasion and takes recall to 1.00; the cost is one false positive, a PowerShell command line passing a long base64-shaped value to an inner tool's -e switch. Missing a known evasion is worse than an occasional alert on `docker run -e`, and the residual FP is fixable by baselining the parent process, whereas the recall gap was only fixable by widening the switch match — which is what this is. |
proc_creation_win_firewall_disabled_netsh | 0.75 | 1.00 | 0.14 | p≥0.70 · r≥1.00 · fp≤0.20 |
| Why the bar is where it is: "netsh advfirewall firewall set rule ... disable" disables a single rule, not the firewall, but shares both keywords with the malicious pattern. Excluding it needs a negative condition on "set rule", which risks an attacker including that phrase to evade. The false positive is the safer trade. |
proc_creation_win_impacket_wmiexec_output | 1.00 | 1.00 | 0.00 | p≥0.90 · r≥1.00 · fp≤0.10 |
security_win_asrep_roasting | 0.75 | 1.00 | 0.25 | p≥0.70 · r≥1.00 · fp≤0.30 |
| Why the bar is where it is: Accounts intentionally configured without pre-authentication are a real configuration state, not a detection failure. The residual false positive is a known legacy account; the fix is an inventory of those accounts fed in as an exclusion list, not a change to the rule logic. |
security_win_dcsync_replication | 1.00 | 1.00 | 0.00 | p≥0.90 · r≥1.00 · fp≤0.10 |
security_win_eventlog_cleared | 0.60 | 1.00 | 1.00 | p≥0.55 · r≥1.00 · fp≤1.00 |
| Why the bar is where it is: Accepts a 100% false-positive rate deliberately. This rule has no discriminating logic and cannot acquire any: a cleared log carries no evidence of why it was cleared. It stays a high-severity alert because the base rate is near zero — 1102 does not occur on a healthy host — so the absolute alert volume is negligible even at FP rate 1.0. That is a base-rate argument, not a quality one, and it is the only reason this bar is set here. |
security_win_kerberoasting_rc4_request | 0.75 | 1.00 | 0.17 | p≥0.70 · r≥1.00 · fp≤0.25 |
| Why the bar is where it is: Measured against a modern AES-default domain, where RC4 is anomalous. The one residual false positive is a legacy appliance still negotiating RC4. In an estate that has not moved off RC4 these numbers do not transfer at all — the bar would have to be re-derived against that environment's own case set. |
security_win_local_user_created | 0.43 | 1.00 | 1.00 | p≥0.40 · r≥1.00 · fp≤1.00 |
| Why the bar is where it is: Unlike the 1102 rule, this one cannot lean on a low base rate: account creation is routine on a managed estate, so FP rate 1.0 here means real, daily noise. The rule is therefore demoted to informational and treated as an audit trail rather than an alert. Closing this properly needs correlation (creation followed by privileged-group addition, or creation outside the helpdesk workflow), which is stateful and out of scope for a single-event rule. |
security_win_scheduled_task_created | 0.75 | 1.00 | 0.25 | p≥0.70 · r≥1.00 · fp≤0.30 |
| Why the bar is where it is: Scheduling a .cmd or .ps1 script is legitimate administrative automation and is indistinguishable from persistence at the event level. This is already a large improvement on the bare EventID match it replaced (precision 0.43 -> 0.75, FP rate 1.00 -> 0.25); closing the rest needs the task author checked against an approved automation identity. |
security_win_service_installed | 1.00 | 1.00 | 0.00 | p≥0.90 · r≥1.00 · fp≤0.10 |
security_win_user_added_to_privileged_group | 0.60 | 1.00 | 0.50 | p≥0.55 · r≥1.00 · fp≤0.55 |
| Why the bar is where it is: Privileged-group additions are performed legitimately by helpdesk and provisioning workflows, and the event carries nothing that separates a sanctioned grant from an attacker's. The discriminator that would work is the actor: additions by accounts outside the helpdesk group. That is enrichment against directory data, not something a single-event rule can express. |
sysmon_proc_access_lsass_dump | 0.80 | 1.00 | 0.20 | p≥0.75 · r≥1.00 · fp≤0.25 |
| Why the bar is where it is: Defender's scanning engine opens lsass with a read mask as normal operation, and any AV or EDR will do the same. This rule is not deployable without a per-estate allow-list of legitimate accessors; the threshold reflects the rule as written, before that allow-list exists. |
Thresholds live in the case files, not in the Sigma rules, so the rules stay
portable — a threshold is a property of this harness, not of the detection.
A false-positive rate of 0.67 is a failure for an alert and
perfectly reasonable for a hunt, which is why the threshold is a property of the rule rather than
a constant. Severity follows what is being obfuscated, not the fact that something is.