// question

An analyst suspects that a restricted shell escape was executed using a common Perl package manager on Linux. Review the process execution records to determine which tool was invoked to spawn the shell.

Reference KQL answer and model-by-model comparison.

bcd4c2bc-490b-4f91-bd31-3709fe75bbdf
question id
8/23
models correct
1
valid answer
35%
success rate
// reference answer

Human-validated correct answer for this question.

cpan
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
no answer
no query generatedfail50.00s0.0163
gpt-35-turbo
Perl
DeviceProcessEvents
| where tolower(FolderPath) contains "perl" 
| where FileName in~ ("cpanm","perlbrew","perl-build","carton","cpan","plenv","ack","perl") 
| project ProcessId, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, ProcessCreationTime
fail211.25s0.0060
gpt-4-turbo-2024-04-09
cpan
DeviceProcessEvents
| where (InitiatingProcessFileName has_any ('cpan','cpanm') or InitiatingProcessCommandLine contains 'cpan' or InitiatingProcessCommandLine contains 'cpanm')
| where ProcessCommandLine has_any ('sh', 'bash', 'zsh')
| project DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, ProcessCommandLine, TimeGenerated
pass217.71s0.0996
gpt-4.1
dash
DeviceProcessEvents
| where InitiatingProcessFileName has_any ('cpan', 'cpanm', 'cpanp') or InitiatingProcessCommandLine has_any ('cpan', 'cpanm', 'cpanp')
| where FileName has_any ('sh', 'bash', 'dash', 'zsh', 'ksh')
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName, FolderPath, InitiatingProcessFolderPath
fail316.30s0.0333
gpt-4.1-finetuned
/bin/sh
DeviceProcessEvents 
| where InitiatingProcessCommandLine contains ".sh"
| summarize processCommandLineCount=count() by InitiatingProcessCommandLine, DeviceName, AccountName
| extend timestamp = now()
| sort by processCommandLineCount desc 
| extend AccountName = tostring(split(AccountName, "@")[0]), DnsDomain = tostring(split(AccountName, "@")[1])
| extend HostName = tostring(split(DeviceName, ".")[0]), DomainIndex = toint(indexof(DeviceName, "."))
| extend DNSDomain = iff(DomainIndex != -1, substring(DeviceName, DomainIndex + 1), DeviceName)
// Please uncomment below to filter the results based on a specific domain/hostname
// | where DNSDomain in ("<your domain/host to be filtered>") or HostName in ("<your domain/host to be filtered>")
// Please uncomment below to filter the results based on a specific account name
// | where AccountName in ("<your account to be filtered>")
fail121.02s0.0866
gpt-4.1-mini
cpan
DeviceProcessEvents
| where FileName in ("cpan", "cpanm") or ProcessCommandLine has_any ("cpan", "cpanm")
| where InitiatingProcessFileName in ("sh", "bash", "/bin/sh", "/bin/bash") or InitiatingProcessCommandLine has_any ("sh", "bash", "/bin/sh", "/bin/bash")
| project TimeGenerated, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by TimeGenerated desc
pass233.25s0.0044
gpt-4.1-nano
perl
DeviceProcessEvents
| where ProcessCommandLine contains 'perl' or ProcessCommandLine contains '/bin/sh' or ProcessCommandLine contains 'bash'
| where ProcessCommandLine matches regex '(?i)escape|restricted|shell|perl'
| project TimeGenerated, DeviceName, InitiatingProcessFileName, ProcessCommandLine, ProcessId, InitiatingProcessParentFileName, InitiatingProcessParentId, InitiatingProcessParentCreationTime
| order by TimeGenerated desc
fail13.20s0.0018
gpt-4o
dash
DeviceProcessEvents
| where InitiatingProcessFileName =~ "perl" or InitiatingProcessFileName =~ "cpan"
| where FileName has_any ("bash", "sh", "shell") or ProcessCommandLine has_any ("bash", "sh", "shell")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine
fail15.70s0.0153
gpt-5-high
cpan
// Shell escape via Perl package manager (cpan/cpanm / perl -MCPAN)
let shell_names = dynamic(["sh","bash","dash","zsh","ksh","ash","busybox","fish"]);
let cpan_regex = @"(?i)\bcpanm?\b|(-MCPAN|CPAN::)";
let Proc = DeviceProcessEvents
    | where ActionType == "ProcessCreated"
    | project DeviceId, DeviceName, TimeGenerated, AccountName, AccountDomain,
              FileName, FolderPath, ProcessCommandLine, ProcessId, ProcessCreationTime,
              InitiatingProcessId, InitiatingProcessCreationTime,
              InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine,
              InitiatingProcessParentId, InitiatingProcessParentCreationTime, InitiatingProcessParentFileName;
// Shell processes on Linux
let Shells = Proc
    | where FileName in (shell_names)
    | where FolderPath startswith "/";
