Detection rules › Elastic

GetAsyncKeyState API Call from Suspicious Process

Time window
5m
Sequence by
process.entity_id
Source
github.com/elastic/protections-artifacts

Identifies attempts to enumerate the state of keyboard keys by an unusual process. Adversaries may log user keystrokes to intercept credentials or other information from the user as the user types them.

MITRE ATT&CK coverage

TacticTechniques
Collection

Rule body

[rule]
description = """
Identifies attempts to enumerate the state of keyboard keys by an unusual process. Adversaries may log user keystrokes
to intercept credentials or other information from the user as the user types them.
"""
id = "ef0a2322-641b-4127-8b48-2def55fe1f1f"
license = "Elastic License v2"
name = "GetAsyncKeyState API Call from Suspicious Process"
os_list = ["windows"]
reference = [
    "https://attack.mitre.org/techniques/T1056/001/",
    "https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate",
    "https://www.elastic.co/security-labs/protecting-your-devices-from-information-theft-keylogger-protection",
]
version = "1.0.5"

query = '''
sequence by process.entity_id with maxspan=5m
 [process where event.action == "start" and
  (
   (process.Ext.device.product_id : ("Virtual DVD-ROM", "Virtual Disk") and not process.executable : "C:\\*") or
   (process.Ext.relative_file_creation_time <= 500 and (process.code_signature.trusted == false or process.code_signature.exists == false)) or
   process.name in~ ("rundll32.exe", "regsvr32.exe", "powershell.exe", "wscript.exe", "cscript.exe", "mshta.exe") or
   process.pe.original_file_name : ("python*.exe", "AutoIt*.exe") or
   (process.Ext.relative_file_creation_time <= 500 and
    process.executable :
                ("?:\\Users\\Public\\*",
                 "?:\\Users\\*\\Downloads\\*",
                 "?:\\Windows\\Temp\\*",
                 "?:\\Windows\\Tasks\\*",
                 "?:\\ProgramData\\*",
                 "?:\\Windows\\Microsoft.NET\\*")) or
    process.executable : ("?:\\Users\\*\\AppData\\Local\\Temp\\7z*\\",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\Rar$*\\*",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\Temp?_*\\*",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\BNZ.*") or
   (process.parent.name : "svchost.exe" and process.parent.args : "Schedule")
  ) and
  not process.executable :
              ("?:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\*\\MsMpEng.exe",
              "?:\\Program Files (x86)\\*.exe",
              "?:\\Program Files\\*.exe") and
  not (process.name : "rundll32.exe" and process.args : "C:\\Windows\\System32\\LogiLDA.dll,LogiFetch")
  ]
 [api where process.Ext.api.name == "GetAsyncKeyState" and
   process.Ext.api.metadata.background_callcount >= 400 and process.Ext.api.metadata.ms_since_last_keyevent >= 100 and 
   not (process.executable : "?:\\Windows\\System32\\rundll32.exe" and 
        process.command_line : "\"?:\\Windows\\System32\\rundll32.exe\" C:\\Windows\\System32\\LogiLDA.dll,LogiFetch" and 
        process.thread.Ext.call_stack_summary == "win32u.dll|winsrvext.dll|ntdll.dll")]
'''

min_endpoint_version = "8.12.0"
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0

[[optional_actions]]
action = "rollback"
field = "process.entity_id"
state = 0

