// question

On a Linux system, logs show that the password expiration settings file was accessed. Identify which command was executed to list its contents.

Reference KQL answer and model-by-model comparison.

7c86c55c-70fa-4a05-83c9-3aa19b145d1a
question id
8/23
models correct
1
valid answer
35%
success rate
// reference answer

Human-validated correct answer for this question.

cat /etc/login.defs
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
no answer
no query generatedfail50.00s0.0173
gpt-35-turbo
no answer
no query generatedfail50.00s0.0109
gpt-4-turbo-2024-04-09
no answer
no query generatedfail50.00s0.2236
gpt-4.1
cat /etc/login.defs
DeviceProcessEvents
| where ProcessCommandLine has "/etc/login.defs"
| where FileName in ("cat", "less", "more", "head", "tail", "grep", "awk", "sed", "view", "vi", "nano")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by TimeGenerated desc
pass418.60s0.0368
gpt-4.1-finetuned
bash -c "cat /etc/login.defs"
let suspect_processes = dynamic(["cat", "less", "more", "strings", "od", "tail", "head", "base64"]);
DeviceProcessEvents
| where FolderPath has_any (suspect_processes) 
| where ProcessCommandLine has_any ("/etc/login.defs", "/usr/bin/chage -l", "/sbin/chage -l") 
| where ProcessCommandLine !has "/bin/bash"
| extend ArgumentMatch = iff(FolderPath !in ("cat", "less", "more", "strings", "od", "tail", "head"),"[not shell org]", "")
| extend ArgumentMatch = iff(isempty(ArgumentMatch), extract(@'(\\s+)? /etc/login\\.defs(\\s+|-|$)', 0, ProcessCommandLine), ArgumentMatch)
| extend ArgumentMatch = iff(isempty(ArgumentMatch), extract(@'(cat|less|more|od|tail|head|strings|base64) (/etc/login\\.defs)?(\\s+|-|$)', 0, ProcessCommandLine), ArgumentMatch)
| extend ArgumentMatch = iff(isempty(ArgumentMatch), extract(@'/usr/bin/chage -l', 0, ProcessCommandLine), ArgumentMatch)
| extend ArgumentMatch = iff(isempty(ArgumentMatch), extract(@'/sbin/chage -l', 0, ProcessCommandLine), ArgumentMatch)
| extend ArgumentMatch = iff(isempty(ArgumentMatch), extract(@'chage -l', 0, ProcessCommandLine), ArgumentMatch)
| where isnotempty(ArgumentMatch)
| order by DeviceName asc, TimeGenerated asc, ProcessCommandLine asc
| extend timestamp = TimeGenerated, HostCustomEntity = DeviceName, AccountCustomEntity = InitiatingProcessAccountName
pass573.07s0.0456
gpt-4.1-mini
no answer
no query generatedfail50.00s0.0081
gpt-4.1-nano
no answer
no query generatedfail50.00s0.0015
gpt-4o
no answer
no query generatedfail50.00s0.0527
gpt-5-high
cat
let targetFiles = dynamic(["/etc/login.defs", "/etc/default/useradd"]);
let viewer_pattern = @"(?i)\b(cat|less|more|head|tail|grep|awk|sed|strings|vi|vim|nano|view)\b";
let linux_devices = DeviceInfo
| where OSPlatform =~ "Linux"
| distinct DeviceId;
DeviceProcessEvents
| where DeviceId in (linux_devices)
| where ActionType == "ProcessCreated"
| where ProcessCommandLine has_any (targetFiles) or InitiatingProcessCommandLine has_any (targetFiles)
| where FileName in~ ("cat","less","more","head","tail","grep","awk","sed","strings","vi","vim","nano","view")
    or ProcessCommandLine matches regex viewer_pattern
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessParentFileName, InitiatingProcessCommandLine
| order by TimeGenerated desc
fail2175.19s0.0910
gpt-5-mini-high
no answer
no query generatedfail50.00s0.0233
gpt-5-mini-low
cat
let target = "/etc/login.defs";
let linuxDevices = DeviceInfo
| where isnotempty(OSPlatform) and tolower(OSPlatform) has "linux"
| project DeviceId, DeviceName;

