Home / Blog / Detection Engineering

Advanced Detection Engineering

How to Detect LSASS Memory Access with Sysmon Event ID 10

Build a tuned LSASS access detection from Sysmon Event ID 10, with Splunk, Sentinel and Sigma versions, plus triage steps and the hardening that reduces your risk.

Attackers who reach local administrator often go straight for the credentials held in LSASS memory, because those credentials let them move on to other hosts. Sysmon Event ID 10 shows you which process opened LSASS and what rights it asked for, but an untuned rule buries you in noise. This guide helps detection engineers and senior analysts build a rule that stays quiet enough to trust.

TechniqueT1003.001, OS Credential Dumping: LSASS Memory
Main signalSysmon Event ID 10 (ProcessAccess) targeting lsass.exe
You will buildCollection config, SPL, KQL and Sigma rules, plus a triage path
>

Scenario: At 02:14 your SIEM flags WS-114. rundll32.exe, running as a local administrator, opened lsass.exe with GrantedAccess of 0x1FFFFF. Ninety seconds later a new .dmp file appears in C:\Windows\Temp. You need to decide quickly whether this is a crash dump or credential theft, and the quality of your rule decides how much of that work is already done.

Understand what Event ID 10 records

Sysmon logs Event ID 10 when one process opens a handle to another. The event is written to Applications and Services Logs/Microsoft/Windows/Sysmon/Operational. Microsoft warns that it can generate a lot of logging when diagnostic tools repeatedly open processes, so it should always be filtered.

These are the fields that matter most for this detection:

  • SourceImage: the process that asked for access. Path and location are your first clues.
  • TargetImage: the process being opened, here C:\Windows\System32\lsass.exe.
  • GrantedAccess: a hexadecimal mask of the rights granted to the handle.
  • CallTrace: the DLLs and offsets on the call stack when the handle was opened.
  • SourceProcessGUID and SourceThreadId: identifiers that let you pivot to other Sysmon events from the same process.

Recent Sysmon versions also record SourceUser and TargetUser. Run sysmon64 -s to print the schema for the version you have deployed.

Read the access mask

The mask is a sum of process access rights. The right that matters most is PROCESS_VM_READ (0x0010), because reading memory is the point of credential dumping. Breaking a mask into its parts tells you what the source process could do with its handle.

GrantedAccessWhat it containsHow to treat it
0x1000Query limited informationCommon and low risk
0x1400Query information and query limited informationCommon and noisy, usually filtered out
0x1010Query limited information and VM readInvestigate, can read memory
0x1410Both query rights and VM readInvestigate, also seen with Task Manager dumps
0x1438Both query rights, VM read, VM write and VM operationHigh concern, write access to LSASS is rarely legitimate
0x143AAs above, plus create threadHigh concern
0x1FFFFFAll accessInvestigate, also seen with ProcDump and Task Manager dumps
i

Note: Sysmon compares GrantedAccess as a literal string, not bitwise. A filter for 0x1010 will not match 0x1410, even though both include VM read. This is the main reason a short list of known bad masks misses things.

Collect the right events

You want every handle opened to LSASS except the two very common query only masks. Sysmon treats repeated rules on the same field as OR, and exclude rules override include rules, so a small configuration does the job.

Sysmon configuration, merge into your existing fileXML
<Sysmon schemaversion="4.90">
  <EventFiltering>
    <RuleGroup name="lsass-include" groupRelation="or">
      <ProcessAccess>
        <TargetImage condition="end with">\lsass.exe</TargetImage>
      </ProcessAccess>
    </RuleGroup>
    <RuleGroup name="lsass-exclude" groupRelation="or">
      <ProcessAccess>
        <GrantedAccess condition="is">0x1000</GrantedAccess>
        <GrantedAccess condition="is">0x1400</GrantedAccess>
      </ProcessAccess>
    </RuleGroup>
  </EventFiltering>
</Sysmon>

Set schemaversion to match your deployed binary, then apply the file with sysmon64 -c yourconfig.xml. Test on a small pilot group first. If your estate has a noisy agent, exclude it in the SIEM before you exclude it on the host, so you can still see the events if that decision needs revisiting.