// Parent (the tool that spawned the shell)
let ShellsWithParent = Shells
    | join kind=inner (
        Proc
        | project DeviceId,
                  ParentProcessId = ProcessId,
                  ParentProcessCreationTime = ProcessCreationTime,
                  ToolFileName = FileName,
                  ToolFolderPath = FolderPath,
                  ToolCommandLine = ProcessCommandLine,
                  ToolProcessId = ProcessId,
                  ToolProcessCreationTime = ProcessCreationTime,
                  ToolInitiatingProcessId = InitiatingProcessId,
                  ToolInitiatingProcessCreationTime = InitiatingProcessCreationTime,
                  ToolInitiatingProcessFileName = InitiatingProcessFileName,
                  ToolInitiatingProcessCommandLine = InitiatingProcessCommandLine
    ) on $left.DeviceId == $right.DeviceId
       and $left.InitiatingProcessId == $right.ParentProcessId
       and $left.InitiatingProcessCreationTime == $right.ParentProcessCreationTime;
// Grandparent (often cpan/cpanm or perl running CPAN)
let ShellsWithAncestors = ShellsWithParent
    | join kind=leftouter (
        Proc
        | project DeviceId,
                  GPProcessId = ProcessId,
                  GPProcessCreationTime = ProcessCreationTime,
                  GPFileName = FileName,
                  GPFolderPath = FolderPath,
                  GPCommandLine = ProcessCommandLine
    ) on $left.DeviceId == $right.DeviceId
       and $left.ToolInitiatingProcessId == $right.GPProcessId
       and $left.ToolInitiatingProcessCreationTime == $right.GPProcessCreationTime
    // Keep cases indicating CPAN involvement
    | where ToolFileName in ("cpan","cpanm")
        or ToolCommandLine matches regex cpan_regex
        or coalesce(GPFileName, "") in ("cpan","cpanm")
        or coalesce(GPCommandLine, "") matches regex cpan_regex
        or (ToolFileName =~ "perl" and ToolCommandLine matches regex cpan_regex)
        or (coalesce(GPFileName, "") =~ "perl" and coalesce(GPCommandLine, "") matches regex cpan_regex)
    | project TimeGenerated, DeviceName, AccountName,
              ShellFileName = FileName, ShellCommandLine = ProcessCommandLine,
              ToolFileName, ToolCommandLine,
              CpanAncestorFile = GPFileName, CpanAncestorCmd = GPCommandLine
    | order by TimeGenerated desc;
ShellsWithAncestors
// Optional: identify which tool most commonly spawns shells in this context
// | summarize ShellLaunches = count(), ExampleShellCmds = make_set(ShellCommandLine, 3) by DeviceName, ToolFileName, ToolCommandLine
// | order by ShellLaunches desc
pass2343.40s0.1685
gpt-5-mini-high
no answer
no query generatedfail50.00s0.0228
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0170
gpt-5-mini-medium
cpan
let perl_tools = dynamic(["cpan","cpanm","ppm","cpanp","cpan2dist","cpanplus","cpanminus"]);

let perl_procs = DeviceProcessEvents
| where isnotempty(FileName) and (tolower(FileName) has_any(perl_tools) or tolower(ProcessCommandLine) has_any(perl_tools))
| extend PerlTool = case(
    tolower(FileName) contains "cpanm" or tolower(ProcessCommandLine) contains "cpanm", "cpanm",
    tolower(FileName) contains "ppm" or tolower(ProcessCommandLine) contains "ppm", "ppm",
    tolower(FileName) contains "cpan" or tolower(ProcessCommandLine) contains "cpan", "cpan",
    "other")
| project PerlTime=TimeGenerated, DeviceId, DeviceName, AccountName, PerlProcId=ProcessId, PerlFileName=FileName, PerlCmd=ProcessCommandLine, PerlCreationTime=ProcessCreationTime, PerlTool;

let shells = DeviceProcessEvents
| where isnotempty(FileName) and (
    tolower(FileName) in ("sh","bash","zsh","dash","ksh")
    or tolower(ProcessCommandLine) contains "/bin/sh"
    or tolower(ProcessCommandLine) contains "/bin/bash"
    or tolower(ProcessCommandLine) contains "sh -c"
    or tolower(ProcessCommandLine) contains "bash -c"
    or tolower(ProcessCommandLine) contains "exec /bin/sh"
)
| extend InitiatingId = tostring(InitiatingProcessId), InitiatingFile = tostring(InitiatingProcessFileName)
| project ShellTime=TimeGenerated, DeviceId, DeviceName, ShellProcId=ProcessId, ShellFileName=FileName, ShellCmd=ProcessCommandLine, ShellCreationTime=ProcessCreationTime, InitiatingId, InitiatingFile;