// File events that directly reference the file path
let fileEvents = DeviceFileEvents
| extend FullPath = strcat(FolderPath, "/", FileName)
| where (tolower(FullPath) contains tolower(target) or tolower(FileName) == "login.defs")
// Common file access actions
| where ActionType has_any ("FileOpened","FileRead","FileAccessed","FileWasRead","FileModified","FileCreated","FileWrite")
| project TimeGenerated, SourceTable = "DeviceFileEvents", DeviceId, DeviceName, FileName, FolderPath, FullPath, ActionType, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessId, InitiatingProcessAccountName;

// Process events where the commandline references the file
let procEvents = DeviceProcessEvents
| where tolower(ProcessCommandLine) contains tolower(target) or tolower(ProcessCommandLine) contains "login.defs"
| project TimeGenerated, SourceTable = "DeviceProcessEvents", DeviceId, DeviceName, FileName = FileName, FolderPath = FolderPath, FullPath = "", ActionType = ActionType, InitiatingProcessFileName = FileName, InitiatingProcessCommandLine = ProcessCommandLine, InitiatingProcessId = ProcessId, InitiatingProcessAccountName = InitiatingProcessAccountName;

union fileEvents, procEvents
| join kind=inner linuxDevices on DeviceId
| extend CommandLine = tostring(coalesce(InitiatingProcessCommandLine, ""))
| extend ProcFile = tostring(coalesce(InitiatingProcessFileName, ""))
| extend ProcBasename = extract(@"([^/]+)$", 1, ProcFile)
| extend FirstToken = extract(@"^\s*([^\s]+)", 1, CommandLine)
| extend LikelyCommand = iff(isnotempty(FirstToken), extract(@"([^/]+)$", 1, FirstToken), ProcBasename)
| project TimeGenerated, DeviceName, DeviceId, FullPath, FolderPath, FileName, ActionType, LikelyCommand, CommandLine, InitiatingProcessId, InitiatingProcessAccountName, SourceTable
| order by TimeGenerated desc

// Provide a summary of most frequent commands seen against the file (optional)
| union (
    union fileEvents, procEvents
    | join kind=inner linuxDevices on DeviceId
    | extend CommandLine = tostring(coalesce(InitiatingProcessCommandLine, ""))
    | extend ProcFile = tostring(coalesce(InitiatingProcessFileName, ""))
    | extend ProcBasename = extract(@"([^/]+)$", 1, ProcFile)
    | extend FirstToken = extract(@"^\s*([^\s]+)", 1, CommandLine)
    | extend LikelyCommand = iff(isnotempty(FirstToken), extract(@"([^/]+)$", 1, FirstToken), ProcBasename)
    | summarize Count = count() by LikelyCommand
    | order by Count desc
)
fail241.25s0.0111
gpt-5-mini-medium
cat /etc/login.defs
// Identify commands used to view password expiration / account files on Linux
let viewCmds = dynamic(["cat","less","more","tac","tail","head","awk","sed","grep","strings","cut","nl","od","xxd","tr","python","perl","ruby","bash","sh","zsh"]);
// File-based events (explicit file access)
let fileEvents = DeviceFileEvents
| where isnotempty(FileName) or isnotempty(FolderPath)
| extend FullPath = iif(isempty(FolderPath), tostring(FileName), strcat(FolderPath, iff(FolderPath endswith "/", "", "/"), FileName))
| where tolower(FullPath) contains "login.defs" or tolower(FullPath) contains "etc/shadow" or tolower(FullPath) contains "etc/passwd" or tolower(FullPath) contains "opasswd" or tolower(FullPath) contains "shadow-"
| where ActionType has_any ("FileOpened","FileRead","FileModified","FileCreated","FileAccessed","Read","Open","Accessed","ReadData")
| extend CommandLine = tostring(InitiatingProcessCommandLine), Proc = tostring(InitiatingProcessFileName), PID = InitiatingProcessId
| where isnotempty(CommandLine) or isnotempty(Proc)
| project TimeGenerated, DeviceName, DeviceId, FullPath, ActionType, Proc, PID, CommandLine;

// Process events where command line references the target files
let processEvents = DeviceProcessEvents
| where isnotempty(ProcessCommandLine)
| where tolower(ProcessCommandLine) contains "login.defs" or tolower(ProcessCommandLine) contains "etc/shadow" or tolower(ProcessCommandLine) contains "etc/passwd" or tolower(ProcessCommandLine) contains "opasswd" or tolower(ProcessCommandLine) contains "shadow-"
| extend CommandLine = tostring(ProcessCommandLine), Proc = tostring(FileName), PID = ProcessId
| project TimeGenerated, DeviceName, DeviceId, FullPath = "(in cmd)", ActionType = "ProcessCmd", Proc, PID, CommandLine;