Baseline before you write the rule

Every estate has legitimate LSASS readers, such as security agents, monitoring tools and backup software. Find yours by counting source and mask pairs over 30 days. Run this after the configuration has been live long enough to cover patching and maintenance windows.

Microsoft Sentinel, Sysmon in the Event tableKQL
Event
| where TimeGenerated > ago(30d)
| where Source == "Microsoft-Windows-Sysmon" and EventID == 10
| extend TargetImage = extract(@'Name="TargetImage">([^<]+)<', 1, EventData),
         SourceImage = extract(@'Name="SourceImage">([^<]+)<', 1, EventData),
         GrantedAccess = extract(@'Name="GrantedAccess">([^<]+)<', 1, EventData)
| where TargetImage endswith @"\lsass.exe"
| summarize Events = count(), Hosts = dcount(Computer) by SourceImage, GrantedAccess
| order by Events desc

Review the output line by line. A source that appears on every host from a protected path under Program Files is a candidate for your allow list. A source that appears on one host from a user profile is a finding, not a baseline entry.

!

Watch out: Never allow list a whole folder or a process name on its own. An attacker can rename a binary, and rundll32.exe is a signed Microsoft file. Use full paths, and only for paths that standard users cannot write to.

Choose your detection logic

There are three common designs. Most teams should run the second as their main rule and add the third as a higher severity alert.

Level 1

Known bad masks

Alert when the mask is on a fixed list such as 0x1010 or 0x1438. Easy to write, but misses any mask you did not list.

Good for a quick start
Level 2

Inverse logic

Alert on any mask outside a short low risk list, from any source outside your allow list. New masks alert by default.

Recommended main rule
Level 3

Correlated sequence

Alert when suspicious access is followed by a dump file on the same host. Fewer alerts and much higher confidence.

Best for high severity paging

Elastic's prebuilt Suspicious Lsass Process Access rule uses the inverse approach. It excludes low risk masks, known tools and specific call trace modules such as mpengine.dll, then alerts on what remains. Elastic also publishes a separate rule for handle duplication, which is worth copying as an idea: 0x40 (PROCESS_DUP_HANDLE) can be noisy, but it deserves its own review rather than being ignored.

Splunk version

Splunk, Sysmon data with the Splunk add-on field namesSPL
index=sysmon EventCode=10 TargetImage="*\\lsass.exe"
    NOT GrantedAccess IN ("0x1000", "0x1400", "0x100000", "0x101000", "0x101400")
    NOT SourceImage IN ("C:\\Windows\\System32\\csrss.exe", "C:\\Windows\\System32\\wininit.exe", "C:\\Windows\\System32\\wbem\\WmiPrvSE.exe")
| stats count min(_time) as firstSeen max(_time) as lastSeen values(GrantedAccess) as masks values(CallTrace) as callTraces by host, SourceImage, SourceUser
| convert ctime(firstSeen) ctime(lastSeen)
| sort - count

Sentinel version

Microsoft Sentinel, inverse logicKQL
let LowRiskMasks = dynamic(["0x1000", "0x1400", "0x100000", "0x101000", "0x101400"]);
let AllowedSources = dynamic([
    "C:\\Windows\\System32\\csrss.exe",
    "C:\\Windows\\System32\\wininit.exe",
    "C:\\Windows\\System32\\wbem\\WmiPrvSE.exe"
]);
Event
| where TimeGenerated > ago(1h)
| where Source == "Microsoft-Windows-Sysmon" and EventID == 10
| extend TargetImage = extract(@'Name="TargetImage">([^<]+)<', 1, EventData),
         SourceImage = extract(@'Name="SourceImage">([^<]+)<', 1, EventData),
         GrantedAccess = extract(@'Name="GrantedAccess">([^<]+)<', 1, EventData),
         CallTrace = extract(@'Name="CallTrace">([^<]+)<', 1, EventData)
| where TargetImage endswith @"\lsass.exe"
| where GrantedAccess !in~ (LowRiskMasks)
| where SourceImage !in~ (AllowedSources)
| project TimeGenerated, Computer, SourceImage, GrantedAccess, CallTrace