perl_procs
| join kind=leftouter (shells) on DeviceId
| where (tostring(PerlProcId) == InitiatingId) or (tolower(InitiatingFile) has_any(perl_tools))
| project DeviceName, DeviceId, PerlTool, PerlFileName, PerlCmd, PerlProcId, PerlCreationTime, ShellFileName, ShellCmd, ShellProcId, ShellCreationTime, ShellTime, InitiatingFile
| summarize Count = count(), SampleShellCmd = any(ShellCmd), SamplePerlCmd = any(PerlCmd), FirstSeen = min(ShellTime), LastSeen = max(ShellTime) by PerlTool, PerlFileName, DeviceName, DeviceId, PerlProcId
| order by FirstSeen desc
| limit 500
pass5109.75s0.0237
gpt-5-nano-high
cpan
let startTime = ago(1d);
let endTime = now();
DeviceProcessEvents
| where TimeGenerated between (startTime .. endTime)
| where (tolower(InitiatingProcessFileName) contains "perl" or tolower(InitiatingProcessCommandLine) contains "perl" or tolower(InitiatingProcessFileName) contains "cpan" or tolower(InitiatingProcessCommandLine) contains "cpan" or tolower(InitiatingProcessFileName) contains "cpanm" or tolower(InitiatingProcessCommandLine) contains "cpanm")
| where (tolower(FileName) contains "sh" or tolower(FileName) contains "bash" or tolower(FileName) contains "zsh" or tolower(FileName) contains "dash" or tolower(FileName) contains "ash")
| project TimeGenerated, DeviceName, ShellFileName = FileName, ShellFolderPath = FolderPath, ShellCmdLine = ProcessCommandLine, ShellPid = ProcessId, ShellCreationTime = ProcessCreationTime, ToolName = InitiatingProcessFileName, ToolCmdLine = InitiatingProcessCommandLine, ToolProcessId = InitiatingProcessId, ParentCreated = InitiatingProcessCreationTime
| order by TimeGenerated desc
pass3128.13s0.0085
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0108
grok-3-beta
dash
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where (ProcessCommandLine contains 'perl' or ProcessCommandLine contains 'cpan')
| where (ProcessCommandLine contains 'sh' or ProcessCommandLine contains 'bash' or ProcessCommandLine contains 'shell')
| join kind=inner (
    DeviceInfo
    | where OSPlatform == 'Linux'
) on DeviceId
| project DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, ProcessCreationTime, DeviceId
| order by ProcessCreationTime desc
| limit 50
fail231.17s0.0644
grok-3-mini-beta
perl
DeviceProcessEvents
| where FolderPath startswith "/"
| where InitiatingProcessFileName has_any ("perl", "cpan", "cpanm")
| where ProcessCommandLine has_any ("/bin/sh", "system", "exec", "shell")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, ProcessId
| order by TimeGenerated desc
fail438.34s0.0059
o1-high
cpan
DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where InitiatingProcessCommandLine has_any ("perl", "cpan", "cpanm")
| where FileName in~ ("sh", "bash", "zsh", "ksh", "dash")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine
| order by TimeGenerated desc
pass132.64s0.2901
o1-low
cpan
DeviceProcessEvents
| where FileName in~ ("sh", "bash", "dash", "zsh", "csh", "ksh")
  // Identify newly created shell-like processes
| where InitiatingProcessFileName in~ ("cpan", "cpanm", "perl") or InitiatingProcessCommandLine has_any ("cpan", "cpanm")
  // Filter for shells spawned by cpan, cpanm, or perl
| project TimeGenerated, DeviceName, FolderPath, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, AccountName
| order by TimeGenerated desc
pass139.89s0.2824
o3-high
no answer
no query generatedfail50.00s0.1072
o3-mini-high
dash
DeviceProcessEvents
| where (tolower(InitiatingProcessFileName) has_any ("cpan", "cpanm") or tolower(InitiatingProcessCommandLine) has_any ("cpan", "cpanm"))
| where (tolower(FileName) has_any ("/bin/bash", "/bin/sh") or tolower(ProcessCommandLine) has_any ("/bin/bash", "/bin/sh", "bash", "sh"))
| project TimeGenerated, DeviceName, ProcessId, FileName, ProcessCommandLine, InitiatingProcessId, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by TimeGenerated desc
fail465.08s0.0374
o3-mini-low
cpan
DeviceProcessEvents
| where InitiatingProcessCommandLine contains "cpan"
| where ProcessCommandLine contains "/bin/sh" or ProcessCommandLine contains "bash"
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, ProcessCommandLine, ProcessCreationTime, ProcessId, InitiatingProcessId
| order by TimeGenerated desc
pass235.41s0.0187
o4-mini-high
no answer
no query generatedfail50.00s0.0434
o4-mini-low
no answer
no query generatedfail50.00s0.0485