// General device events (processes that initiated an action referencing the files)
let deviceEvents = DeviceEvents
| where isnotempty(InitiatingProcessCommandLine)
| where tolower(InitiatingProcessCommandLine) contains "login.defs" or tolower(InitiatingProcessCommandLine) contains "etc/shadow" or tolower(InitiatingProcessCommandLine) contains "etc/passwd" or tolower(InitiatingProcessCommandLine) contains "opasswd" or tolower(InitiatingProcessCommandLine) contains "shadow-"
| extend CommandLine = tostring(InitiatingProcessCommandLine), Proc = tostring(InitiatingProcessFileName), PID = InitiatingProcessId
| project TimeGenerated, DeviceName, DeviceId, FullPath = "(in cmd)", ActionType = "DeviceEventCmd", Proc, PID, CommandLine;

union fileEvents, processEvents, deviceEvents
| extend CmdVerb = extract(@"^\s*([^ \t]+)", 1, CommandLine)
| extend CmdVerbLower = tolower(CmdVerb)
| extend IsView = CmdVerbLower in~ (viewCmds)
| project TimeGenerated, DeviceName, DeviceId, FullPath, ActionType, Proc, PID, CommandLine, CmdVerb, IsView
| order by TimeGenerated desc
| distinct DeviceName, PID, CommandLine, CmdVerb, FullPath, IsView, ActionType, Proc
| take 200
pass470.29s0.0173
gpt-5-nano-high
no answer
no query generatedfail50.00s0.0080
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0075
grok-3-beta
no answer
no query generatedfail50.00s0.0875
grok-3-mini-beta
bash -c "cat /etc/login.defs"
DeviceProcessEvents
| where InitiatingProcessCommandLine has '/etc/login.defs'
| join kind=inner (
    DeviceInfo
    | where OSPlatform == 'Linux'
    | project DeviceId, DeviceName
  ) on DeviceId
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine
| order by TimeGenerated desc
| take 10
pass552.76s0.0072
o1-high
cat
DeviceProcessEvents
| join kind=inner (
    DeviceInfo
    | where OSPlatform =~ "Linux"
    | project DeviceId
) on DeviceId
| where ProcessCommandLine has_any ("/etc/login.defs", "/etc/shadow")
| project TimeGenerated, DeviceName, FolderPath, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| order by TimeGenerated desc
pass267.80s0.6180
o1-low
cat
DeviceProcessEvents
| where Timestamp between (startofday(ago(7d))..now())
| join kind=inner (
    DeviceInfo
    | where OSPlatform == "Linux"
) on DeviceId
| where ProcessCommandLine has "/etc/login.defs"
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine
| order by TimeGenerated desc
pass250.03s0.3705
o3-high
cat
// --- Commands that displayed the contents of the Linux password-expiration settings file
let password_file_path = "/etc/login.defs";

// 1. Identify Linux devices in the tenant
let linux_devices =  DeviceInfo
| where OSPlatform =~ "Linux"
| project DeviceId, DeviceName;

// 2. Look for any process execution whose command-line references the target file
DeviceProcessEvents
| where DeviceId in (linux_devices)
| where ProcessCommandLine has_cs password_file_path        // case-sensitive match for exact path
// 3. Return the useful details
| summarize EarliestExec = min(TimeGenerated),
            LatestExec   = max(TimeGenerated),
            ExecCount    = count()
          by DeviceName,
             AccountName,
             ProcessFileName = FileName,
             ProcessCommandLine
| order by LatestExec desc
fail286.85s0.0564
o3-mini-high
cat
DeviceProcessEvents
| where InitiatingProcessCommandLine matches regex @"(?i).*(cat|more|less|head).*login.defs.*"
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine
| sort by TimeGenerated desc
pass116.84s0.0115
o3-mini-low
cat
DeviceProcessEvents
| where ProcessCommandLine contains "/etc/login.defs"
// Optionally, further filter if you expect commands like 'cat', 'less', etc. Uncomment the following line if needed:
// | where ProcessCommandLine has_any("cat", "less", "more", "head", "tail")
| project TimeGenerated, ProcessCommandLine
| distinct ProcessCommandLine
pass234.21s0.0265
o4-mini-high
no answer
no query generatedfail50.00s0.0415
o4-mini-low
no answer
no query generatedfail50.00s0.0357