[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1056"
name = "Input Capture"
reference = "https://attack.mitre.org/techniques/T1056/"
[[threat.technique.subtechnique]]
id = "T1056.001"
name = "Keylogging"
reference = "https://attack.mitre.org/techniques/T1056/001/"



[threat.tactic]
id = "TA0009"
name = "Collection"
reference = "https://attack.mitre.org/tactics/TA0009/"

[internal]
min_endpoint_version = "8.12.0"

Stages and Predicates

Ordered sequence: each step below must occur in order within 5m, correlated by process.entity_id.

Stage 1: process

[process where event.action == "start" and
  (
   (process.Ext.device.product_id : ("Virtual DVD-ROM", "Virtual Disk") and not process.executable : "C:\\*") or
   (process.Ext.relative_file_creation_time <= 500 and (process.code_signature.trusted == false or process.code_signature.exists == false)) or
   process.name in~ ("rundll32.exe", "regsvr32.exe", "powershell.exe", "wscript.exe", "cscript.exe", "mshta.exe") or
   process.pe.original_file_name : ("python*.exe", "AutoIt*.exe") or
   (process.Ext.relative_file_creation_time <= 500 and
    process.executable :
                ("?:\\Users\\Public\\*",
                 "?:\\Users\\*\\Downloads\\*",
                 "?:\\Windows\\Temp\\*",
                 "?:\\Windows\\Tasks\\*",
                 "?:\\ProgramData\\*",
                 "?:\\Windows\\Microsoft.NET\\*")) or
    process.executable : ("?:\\Users\\*\\AppData\\Local\\Temp\\7z*\\",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\Rar$*\\*",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\Temp?_*\\*",
                         "?:\\Users\\*\\AppData\\Local\\Temp\\BNZ.*") or
   (process.parent.name : "svchost.exe" and process.parent.args : "Schedule")
  ) and
  not process.executable :
              ("?:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\*\\MsMpEng.exe",
              "?:\\Program Files (x86)\\*.exe",
              "?:\\Program Files\\*.exe") and
  not (process.name : "rundll32.exe" and process.args : "C:\\Windows\\System32\\LogiLDA.dll,LogiFetch")
  ]

Stage 2: api

[api where process.Ext.api.name == "GetAsyncKeyState" and
   process.Ext.api.metadata.background_callcount >= 400 and process.Ext.api.metadata.ms_since_last_keyevent >= 100 and 
   not (process.executable : "?:\\Windows\\System32\\rundll32.exe" and 
        process.command_line : "\"?:\\Windows\\System32\\rundll32.exe\" C:\\Windows\\System32\\LogiLDA.dll,LogiFetch" and 
        process.thread.Ext.call_stack_summary == "win32u.dll|winsrvext.dll|ntdll.dll")]

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
process.argseqC:\Windows\System32\LogiLDA.dll,LogiFetchexcludes:process.args field:"process.args" value:"C:\Windows\System32\LogiLDA.dll,LogiFetch"
process.nameeqrundll32.exeexcludes:process.name field:"process.name" value:"rundll32.exe"
process.executablewildcard?:\ProgramData\Microsoft\Windows Defender\Platform\*\MsMpEng.exe, ?:\Program Files (x86)\*.exe, ?:\Program Files\*.exeexcludes:process.executable field:"process.executable" value:"?:\ProgramData\Microsoft\Windows Defender\Platform\*\MsMpEng.exe" field:"process.executable" value:"?:\Program Files (x86)\*.exe" field:"process.executable" value:"?:\Program Files\*.exe"
process.command_lineeq"?:\Windows\System32\rundll32.exe" C:\Windows\System32\LogiLDA.dll,LogiFetchexcludes:process.command_line
process.executableeq?:\Windows\System32\rundll32.exeexcludes:process.executable field:"process.executable" value:"?:\Windows\System32\rundll32.exe"
process.thread.Ext.call_stack_summaryeqwin32u.dll|winsrvext.dll|ntdll.dllexcludes:process.thread.Ext.call_stack_summary field:"process.thread.Ext.call_stack_summary" value:"win32u.dll|winsrvext.dll|ntdll.dll"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
event.actioneq
  • start corpus 391 (elastic 391)
field:"EventType" kind:eq value:"start"
process.Ext.api.metadata.background_callcountge
  • 400 transforms: number corpus 2 (elastic 2)
field:"process.Ext.api.metadata.background_callcount" kind:ge value:"400"
process.Ext.api.metadata.ms_since_last_keyeventge
  • 100 transforms: number corpus 2 (elastic 2)
field:"process.Ext.api.metadata.ms_since_last_keyevent" kind:ge value:"100"
process.Ext.api.nameeq
  • GetAsyncKeyState corpus 4 (elastic 4)
field:"process.Ext.api.name" kind:eq value:"GetAsyncKeyState"
process.Ext.device.product_idwildcard
  • Virtual DVD-ROM corpus 13 (elastic 13)
  • Virtual Disk corpus 13 (elastic 13)
field:"process.Ext.device.product_id" kind:wildcard
process.Ext.relative_file_creation_timele
  • 500 transforms: number corpus 28 (elastic 28)
field:"process.Ext.relative_file_creation_time" kind:le value:"500"
process.code_signature.existseq
  • false transforms: boolean corpus 119 (elastic 119)
field:"process.code_signature.exists" kind:eq value:"false"
process.code_signature.trustedeq
  • false transforms: boolean corpus 115 (elastic 115)
field:"process.code_signature.trusted" kind:eq value:"false"
process.executablewildcard
  • ?:\ProgramData\* corpus 17 (elastic 17)
  • ?:\Users\*\AppData\Local\Temp\7z*\ corpus 3 (elastic 3)
  • ?:\Users\*\AppData\Local\Temp\BNZ.* corpus 7 (elastic 7)
  • ?:\Users\*\AppData\Local\Temp\Rar$*\* corpus 4 (elastic 4)
  • ?:\Users\*\AppData\Local\Temp\Temp?_*\* corpus 4 (elastic 4)
  • ?:\Users\*\Downloads\* corpus 12 (elastic 12)
  • ?:\Users\Public\* corpus 4 (elastic 4)
  • ?:\Windows\Microsoft.NET\* corpus 5 (elastic 5)
  • ?:\Windows\Tasks\* corpus 7 (elastic 7)
  • ?:\Windows\Temp\* corpus 9 (elastic 9)
field:"Image" kind:wildcard
process.namein
  • cscript.exe corpus 67 (elastic 65, splunk 2)
  • mshta.exe corpus 84 (elastic 79, splunk 5)
  • powershell.exe corpus 184 (elastic 140, splunk 44)
  • regsvr32.exe corpus 73 (elastic 68, splunk 5)
  • rundll32.exe corpus 126 (elastic 100, splunk 26)
  • wscript.exe corpus 83 (elastic 82, splunk 1)
field:"process_name" kind:in
process.parent.argswildcard
  • Schedule corpus 4 (elastic 4)
field:"process.parent.args" kind:wildcard value:"Schedule"
process.parent.namewildcard
  • svchost.exe corpus 19 (elastic 12, splunk 5, kusto 2)
field:"parent_process_name" kind:wildcard value:"svchost.exe"
process.pe.original_file_namewildcard
  • AutoIt*.exe corpus 8 (elastic 8)
  • python*.exe corpus 2 (elastic 2)
field:"OriginalFileName" kind:wildcard