Detection rules › Kusto

Detect Suspicious ncrypt.dll usage by process requesting Entra ID Nonce

Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This detection rule uses a WDAC audit policy to ingest missing DeviceImageLoad events in MDE, and check for suspicious processes using the ncrypt.dll and requesting an Entra ID Nonce. More information on the attack scenario this is detection is applicable for can be found in the references.

MITRE ATT&CK coverage

References

Event coverage

Rule body yaml

let cli_tools = dynamic(["powershell", "python"]);
// Get all possible nonce requests
let nonce_requests = (
    DeviceNetworkEvents
    | where Timestamp > ago(1h)
    | where ActionType startswith "ConnectionSuccess"
    | where RemoteUrl =~ "login.microsoftonline.com"
    | project-rename NonceTimestamp = Timestamp
);
// Get suspicious ncrypt.dll usage via WDAC audit policy
DeviceEvents
| where Timestamp > ago(1h)
| where ActionType startswith "AppControl" and FileName =~ "ncrypt.dll"
// Check if the same initiating process is doing a nonce request
| join kind=inner nonce_requests on InitiatingProcessId, DeviceId
// Only flag when nonce was request 10min before of after ncrypt usage
| where Timestamp between (todatetime(NonceTimestamp - 10m) .. todatetime(NonceTimestamp + 10m))
| invoke FileProfile(InitiatingProcessSHA1, 1000)
| where (
    // Flag CLI tools
    InitiatingProcessFileName has_any (cli_tools) or 
    // Flag unknown processes
    GlobalPrevalence < 250
)

let cli_tools = dynamic(["powershell", "python"]);
// Get all possible nonce requests
let nonce_requests = (
    DeviceNetworkEvents
    | where TimeGenerated > ago(1h)
    | where ActionType startswith "ConnectionSuccess"
    | where RemoteUrl =~ "login.microsoftonline.com"
    | project-rename NonceTimestamp = TimeGenerated
);
// Get suspicious ncrypt.dll usage via WDAC audit policy
DeviceEvents
| where TimeGenerated > ago(1h)
| where ActionType startswith "AppControl" and FileName =~ "ncrypt.dll"
// Check if the same initiating process is doing a nonce request
| join kind=inner nonce_requests on InitiatingProcessId, DeviceId
// Only flag when nonce was request 10min before of after ncrypt usage
| where TimeGenerated between (todatetime(NonceTimestamp - 10m) .. todatetime(NonceTimestamp + 10m))
| invoke FileProfile(InitiatingProcessSHA1, 1000)
| where (
    // Flag CLI tools
    InitiatingProcessFileName has_any (cli_tools) or 
    // Flag unknown processes
    GlobalPrevalence < 250
)

Stages and Predicates

Parameters

let cli_tools = dynamic(["powershell", "python"]);

Let binding: nonce_requests

let nonce_requests = (
    DeviceNetworkEvents
    | where Timestamp > ago(1h)
    | where ActionType startswith "ConnectionSuccess"
    | where RemoteUrl =~ "login.microsoftonline.com"
    | project-rename NonceTimestamp = Timestamp
);

Stage 1: source

DeviceEvents

Stage 2: where

| where Timestamp > ago(1h)

Stage 3: where

| where ActionType startswith "AppControl" and FileName =~ "ncrypt.dll"

Stage 4: join

| join kind=inner nonce_requests on InitiatingProcessId, DeviceId

Stage 5: where

| where Timestamp between (todatetime(NonceTimestamp - 10m) .. todatetime(NonceTimestamp + 10m))

Stage 6: invoke

| invoke FileProfile(InitiatingProcessSHA1, 1000)

Stage 7: where

| where (
    InitiatingProcessFileName has_any (cli_tools) or 
    GlobalPrevalence < 250
)

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
ActionTypestarts_with
  • AppControl corpus 5 (kusto 5)
  • ConnectionSuccess
FileNameeq
  • ncrypt.dll corpus 4 (kusto 4)
GlobalPrevalencelt
  • 250 transforms: cased corpus 4 (kusto 4)
InitiatingProcessFileNamematch
  • powershell corpus 2 (kusto 2)
  • python corpus 2 (kusto 2)
RemoteUrleq
  • login.microsoftonline.com corpus 3 (kusto 3)