Portable Sigma version

Sigma rule, add your own allow listYAML
title: Suspicious Process Access to LSASS
id: 23a44e9a-2853-43c6-aadc-fcbb97037ca8
status: experimental
description: Detects handles opened to LSASS with rights outside a low risk list, from sources outside an allow list.
references:
    - https://attack.mitre.org/techniques/T1003/001/
author: SOC Analysts
date: 2026-11-04
tags:
    - attack.credential-access
    - attack.t1003.001
logsource:
    category: process_access
    product: windows
detection:
    selection:
        TargetImage|endswith: '\lsass.exe'
    filter_masks:
        GrantedAccess:
            - '0x1000'
            - '0x1400'
            - '0x100000'
            - '0x101000'
            - '0x101400'
    filter_sources:
        SourceImage:
            - 'C:\Windows\System32\csrss.exe'
            - 'C:\Windows\System32\wininit.exe'
            - 'C:\Windows\System32\wbem\WmiPrvSE.exe'
    condition: selection and not 1 of filter_*
falsepositives:
    - Security agents, backup tools and monitoring software not yet in the allow list
level: high

Correlate with a dump file

Pairing access with a new dump file gives you an alert that is worth waking someone for. This query looks for a .dmp file, Sysmon Event ID 11 (FileCreate), within ten minutes of suspicious access on the same host.

Microsoft Sentinel, access followed by a dump fileKQL
let LowRiskMasks = dynamic(["0x1000", "0x1400", "0x100000", "0x101000", "0x101400"]);
let Access = Event
    | where TimeGenerated > ago(1d)
    | where Source == "Microsoft-Windows-Sysmon" and EventID == 10
    | extend TargetImage = extract(@'Name="TargetImage">([^<]+)<', 1, EventData),
             SourceImage = extract(@'Name="SourceImage">([^<]+)<', 1, EventData),
             GrantedAccess = extract(@'Name="GrantedAccess">([^<]+)<', 1, EventData)
    | where TargetImage endswith @"\lsass.exe"
    | where GrantedAccess !in~ (LowRiskMasks)
    | project AccessTime = TimeGenerated, Computer, SourceImage, GrantedAccess;
let Dumps = Event
    | where TimeGenerated > ago(1d)
    | where Source == "Microsoft-Windows-Sysmon" and EventID == 11
    | extend TargetFilename = extract(@'Name="TargetFilename">([^<]+)<', 1, EventData),
             Creator = extract(@'Name="Image">([^<]+)<', 1, EventData)
    | where TargetFilename endswith ".dmp"
    | project DumpTime = TimeGenerated, Computer, Creator, TargetFilename;
Access
| join kind=inner Dumps on Computer
| where DumpTime between (AccessTime .. (AccessTime + 10m))
| project AccessTime, DumpTime, Computer, SourceImage, GrantedAccess, Creator, TargetFilename
+

Tip: Add a command line detection alongside this. ATT&CK documents dumping through the built-in comsvcs.dll and its MiniDump export. Look for comsvcs together with minidump or #24 in Sysmon Event ID 1 or Windows Security Event ID 4688, if you have command line auditing enabled.

Triage a hit

When the rule fires, work out who opened LSASS, how, and what happened next. These cards cover the patterns you are most likely to meet.

Source in a writable path

High
Indicator

SourceImage in a user profile, C:\Users\Public, C:\Windows\Temp or a share.

Response

Isolate the host, collect the binary and treat exposed credentials as compromised.

Living off the land dump

High
Indicator

rundll32.exe as the source, or dbghelp.dll or dbgcore.dll in CallTrace, with no matching change ticket.

Response

Find the parent process and the command line, then locate and preserve the dump file.

Unbacked memory in the stack

High
Indicator

UNKNOWN entries in CallTrace, which suggest code running outside a mapped file on disk.

Response

Capture memory if your process allows it, then check the source process for injection.

Signed admin tool

Medium
Indicator

Task Manager or ProcDump opening LSASS, often with 0x1410 or 0x1FFFFF.

Response

Confirm the user, the ticket and the reason. Dumping LSASS is rarely routine, so verify directly with the person.

