// question

A Windows host logs show PowerShell fetching and executing a remote script to gain SeDebugPrivilege token duplication. Which Empire module was invoked?

Reference KQL answer and model-by-model comparison.

34f0a430-9d04-4d98-bcb5-1989f14719f0
question id
6/23
models correct
1
valid answer
26%
success rate
// reference answer

Human-validated correct answer for this question.

Get-System
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
Get-System
DeviceProcessEvents
| where FileName in~ ('powershell.exe', 'pwsh.exe')
| where ActionType == 'ProcessCreated'
| where ProcessCommandLine has_any ('iex', 'Invoke-Expression') and ProcessCommandLine has_any ('DownloadString', 'Invoke-WebRequest', 'http://', 'https://')
| project Timestamp, DeviceName, AccountName, InitiatingProcessCommandLine, ProcessCommandLine, ReportId
pass120.51s0.0098
gpt-35-turbo
no answer
no query generatedfail50.00s0.0115
gpt-4-turbo-2024-04-09
no answer
no query generatedfail50.00s0.2320
gpt-4.1
Get-System
DeviceProcessEvents
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any ("SeDebugPrivilege", "token", "Invoke-TokenManipulation", "Invoke-Token")
| where ProcessCommandLine has_any ("http://", "https://") // Indicates remote script fetch
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ProcessCommandLine, FileName, FolderPath
| top 20 by TimeGenerated desc
pass15.32s0.0118
gpt-4.1-finetuned
no answer
no query generatedfail50.00s0.0420
gpt-4.1-mini
no answer
no query generatedfail50.00s0.0079
gpt-4.1-nano
no answer
no query generatedfail50.00s0.0017
gpt-4o
no answer
no query generatedfail50.00s0.0556
gpt-5-high
Get-System
// Detect the Empire module used to gain SeDebugPrivilege via token duplication
// Time range is set by the query invocation context

// 1) PowerShell process events (attempt to decode -EncodedCommand)
let ps_proc =
DeviceProcessEvents
| where FileName in~ ("powershell.exe", "pwsh.exe", "powershell_ise.exe")
| extend RawCmd = tostring(ProcessCommandLine)
// Extract possible -EncodedCommand payload (quoted or unquoted)
| extend Enc1 = extract(@'(?i)(?:^|\s)(?:-|/)(?:e|enc|encodedcommand)\s+"([^"]+)"', 1, RawCmd)
| extend Enc2 = extract(@"(?i)(?:^|\s)(?:-|/)(?:e|enc|encodedcommand)\s+'([^']+)'", 1, RawCmd)
| extend Enc3 = extract(@'(?i)(?:^|\s)(?:-|/)(?:e|enc|encodedcommand)\s+([A-Za-z0-9+/=]{8,})', 1, RawCmd)
| extend Enc = coalesce(Enc1, Enc2, Enc3)
| extend Decoded = iff(isnotempty(Enc), base64_decode_tostring(replace_string(Enc, " ", "")), "")
| extend FullText = trim(' ', strcat(RawCmd, ' ', Decoded))
| extend FullText_l = tolower(FullText)
// Remote fetch indicators (relaxed) + execution
| where FullText_l has_any ("downloadstring","invoke-webrequest","iwr","start-bitstransfer","bitsadmin","system.net.webclient","new-object net.webclient","curl","wget")
| where FullText_l has_any ("invoke-expression","iex","|iex"," | iex",".downloadstring(")
// Token duplication / SeDebugPrivilege / Empire module cues
| where FullText_l has_any ("invoke-tokenmanipulation","get-system","getsystem","sedebugprivilege","duplicatetoken","duplicate token","impersonat")
| extend EmpireModule = case(
    FullText_l matches regex @"\binvoke[-_ ]?tokenmanipulation\b", "Invoke-TokenManipulation",
    FullText_l matches regex @"\bget[-_ ]?system\b", "Get-System",
    FullText_l has "sedebugprivilege" and FullText_l has_any ("duplicatetoken","duplicate token","impersonat"), "Invoke-TokenManipulation",
    "Unknown"
)
| project TimeGenerated, DeviceId, DeviceName, AccountName, ProcessId, FullText, EmpireModule;

