Detection rules › Kusto
LSASS Dumping using Debug Privileges
This query searches for a process that requests the SeDebugPrivilege privilege and opens LSASS memory using specific permission 0x1fffff which represents PROCESS_ALL_ACCESS.
Known false positives
- There are some applications that perform these actions for legitimate purposes. One example is Procmon by Sysinternals.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Credential Access |
References
Telemetry coverage
Rule body
let timeframe = 2*1h;
let SeDebugPrivilege = binary_shift_left(1, 20); // Value for SeDebugPrivilege is 2**20 = 0x100000.
let LSASSOpen=materialize (
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| extend AccessRights=parse_json(AdditionalFields).DesiredAccess
| where AccessRights == 0x1fffff // PROCESS_ALL_ACCESS.
| summarize by DeviceId, InitiatingProcessId, InitiatingProcessSHA1
);
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType == "ProcessPrimaryTokenModified"
| where isnotempty(InitiatingProcessSHA1)
// Look for processes that request debug privilege that also opened LSASS
| where InitiatingProcessSHA1 in ((LSASSOpen | project InitiatingProcessSHA1)) // Speeds up the query.
| lookup kind=inner LSASSOpen on DeviceId, InitiatingProcessSHA1, InitiatingProcessId
// Check that debug privilege is enabled.
| extend AdditionalFields=parse_json(AdditionalFields)
| extend CurrentTokenPrivEnabled = toint(AdditionalFields.CurrentTokenPrivEnabled)
| extend OriginalTokenPrivEnabled = toint(AdditionalFields.OriginalTokenPrivEnabled)
// Value for SeDebugPrivilege is 2**20 = 0x100000.
// Refer to https://downloads.volatilityfoundation.org//omfw/2012/OMFW2012_Gurkok.pdf for numeric values for privileges.
| extend DebugPrivCurrent = binary_and(CurrentTokenPrivEnabled,SeDebugPrivilege) == SeDebugPrivilege
| extend DebugPrivOrig = binary_and(OriginalTokenPrivEnabled,SeDebugPrivilege) == SeDebugPrivilege
// Check for processes that have debug privilege after the event, but did not have it before.
| where not(DebugPrivOrig) and DebugPrivCurrent
| extend CleanCmdLine = parse_command_line(InitiatingProcessCommandLine, "windows")
| where not(InitiatingProcessFileName =~ "tasklist.exe" and CleanCmdLine has_any ("/m", "-m"))
| extend HostName=tostring(split(DeviceName,".")[0]),DnsDomain=iif(DeviceName contains ".", substring(DeviceName, indexof(DeviceName, ".") + 1, strlen(DeviceName)),"")
| project-reorder Timestamp, DeviceId, InitiatingProcessFileName
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1h;
let SeDebugPrivilege = binary_shift_left(1, 20);
Let binding: LSASSOpen
let LSASSOpen = materialize (
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| extend AccessRights=parse_json(AdditionalFields).DesiredAccess
| where AccessRights == 0x1fffff
| summarize by DeviceId, InitiatingProcessId, InitiatingProcessSHA1
);
Stage 1: source
let LSASSOpen
Stage 2: source
DeviceEvents
Stage 3: where
where ...
Stage 4: where
where ActionType =~ "ProcessPrimaryTokenModified"
Stage 5: where
where isnotempty(InitiatingProcessSHA1)
Stage 6: where
where /* macro: (InitiatingProcessSHA1 in <NestedQuery>) */
Stage 7: kusto:lookup
lookup kind=inner (LSASSOpen) on DeviceId, InitiatingProcessSHA1, InitiatingProcessId
Stage 8: extend (5 consecutive steps)
extend AdditionalFields, CurrentTokenPrivEnabled, DebugPrivCurrent, DebugPrivOrig, OriginalTokenPrivEnabled
Stage 9: where
where /* macro: DebugPrivOrig */
Stage 10: extend
extend CleanCmdLine
Stage 11: where
where not (((CleanCmdLine contains "/m" or CleanCmdLine contains "-m") and InitiatingProcessFileName =~ "tasklist.exe"))
Stage 12: extend
extend DnsDomain, HostName
Stage 13: project-reorder
project-reorder
Stage 14: summarize aggregation inside the lookup branch
summarize by DeviceId, InitiatingProcessId, InitiatingProcessSHA1
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
CleanCmdLine | match | /m, -m | excludes:CleanCmdLine field:"CleanCmdLine" value:"/m" field:"CleanCmdLine" value:"-m" |
InitiatingProcessFileName | eq | tasklist.exe | excludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"tasklist.exe" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AccessRights | eq |
| field:"AccessRights" kind:eq value:"2097151" |
ActionType | eq |
| field:"ActionType" kind:eq |
FileName | eq |
| field:"file_name" kind:eq value:"lsass.exe" |
InitiatingProcessSHA1 | is_not_null | field:"InitiatingProcessSHA1" kind:is_not_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceId | summarize |
InitiatingProcessId | summarize |
InitiatingProcessSHA1 | summarize |