New security or backup agent

Medium
Indicator

Vendor signed binary under Program Files, seen on many hosts around a rollout.

Response

Check signature and change record, then add the exact path to the allow list.

Then answer three questions before you close or escalate:

  • Which parent process started the source, and which user was it running as? Pivot on SourceProcessGUID to the matching Event ID 1.
  • Was a dump file created, moved, compressed or copied off the host? Check Event ID 11 and network events around the same time.
  • Which accounts had logged on to this host? Any of them may have been exposed, so check their later logons on other systems.

Reduce the risk on the endpoint

Detection tells you about an attempt, but hardening makes that attempt less likely to succeed. These three controls work well together.

LSA protectionRuns LSASS as a protected process, controlled by the RunAsPPL registry value.
Credential GuardUses virtualisation based security to isolate secrets from the normal operating system.
ASR ruleBlocks credential stealing from LSASS through Microsoft Defender Antivirus.

Check LSA protection

On Windows 11 version 22H2 and later, LSA protection is enabled by default on new installs that are enterprise joined and capable of HVCI. Upgraded devices and many servers may not have it, so check rather than assume. A RunAsPPL value of 1 enables it with a UEFI lock. A value of 2 enables it without one, and is only enforced on Windows 11 22H2 and later.

Run on the host, or through your management toolPowerShell
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' -Name RunAsPPL -ErrorAction SilentlyContinue

Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-Wininit'; Id = 12 } -MaxEvents 1 |
    Select-Object TimeCreated, Message

Event 12 from WinInit confirms that LSASS started as a protected process. Before enforcing the setting, use audit mode and review Event IDs 3065 and 3066 in the CodeIntegrity Operational log to spot LSA plugins and drivers that would fail to load.

Hunt ASR rule activity

The attack surface reduction rule is called "Block credential stealing from the Windows local security authority subsystem", with GUID 9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2. Microsoft says it is not required if LSA protection is enabled, because the two work similarly. It is still useful on devices where LSA protection cannot be turned on. In the Defender event log, Event ID 1121 is a block and 1122 is an audit.

Microsoft Defender XDR, advanced huntingKQL
DeviceEvents
| where Timestamp > ago(7d)
| where ActionType in ("AsrLsassCredentialTheftBlocked", "AsrLsassCredentialTheftAudited")
| summarize Events = count(), Devices = dcount(DeviceName) by ActionType, InitiatingProcessFolderPath, InitiatingProcessFileName
| order by Events desc
!

Watch out: Microsoft notes that this rule produces a large volume of audit events, most of which are safe to ignore once it is in block mode. Use the audited events for hunting and baselining, not for alerting. Also keep your Sysmon detection running after hardening, because a protection setting does not remove the need to see an attempt.

Roll the detection out safely

Introduce the rule in stages so that your analysts see it as a reliable signal from the first day it pages anyone.

  1. L0
    CollectDeploy the Sysmon config to a pilot group and check event volume in your SIEM.
    Week 1
  2. L1
    BaselineReview source and mask pairs, and build an allow list of exact paths with an owner for each entry.
    Weeks 2 to 3
  3. L2
    TestIn an isolated lab, validate with a framework such as Atomic Red Team tests for T1003.001, and confirm each test alerts.
    Week 4
  4. L3
    PromoteEnable the inverse rule at medium severity and the correlated rule at high, then review allow list entries each quarter.
    Production

Key takeaways

  1. Read GrantedAccess as a set of rights. PROCESS_VM_READ is the one that most often matters for credential dumping.
  2. Prefer inverse logic over a list of known bad masks, because Sysmon and most SIEM filters match the mask as an exact string.
  3. Baseline for 30 days and allow list exact paths only, never process names or whole folders.
  4. Use CallTrace, the parent process and dump file creation to separate real attacks from admin activity.
  5. Pair the detection with LSA protection and Credential Guard, and treat exposed accounts as compromised when a dump is confirmed.

Start with the baseline query this week, even if you cannot deploy the full rule yet. The list of processes touching LSASS in your estate is useful on its own, and anything on it that nobody can explain is your first hunt.