// 2) PowerShell command/script logging (ScriptBlock, etc.)
let ps_logs =
DeviceEvents
| where ActionType in~ ("PowerShellCommand","PowerShellScript","PowerShellScriptBlockLogging","PowerShellSuspiciousCommand")
| extend PsText = coalesce(
    tostring(AdditionalFields.ScriptBlockText),
    tostring(AdditionalFields.Command),
    tostring(AdditionalFields.ScriptContent),
    tostring(AdditionalFields.ScriptName),
    tostring(AdditionalFields.Payload)
)
| where isnotempty(PsText)
| extend Text_l = tolower(PsText)
| where Text_l has_any ("downloadstring","invoke-webrequest","iwr","start-bitstransfer","bitsadmin","system.net.webclient","new-object net.webclient","curl","wget")
| where Text_l has_any ("invoke-expression","iex","|iex"," | iex",".downloadstring(")
| where Text_l has_any ("invoke-tokenmanipulation","get-system","getsystem","sedebugprivilege","duplicatetoken","duplicate token","impersonat")
| extend EmpireModule = case(
    Text_l matches regex @"\binvoke[-_ ]?tokenmanipulation\b", "Invoke-TokenManipulation",
    Text_l matches regex @"\bget[-_ ]?system\b", "Get-System",
    Text_l has "sedebugprivilege" and Text_l has_any ("duplicatetoken","duplicate token","impersonat"), "Invoke-TokenManipulation",
    "Unknown"
)
| project TimeGenerated, DeviceId, DeviceName, AccountName, ProcessId=tolong(AdditionalFields.ProcessId), FullText=PsText, EmpireModule;

// 3) Combine and answer: which Empire module(s) were invoked
union isfuzzy=true ps_proc, ps_logs
| where EmpireModule != "Unknown"
| summarize 
    FirstSeen=min(TimeGenerated),
    LastSeen=max(TimeGenerated),
    Devices=make_set(DeviceName, 10),
    Samples=make_set(substring(FullText, 0, 220), 5)
  by EmpireModule
| order by LastSeen desc
pass5668.71s0.3114
gpt-5-mini-high
Get-System
// Find PowerShell commands that fetch/execute remote scripts and reference SeDebug/token behavior, extract Invoke-* names (likely Empire module names)
let hits =
(
  union isfuzzy=true
  (
    DeviceProcessEvents
    | project TimeGenerated, DeviceName, ProcessId, CommandLine = ProcessCommandLine, InitiatingCommandLine = InitiatingProcessCommandLine, InitiatingFileName = InitiatingProcessFileName, RemoteUrl = ""
  ),
  (
    DeviceEvents
    | project TimeGenerated, DeviceName, ProcessId = ProcessId, CommandLine = ProcessCommandLine, InitiatingCommandLine = InitiatingProcessCommandLine, InitiatingFileName = InitiatingProcessFileName, RemoteUrl = FileOriginUrl
  )
)
| where tolower(CommandLine) has "powershell"
// indicators of remote fetch/execute
| where tolower(CommandLine) has_any ("iex", "invoke-expression", "invoke-webrequest", "invoke-restmethod", "downloadstring", "downloadfile", "-encodedcommand", "iwr ", "irm ")
// indicators of token/SeDebug usage or explicit Invoke-Module names
| where tolower(CommandLine) has_any ("sedebug", "sedebugprivilege", "se_debug", "token", "invoke-")
| extend Modules = extract_all(@"(?i)(Invoke-[A-Za-z0-9_]+)", CommandLine)
| mv-expand Modules
| extend Module = tostring(Modules)
;
// Summary of candidate modules (likely Empire module names)
hits
| summarize Count = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by Module
| order by Count desc

