Detection rules › Elastic
Potential Library Load via ROP Gadgets
Identifies the load of a library from unusual modules or NTDLL offsets. This may indicate an attempt to bypass API monitoring using Return Oriented Programming (ROP) assembly gadgets to execute a syscall instruction.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule body
[rule]
description = """
Identifies the load of a library from unusual modules or NTDLL offsets. This may indicate an attempt to bypass API
monitoring using Return Oriented Programming (ROP) assembly gadgets to execute a syscall instruction.
"""
id = "415c647f-ebb1-474d-b621-192d6f64c01d"
license = "Elastic License v2"
name = "Potential Library Load via ROP Gadgets"
os_list = ["windows"]
version = "1.0.33"
query = '''
library where process.pid != 4 and
process.executable != null and dll.name != null and
not startswith~(dll.name, process.name) and not dll.name : "ntdll.dll" and
process.thread.Ext.call_stack_summary : ("ntdll.dll|*", "win32u.dll|*") and
dll.name : ("ws2_32.dll", "wininet.dll", "winhttp.dll", "amsi.dll", "ntdll.dll", "dnsapi.dll") and
not _arraysearch(process.thread.Ext.call_stack, $entry,
$entry.symbol_info: ("*ntdll.dll*!Ldr*", "*ntdll.dll!LdrLoadDll*", "*KernelBase.dll*!LoadLibrary*", "*ntdll.dll*!*MapViewOfSection*",
"*wow64win.dll+0x*", "*ntdll.dll!NtMapViewOfSection*",
"*\\KernelBase.dll!KernelbasePostInit*", "*\\ntdll.dll!RtlCaptureStackBackTrace*", "*ntdll.dll!NtQueryVirtualMemory*",
"*ntdll.dll!ZwQueryVirtualMemory*", "*ntdll.dll!ZwMapViewOfSection*", "*ntdll.dll!ZwReadFile*",
"*ntdll.dll*!*OpenThreadTokenEx*", "*ntdll.dll*!*DeviceIoControlFile*", "*ntdll.dll*!NtOpenProcess*",
"*ntdll.dll*!*CreateUserProcess*", "*ntdll.dll*!NtTerminateProcess*", "*ntdll.dll*!NtAlpcOpenSenderProcess*",
"*ntdll.dll*!*ReplyWaitReceivePortEx*", "*ntdll.dll*!NtSetInformationFile*", "*ntdll.dll!ZwSetInformationFile*", "*ntdll.dll!TpAllocPool*",
"*ntdll.dll!RtlUnhandledExceptionFilter*", "*ntdll.dll*!*AlpcOpenSenderProcess*", "*ntdll.dll!KiUserApcDispatcher*",
"*ntdll.dll!TpCallbackIndependent*", "*win32u.dll!NtGdiDdDDIQueryAdapterInfo*", "*ntdll.dll!*DuplicateObject*",
"*kernel32.dll!CreateThread*", "*KernelBase.dll!CreateRemoteThread*", "*ntdll.dll!*SetInformationWorkerFactory*", "*ntdll.dll!TpReleaseCleanupGroupMembers*",
"*ntdll.dll!NtReleaseWorkerFactoryWorker+*", "*ntdll.dll!ZwWaitForWorkViaWorkerFactory*",
"*KernelBase.dll!MapViewOfFileEx*", "*ntdll.dll!NtdllDialogWndProc*", "*ntdll.dll!EtwRegisterSecurityProvider*",
"*ntdll.dll!NtMapUserPhysicalPagesScatter*", "?:\\Windows\\System32\\cyinjct.dll!CyProcessPreinitThunk*",
"*\\ntdll.dll!ZwAlpcDeleteSecurityContext*", "*\\ntdll.dll!ZwAccessCheckAndAuditAlarm*",
"*\\win32u.dll!NtGdiDdDDIQueryAdapterInfo*", "*\\ntdll.dll!NtdllDefWindowProc_W*",
"C:\\Windows\\System32\\ntdll.dll!TpCallbackMayRunLong*", "C:\\Windows\\System32\\KernelBase.dll!GlobalUnlock+*",
"C:\\Windows\\System32\\ntdll.dll!NtGetTickCount*", "C:\\Windows\\System32\\ntdll.dll!NtSetDebugFilterState*",
"C:\\Windows\\System32\\ntdll.dll!NtWaitForDebugEvent*", "C:\\Windows\\System32\\ntdll.dll!RtlEncodeRemotePointer*",
"C:\\Windows\\System32\\ntdll.dll!SbExecuteProcedure*", "C:\\Windows\\System32\\ntdll.dll!MD4Init*", "*ntdll.dll!*CallbackReturn*",
"C:\\Windows\\System32\\ntdll.dll!ZwWaitForDebugEvent+*", "*ntdll.dll!RtlReAllocateHeap*", "*ntdll.dll!RtlAllocateHeap*",
"*ntdll.dll!NtReplyPort*", "*ntdll.dll!ZwCallbackReturn*", "*ntdll.dll!NtCreateWnfStateName*",
"C:\\Windows\\System32\\ntdll.dll!NtRequestWaitReplyPort*", "C:\\Windows\\System32\\ntdll.dll!NtAlpcImpersonateClientOfPort*",
"C:\\Windows\\System32\\ntdll.dll!RtlInsertElementGenericTableFullAvl*", "C:\\Windows\\System32\\ntdll.dll!RtlLookupElementGenericTableAvl*",
"C:\\Windows\\System32\\ntdll.dll!RtlCloneUserProcess*", "*ntdll.dll!RtlSetThreadSubProcessTag*",
/* Win11 24H2/25H2 (builds 26100/26200) moved ntdll loader internals away from their classic exports,
nearest-export symbolication resolves Ldrp* frames to these unrelated exports */
"*ntdll.dll!RtlGetSuiteMask+*", "*ntdll.dll!RtlLocateExtendedFeature+*", "*ntdll.dll!NtInitiatePowerAction+*")) and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : "*ntdll.dll*" and $entry.allocation_private_bytes > 0) and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : "*ntdll.dll+0x*") and
/* trusted-signed process whose stack resolves back into its own main image contradicts the hidden-caller
premise; covers Go runtimes (e.g. Elastic Agent) whose symbol-less binaries cannot rescue attribution */
not (process.code_signature.trusted == true and stringcontains~(process.thread.Ext.call_stack_summary, process.name)) and
/* fully module-backed stack in a trusted-signed process contradicts the hidden-caller premise: ROP payloads live in
unbacked memory, which surfaces as Unbacked/Unknown segments in the stack summary. Each monthly 26100/26200 build
reshuffles the nearest-export misattributions (26200.8457 resolves loader internals to RtlGetSuiteMask and
RtlLocateExtendedFeature, 26200.8875 to RtlDecodeSystemPointer, wcsnlen and TpCaptureCaller), so symbol
allowlisting alone cannot keep up */
not (process.code_signature.trusted == true and
not stringcontains~(process.thread.Ext.call_stack_summary, "unbacked") and
not stringcontains~(process.thread.Ext.call_stack_summary, "unknown")) and
/* CLR frame in a trusted-signed process identifies the caller as the .NET runtime (P/Invoke or runtime-internal
LoadLibrary), contradicting the hidden-caller premise; covers JIT-compiled callers whose stacks do contain
unbacked regions but cannot rescue attribution when nearest-export drift hides the Ldrp and LoadLibrary frames */
not (process.code_signature.trusted == true and
_arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : ("*\\clr.dll*", "*\\coreclr.dll*"))) and
not process.thread.Ext.call_stack_summary like ("ntdll.dll|kernelbase.dll|fastprox.dll|combase.dll|rpcrt4.dll|*",
"ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|fastprox.dll|combase.dll|rpcrt4.dll|combase.dll|wbemprox.dll|wbemdisp.dll|oleaut32.dll|wbemdisp.dll|oleaut32.dll|rpcrt4.dll|combase.dll|oleaut32.dll|combase.dll|user32.dll") and
not (process.executable : "C:\\Program Files\\Tenable\\Nessus Agent\\nessus*.exe" and process.code_signature.subject_name == "TENABLE, INC." and process.code_signature.trusted == true)
'''
min_endpoint_version = "8.10.0"
optional_actions = []
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1055"
name = "Process Injection"
reference = "https://attack.mitre.org/techniques/T1055/"
[threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[internal]
min_endpoint_version = "8.10.0"
Stages and Predicates
Stage 1: library
library where process.pid != 4 and
process.executable != null and dll.name != null and
not startswith~(dll.name, process.name) and not dll.name : "ntdll.dll" and
process.thread.Ext.call_stack_summary : ("ntdll.dll|*", "win32u.dll|*") and
dll.name : ("ws2_32.dll", "wininet.dll", "winhttp.dll", "amsi.dll", "ntdll.dll", "dnsapi.dll") and
not _arraysearch(process.thread.Ext.call_stack, $entry,
$entry.symbol_info: ("*ntdll.dll*!Ldr*", "*ntdll.dll!LdrLoadDll*", "*KernelBase.dll*!LoadLibrary*", "*ntdll.dll*!*MapViewOfSection*",
"*wow64win.dll+0x*", "*ntdll.dll!NtMapViewOfSection*",
"*\\KernelBase.dll!KernelbasePostInit*", "*\\ntdll.dll!RtlCaptureStackBackTrace*", "*ntdll.dll!NtQueryVirtualMemory*",
"*ntdll.dll!ZwQueryVirtualMemory*", "*ntdll.dll!ZwMapViewOfSection*", "*ntdll.dll!ZwReadFile*",
"*ntdll.dll*!*OpenThreadTokenEx*", "*ntdll.dll*!*DeviceIoControlFile*", "*ntdll.dll*!NtOpenProcess*",
"*ntdll.dll*!*CreateUserProcess*", "*ntdll.dll*!NtTerminateProcess*", "*ntdll.dll*!NtAlpcOpenSenderProcess*",
"*ntdll.dll*!*ReplyWaitReceivePortEx*", "*ntdll.dll*!NtSetInformationFile*", "*ntdll.dll!ZwSetInformationFile*", "*ntdll.dll!TpAllocPool*",
"*ntdll.dll!RtlUnhandledExceptionFilter*", "*ntdll.dll*!*AlpcOpenSenderProcess*", "*ntdll.dll!KiUserApcDispatcher*",
"*ntdll.dll!TpCallbackIndependent*", "*win32u.dll!NtGdiDdDDIQueryAdapterInfo*", "*ntdll.dll!*DuplicateObject*",
"*kernel32.dll!CreateThread*", "*KernelBase.dll!CreateRemoteThread*", "*ntdll.dll!*SetInformationWorkerFactory*", "*ntdll.dll!TpReleaseCleanupGroupMembers*",
"*ntdll.dll!NtReleaseWorkerFactoryWorker+*", "*ntdll.dll!ZwWaitForWorkViaWorkerFactory*",
"*KernelBase.dll!MapViewOfFileEx*", "*ntdll.dll!NtdllDialogWndProc*", "*ntdll.dll!EtwRegisterSecurityProvider*",
"*ntdll.dll!NtMapUserPhysicalPagesScatter*", "?:\\Windows\\System32\\cyinjct.dll!CyProcessPreinitThunk*",
"*\\ntdll.dll!ZwAlpcDeleteSecurityContext*", "*\\ntdll.dll!ZwAccessCheckAndAuditAlarm*",
"*\\win32u.dll!NtGdiDdDDIQueryAdapterInfo*", "*\\ntdll.dll!NtdllDefWindowProc_W*",
"C:\\Windows\\System32\\ntdll.dll!TpCallbackMayRunLong*", "C:\\Windows\\System32\\KernelBase.dll!GlobalUnlock+*",
"C:\\Windows\\System32\\ntdll.dll!NtGetTickCount*", "C:\\Windows\\System32\\ntdll.dll!NtSetDebugFilterState*",
"C:\\Windows\\System32\\ntdll.dll!NtWaitForDebugEvent*", "C:\\Windows\\System32\\ntdll.dll!RtlEncodeRemotePointer*",
"C:\\Windows\\System32\\ntdll.dll!SbExecuteProcedure*", "C:\\Windows\\System32\\ntdll.dll!MD4Init*", "*ntdll.dll!*CallbackReturn*",
"C:\\Windows\\System32\\ntdll.dll!ZwWaitForDebugEvent+*", "*ntdll.dll!RtlReAllocateHeap*", "*ntdll.dll!RtlAllocateHeap*",
"*ntdll.dll!NtReplyPort*", "*ntdll.dll!ZwCallbackReturn*", "*ntdll.dll!NtCreateWnfStateName*",
"C:\\Windows\\System32\\ntdll.dll!NtRequestWaitReplyPort*", "C:\\Windows\\System32\\ntdll.dll!NtAlpcImpersonateClientOfPort*",
"C:\\Windows\\System32\\ntdll.dll!RtlInsertElementGenericTableFullAvl*", "C:\\Windows\\System32\\ntdll.dll!RtlLookupElementGenericTableAvl*",
"C:\\Windows\\System32\\ntdll.dll!RtlCloneUserProcess*", "*ntdll.dll!RtlSetThreadSubProcessTag*",
/* Win11 24H2/25H2 (builds 26100/26200) moved ntdll loader internals away from their classic exports,
nearest-export symbolication resolves Ldrp* frames to these unrelated exports */
"*ntdll.dll!RtlGetSuiteMask+*", "*ntdll.dll!RtlLocateExtendedFeature+*", "*ntdll.dll!NtInitiatePowerAction+*")) and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : "*ntdll.dll*" and $entry.allocation_private_bytes > 0) and
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : "*ntdll.dll+0x*") and
/* trusted-signed process whose stack resolves back into its own main image contradicts the hidden-caller
premise; covers Go runtimes (e.g. Elastic Agent) whose symbol-less binaries cannot rescue attribution */
not (process.code_signature.trusted == true and stringcontains~(process.thread.Ext.call_stack_summary, process.name)) and
/* fully module-backed stack in a trusted-signed process contradicts the hidden-caller premise: ROP payloads live in
unbacked memory, which surfaces as Unbacked/Unknown segments in the stack summary. Each monthly 26100/26200 build
reshuffles the nearest-export misattributions (26200.8457 resolves loader internals to RtlGetSuiteMask and
RtlLocateExtendedFeature, 26200.8875 to RtlDecodeSystemPointer, wcsnlen and TpCaptureCaller), so symbol
allowlisting alone cannot keep up */
not (process.code_signature.trusted == true and
not stringcontains~(process.thread.Ext.call_stack_summary, "unbacked") and
not stringcontains~(process.thread.Ext.call_stack_summary, "unknown")) and
/* CLR frame in a trusted-signed process identifies the caller as the .NET runtime (P/Invoke or runtime-internal
LoadLibrary), contradicting the hidden-caller premise; covers JIT-compiled callers whose stacks do contain
unbacked regions but cannot rescue attribution when nearest-export drift hides the Ldrp and LoadLibrary frames */
not (process.code_signature.trusted == true and
_arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : ("*\\clr.dll*", "*\\coreclr.dll*"))) and
not process.thread.Ext.call_stack_summary like ("ntdll.dll|kernelbase.dll|fastprox.dll|combase.dll|rpcrt4.dll|*",
"ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|fastprox.dll|combase.dll|rpcrt4.dll|combase.dll|wbemprox.dll|wbemdisp.dll|oleaut32.dll|wbemdisp.dll|oleaut32.dll|rpcrt4.dll|combase.dll|oleaut32.dll|combase.dll|user32.dll") and
not (process.executable : "C:\\Program Files\\Tenable\\Nessus Agent\\nessus*.exe" and process.code_signature.subject_name == "TENABLE, INC." and process.code_signature.trusted == true)
Exclusions
The rule actively suppresses these predicates.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
dll.name | is_not_null | field:"dll.name" kind:is_not_null | |
dll.name | wildcard |
| field:"dll.name" kind:wildcard |
process.executable | is_not_null | field:"Image" kind:is_not_null | |
process.pid | ne |
| field:"process_id" kind:ne value:"4" |
process.thread.Ext.call_stack_summary | contains |
| field:"process.thread.Ext.call_stack_summary" kind:contains |
process.thread.Ext.call_stack_summary | wildcard |
| field:"process.thread.Ext.call_stack_summary" kind:wildcard |