Question Analysis
An attacker on a Linux host may try to enumerate installed shells by reading the system file that lists valid shells. Using process or syslog data, determine which command was executed to perform this enumeration.
bf23c7dc-1004-4949-8262-4c1d1ef87702
Question ID
16/23
Models Correct
1
Valid Answer
70%
Success Rate
Reference Answer
Human-validated correct answer for this question
cat /etc/shells
Model Performance Comparison
See how different AI models performed on this question
Model | Answer | KQL Query | Status | Attempts | Time | Cost |
---|---|---|---|---|---|---|
gemini-2.5-flash-preview-04-17 | No answer provided | No query generated | Correct | 5 | 0.00s | 0.0363 |
gpt-35-turbo | cat | DeviceProcessEvents
| where ProcessCommandLine contains 'cat /etc/shells' or ProcessCommandLine contains 'less /etc/shells' or ProcessCommandLine contains 'more /etc/shells' | Incorrect | 1 | 3.47s | 0.0042 |
gpt-4-turbo-2024-04-09 | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.2278 |
gpt-4.1 | cat /etc/shells | DeviceProcessEvents
| where ProcessCommandLine has "/etc/shells"
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessCommandLine | Correct | 1 | 7.90s | 0.0115 |
gpt-4.1-finetuned | cat /etc/shells | DeviceProcessEvents
| where AccountDomain != ""
| where AccountName != ""
//Detect direct `/bin/sh /etc/shells` and similar, or indirect `/etc/shells |awk -F: '/bin/{print $1}'` and similar
| where (ProcessCommandLine in~ ("cat /etc/shells", "more /etc/shells", "less /etc/shells", "tail /etc/shells", "head /etc/shells", "awk -F: '/bin/{print $1}' /etc/shells", "/etc/shells |awk -F: '/bin/{print $1}'", "/etc/shells |awk -F:' '{print $1}'", "/bin/{print ", "|awk -F: '/bin/'") and ProcessCommandLine contains "/etc/shells")
or (ProcessCommandLine has "/etc/shells" and InitiatingProcessFileName == "/bin/sh" )
//Raise if OS identified as Linux.
| summarize count() by bin(TimeGenerated, 1h), AccountName, InitiatingProcessAccountSid, InitiatingProcessCommandLine, InitiatingProcessFileName, DeviceName, InitiatingProcessId, ProcessCommandLine,ProcessId, Type, InitiatingProcessParentFileName
| extend timestamp = bin(TimeGenerated, 1h)
| extend Account_0_Name = AccountName
| extend Account_0_Sid = InitiatingProcessAccountSid
| extend Host_0_HostName = DeviceName
| extend Process_0_ProcessId = ProcessId
| extend Process_0_CommandLine = InitiatingProcessCommandLine
| extend Process_1_ProcessId = InitiatingProcessId
| extend Process_1_ProcessId = ProcessId
| extend Process_1_CommandLine = ProcessCommandLine
| extend Process_0_FileName = InitiatingProcessParentFileName
| extend Type = strcat(Type, "_" , InitiatingProcessParentFileName) | Correct | 2 | 25.41s | 0.0258 |
gpt-4.1-mini | cat /etc/shells | DeviceProcessEvents
| where ProcessCommandLine contains "/etc/shells" or FileName contains "etc/shells"
| project DeviceName, AccountName, ProcessCommandLine, TimeGenerated
| sort by TimeGenerated desc | Correct | 1 | 28.28s | 0.0022 |
gpt-4.1-nano | cat /etc/shells | DeviceProcessEvents
| where ProcessCommandLine has_any ('cat', 'less', 'more', 'head')
| where ProcessCommandLine contains '/etc/shells'
| project TimeGenerated, DeviceName, ProcessCommandLine | Correct | 2 | 5.52s | 0.0010 |
gpt-4o | cat /etc/shells | let suspicious_file="/etc/shells";
DeviceProcessEvents
| where ProcessCommandLine has suspicious_file or InitiatingProcessCommandLine has suspicious_file
| project TimeGenerated, DeviceId, DeviceName, AccountName, InitiatingProcessCommandLine, InitiatingProcessCreationTime | Correct | 2 | 28.09s | 0.0289 |
gpt-5-high | cat | let di = DeviceInfo
| summarize arg_max(TimeGenerated, OSPlatform) by DeviceId
| project DeviceId, OSPlatform;
union isfuzzy=true
(
DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where ProcessCommandLine contains "/etc/shells" or ProcessCommandLine contains "getent shells"
or InitiatingProcessCommandLine contains "/etc/shells" or InitiatingProcessCommandLine contains "getent shells"
| extend SourceTable = "DeviceProcessEvents"
),
(
DeviceEvents
| where ProcessCommandLine contains "/etc/shells" or ProcessCommandLine contains "getent shells"
or InitiatingProcessCommandLine contains "/etc/shells" or InitiatingProcessCommandLine contains "getent shells"
| extend SourceTable = "DeviceEvents"
)
| join kind=inner di on DeviceId
| where OSPlatform =~ "Linux"
| extend EnumerationMethod = case(
ProcessCommandLine contains "/etc/shells" or InitiatingProcessCommandLine contains "/etc/shells", "Direct read of /etc/shells",
ProcessCommandLine contains "getent shells" or InitiatingProcessCommandLine contains "getent shells", "getent shells via NSS",
"Other"
)
| project TimeGenerated, DeviceName, AccountName, SourceTable, ActionType, FileName,
ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessParentFileName, InitiatingProcessParentId, ProcessId,
DeviceId, MachineGroup, EnumerationMethod
| order by TimeGenerated desc | Incorrect | 1 | 95.13s | 0.0539 |
gpt-5-mini-high | cat /etc/shells | let toolsPattern = "(?i)\b(cat|less|more|tail|head|awk|sed|grep|strings|cut|xargs|stat|ls|getent|python|perl|ruby|sh|bash)\b";
// normalize candidate command field from both tables
let proc = DeviceProcessEvents
| extend Cmd = coalesce(ProcessCommandLine, InitiatingProcessCommandLine, tostring(AdditionalFields.CommandLine), tostring(AdditionalFields.command), tostring(AdditionalFields))
| where isnotempty(Cmd)
| where tolower(Cmd) contains "/etc/shells" or tolower(Cmd) contains "etc/shells" or (Cmd matches regex toolsPattern and tolower(Cmd) contains "/etc/")
| extend Tool = tolower(extract(toolsPattern, 1, Cmd))
| project TimeGenerated, DeviceName, DeviceId, AccountName, InitiatingProcessFileName, FileName, ProcessId, Tool, Cmd;
let dev = DeviceEvents
| extend Cmd = coalesce(ProcessCommandLine, InitiatingProcessCommandLine, tostring(AdditionalFields.CommandLine), tostring(AdditionalFields.command), tostring(AdditionalFields))
| where isnotempty(Cmd)
| where tolower(Cmd) contains "/etc/shells" or tolower(Cmd) contains "etc/shells" or (Cmd matches regex toolsPattern and tolower(Cmd) contains "/etc/")
| extend Tool = tolower(extract(toolsPattern, 1, Cmd))
| project TimeGenerated, DeviceName, DeviceId, AccountName, InitiatingProcessFileName, FileName, ProcessId, Tool, Cmd;
(proc
| union dev)
// restrict to Linux machines to reduce noise and volume (adjust/remove as needed)
| join kind=inner (
DeviceInfo
| where isnotempty(OSPlatform) and tolower(OSPlatform) contains "linux"
| project DeviceId
) on DeviceId
| summarize Count = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by DeviceName, DeviceId, AccountName, Tool, Cmd
| order by LastSeen desc
| top 200 by LastSeen
| project DeviceName, DeviceId, AccountName, Tool=coalesce(Tool, "(not-extracted)"), CommandLine=Cmd, Count, FirstSeen, LastSeen
| Correct | 5 | 93.99s | 0.0250 |
gpt-5-mini-low | cat /etc/shells | // Search for enumeration of the shells file (/etc/shells) across process and file/device events
// Process events: commands that reference /etc/shells or use common read utilities
let proc_hits = DeviceProcessEvents
| where isnotempty(ProcessCommandLine)
| where ProcessCommandLine has "/etc/shells"
or (ProcessCommandLine has "cat" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "grep" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "less" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "more" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "awk" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "sed" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "head" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "tail" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "strings" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "python" and ProcessCommandLine has "shells")
or (ProcessCommandLine has "perl" and ProcessCommandLine has "shells")
| project TimeGenerated, DeviceName, Account = InitiatingProcessAccountName, ProcessId, ParentProcessId = InitiatingProcessParentId, ProcessCommandLine, ProcessFileName = InitiatingProcessFileName, SourceTable = "DeviceProcessEvents";
// File events: file reads or references to /etc/shells
let file_hits = DeviceFileEvents
| where (isnotempty(FileName) and FileName has "shells")
or (isnotempty(FolderPath) and FolderPath has "/etc/shells")
or (isnotempty(PreviousFileName) and PreviousFileName has "shells")
or (isnotempty(AdditionalFields) and tostring(AdditionalFields) has "/etc/shells")
| project TimeGenerated, DeviceName, Account = InitiatingProcessAccountName, ProcessId = InitiatingProcessId, ParentProcessId = InitiatingProcessParentId, ProcessCommandLine = InitiatingProcessCommandLine, FileName, FolderPath, ActionType, SourceTable = "DeviceFileEvents";
// Generic device events (syslog-like) that may reference /etc/shells in AdditionalFields or file fields
let evt_hits = DeviceEvents
| where (isnotempty(FileName) and FileName has "shells")
or (isnotempty(FolderPath) and FolderPath has "/etc/shells")
or (isnotempty(AdditionalFields) and tostring(AdditionalFields) has "/etc/shells")
| project TimeGenerated, DeviceName, Account = AccountName, ProcessId = InitiatingProcessId, ParentProcessId = InitiatingProcessParentId, ProcessCommandLine = InitiatingProcessCommandLine, FileName, FolderPath, ActionType, SourceTable = "DeviceEvents";
// Combine, normalize and summarize for investigation
proc_hits
| union file_hits
| union evt_hits
| extend ProcessCommandLine = coalesce(ProcessCommandLine, strcat(FileName, " ", FolderPath, " ", tostring(ActionType)))
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), Count = count(), ExampleDevices = make_set(DeviceName, 5), ExampleCommands = make_set(ProcessCommandLine, 10), ExampleFiles = make_set(FileName, 5) by Account = coalesce(Account, "<unknown>"), SourceTable, ProcessId, ParentProcessId
| order by LastSeen desc
| limit 500
| Correct | 3 | 60.37s | 0.0143 |
gpt-5-mini-medium | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0160 |
gpt-5-nano-high | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0073 |
gpt-5-nano-medium | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0080 |
grok-3-beta | cat /etc/shells | DeviceProcessEvents
| where ProcessCommandLine has '/etc/shells' or ProcessCommandLine has 'cat /etc/shells' or ProcessCommandLine has 'more /etc/shells' or ProcessCommandLine has 'less /etc/shells' or ProcessCommandLine has 'nano /etc/shells' or ProcessCommandLine has 'vim /etc/shells' or ProcessCommandLine has 'vi /etc/shells'
| join kind=inner (
DeviceInfo
| where OSPlatform has 'Linux' or OSDistribution has 'Linux'
) on DeviceId
| project TimeGenerated, DeviceName, ProcessCommandLine, AccountName, InitiatingProcessAccountName, InitiatingProcessCommandLine
| order by TimeGenerated desc | Correct | 3 | 40.73s | 0.0617 |
grok-3-mini-beta | cat /etc/shells | DeviceProcessEvents
| where ProcessCommandLine contains '/etc/shells'
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine
| order by TimeGenerated desc | Correct | 1 | 15.66s | 0.0016 |
o1-high | cat | DeviceProcessEvents
| join kind=inner (
DeviceInfo
| where OSPlatform == "Linux"
) on DeviceId
| where ProcessCommandLine has "/etc/shells"
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine | Correct | 2 | 73.09s | 0.3824 |
o1-low | cat | DeviceInfo
| where OSPlatform == "Linux"
| join (
DeviceProcessEvents
| where ProcessCommandLine has "/etc/shells"
) on DeviceId
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| order by TimeGenerated desc | Correct | 1 | 30.13s | 0.2281 |
o3-high | cat | // Detect enumeration of interactive shells on Linux hosts
let lookback = 7d; // will be overwritten by portal / function-caller
let shell_viewers = dynamic(["cat","less","more","head","tail","awk","sed","grep","cut","strings"]);
DeviceProcessEvents
| where TimeGenerated >= ago(lookback)
// ---- Step 1 : narrow down to commands that reference the shells file ----
| where ProcessCommandLine has "/etc/shells"
or (FileName =~ "getent" and ProcessCommandLine has "shells")
or (FileName in~ (shell_viewers) and ProcessCommandLine has "shells")
// ---- Step 2 : Pick the interesting fields for triage ----
| project TimeGenerated,
DeviceName,
DeviceId,
AccountName,
AccountDomain,
FileName,
ProcessCommandLine,
ParentProcessName = InitiatingProcessFileName,
ParentProcessCommandLine = InitiatingProcessCommandLine,
ParentAccount = strcat(InitiatingProcessAccountDomain, "\\", InitiatingProcessAccountName),
RemoteSession = iff(IsInitiatingProcessRemoteSession, "Yes", "No"),
ReportId
// ---- Step 3 : Optional grouping to reduce noise ----
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), EventCount=count()
by DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine,
ParentProcessName, ParentProcessCommandLine, ParentAccount, RemoteSession, ReportId
| order by LastSeen desc | Incorrect | 1 | 47.06s | 0.0275 |
o3-mini-high | cat | union DeviceProcessEvents, DeviceEvents
| where tolower(InitiatingProcessCommandLine) has "/etc/shells"
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, ActionType, DeviceId
| sort by TimeGenerated desc | Correct | 1 | 11.48s | 0.0113 |
o3-mini-low | cat | DeviceProcessEvents
| where InitiatingProcessCommandLine contains "/etc/shells"
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, FileName, FolderPath
| sort by TimeGenerated desc | Correct | 1 | 31.35s | 0.0113 |
o4-mini-high | cat /etc/shells | let ShellProcessEvents = DeviceProcessEvents
| where ProcessCommandLine matches regex @"\b(cat|grep|awk|sed|less|more|tail|head)\b.*\/etc\/shells"
| project TimeGenerated, DeviceName, AccountName=InitiatingProcessAccountName, CommandLine=ProcessCommandLine;
// If syslog data is available, look for raw slash etc shells reads in logs
let ShellSyslogEvents = Syslog
| where SyslogMessage has "/etc/shells"
| project TimeGenerated, DeviceName=HostName, AccountName="(unknown)", CommandLine=SyslogMessage;
// Combine both sources for a full view
ShellProcessEvents
| union ShellSyslogEvents
| sort by TimeGenerated desc | Correct | 1 | 44.35s | 0.0155 |
o4-mini-low | cat /etc/shells | let ShellFilePath = "/etc/shells";
// 1) Find any processes that reference /etc/shells in their command line
let ProcHits = DeviceProcessEvents
| where ProcessCommandLine contains ShellFilePath
| project TimeGenerated, DeviceName, AccountName, CommandLine = ProcessCommandLine;
// 2) Find any file‐access events reading the /etc/shells file
let FileHits = DeviceFileEvents
| where FileName == "shells" and FolderPath endswith "/etc"
| where ActionType has "Read"
| project TimeGenerated, DeviceName, AccountName = InitiatingProcessAccountName, CommandLine = InitiatingProcessCommandLine;
// Combine and sort
union ProcHits, FileHits
| sort by TimeGenerated desc | Correct | 1 | 31.60s | 0.0097 |