// Recent sample events for analyst review (top 50)

| union (
    hits
    | top 50 by TimeGenerated desc
    | project TimeGenerated, DeviceName, ProcessId, Module, CommandLine, InitiatingCommandLine, InitiatingFileName, RemoteUrl
  )
pass135.67s0.0088
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0173
gpt-5-mini-medium
no answer
no query generatedfail50.00s0.0322
gpt-5-nano-high
Get-System
let psRemoteEvents =
DeviceProcessEvents
| where InitiatingProcessFileName in ("powershell.exe","pwsh.exe")
| where (InitiatingProcessCommandLine contains "IEX" or InitiatingProcessCommandLine contains "Invoke-Expression" or InitiatingProcessCommandLine contains "DownloadString" or InitiatingProcessCommandLine contains "Invoke-WebRequest" or InitiatingProcessCommandLine contains "New-Object System.Net.WebClient")
| join kind=inner (
    DeviceNetworkEvents
    | where isnotempty(RemoteUrl)
    | extend NetTimeGenerated = TimeGenerated
    | project InitiatingProcessId, RemoteUrlUsed = RemoteUrl, NetTimeGenerated
) on InitiatingProcessId
| extend RemoteUrlUsed = RemoteUrlUsed
| extend EmpireModuleCandidate = coalesce(tostring(extract(@"([^/?#]+)\\.ps1$", 1, RemoteUrlUsed)), tostring(extract(@"([^/?#]+)$", 1, RemoteUrlUsed)))
| where isnotempty(EmpireModuleCandidate)
| project TimeGenerated = NetTimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrlUsed, EmpireModuleCandidate;

psRemoteEvents
pass368.28s0.0056
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0088
grok-3-beta
no answer
no query generatedfail50.00s0.1036
grok-3-mini-beta
no answer
no query generatedfail50.00s0.0078
o1-high
no answer
no query generatedfail50.00s0.8012
o1-low
Get-System
DeviceProcessEvents
| where FileName in~ ("powershell.exe", "powershell_ise.exe")
    // Filter for PowerShell processes.
| where isnotempty(ProcessCommandLine)
    // Ensure we have a command line to investigate.
| where ProcessCommandLine has_any ("IEX", "Invoke-Expression", "SeDebugPrivilege", "Invoke-TokenManipulation", "empire")
    // Look for script loading, token manipulation, or Empire references.
| parse kind=regex ProcessCommandLine with * "powershell/" ModuleName " " *
    // Attempt to parse out the Empire module name from the command.
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, ModuleName
| order by TimeGenerated desc
pass138.59s0.2849
o3-high
no answer
no query generatedfail50.00s0.0938
o3-mini-high
no answer
no query generatedfail50.00s0.0395
o3-mini-low
no answer
no query generatedfail50.00s0.0432
o4-mini-high
WinPwn
DeviceProcessEvents
| where FileName =~ "powershell.exe"
| where ProcessCommandLine contains "DownloadString" and ProcessCommandLine contains "IEX"
| extend EmpireModuleWithExt = extract(@"\/([^\/]+\.ps1)", 1, ProcessCommandLine)
| extend EmpireModule = replace(@"\.ps1$", "", EmpireModuleWithExt)
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ProcessCommandLine, EmpireModule
fail263.39s0.0218
o4-mini-low
Invoke-Seatbelt
DeviceProcessEvents
| where ProcessCommandLine matches regex "(?i)powershell.*DownloadString.*Invoke-[A-Za-z-]+"
// Extract the Empire module name (Invoke-…)
| extend EmpireModule = extract(@"(Invoke-[A-Za-z-]+)", 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, ProcessCommandLine, EmpireModule
| summarize Count = count() by EmpireModule, DeviceName
| order by Count desc
fail126.97s0.0067