// question

On a Linux host, process execution logs show a chmod invocation with a recursive flag. Which file or folder was targeted by this recursive permission change?

Reference KQL answer and model-by-model comparison.

ea79f937-4a4d-4348-ace6-9916aec453a4
question id
18/23
models correct
1
valid answer
78%
success rate
// reference answer

Human-validated correct answer for this question.

T1222.002
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where FileName == "chmod"
| where ProcessCommandLine has "-R" or ProcessCommandLine has "-r"
// Extract the target path assuming it's the first sequence of non-space characters after the recursive flag and a space.
// This regex (?:-R|-r)\s+(\S+) matches:
// (?:-R|-r) - a non-capturing group for either "-R" or "-r"
// \s+       - one or more whitespace characters
// (\S+)     - one or more non-whitespace characters (this is captured in group 1)
| extend TargetedPath = extract(@"(?:-R|-r)\s+(\S+)", 1, ProcessCommandLine)
| where isnotempty(TargetedPath) // Filter out events where extraction failed or didn't find a target
| project Timestamp, DeviceName, InitiatingProcessCommandLine, ProcessCommandLine, TargetedPath, AccountName, AccountDomain
pass125.72s0.0131
gpt-35-turbo
no answer
no query generatedfail50.00s0.0110
gpt-4-turbo-2024-04-09
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where InitiatingProcessCommandLine contains 'chmod' and InitiatingProcessCommandLine contains '-R'
| project Timestamp, DeviceName, InitiatingProcessCommandLine, FolderPath
pass112.89s0.1258
gpt-4.1
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where FileName == "chmod" or ProcessCommandLine has "chmod"
| where ProcessCommandLine has "-R" or ProcessCommandLine has "--recursive"
| extend TargetPath = extract("chmod .*-R(?:[ =]?|[[:space:]]+| --recursive[ =]?)([^ ]+)", 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, AccountName, AccountDomain, ProcessCommandLine, TargetPath
| order by TimeGenerated desc
pass16.75s0.0144
gpt-4.1-finetuned
no answer
no query generatedfail50.00s0.0514
gpt-4.1-mini
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where FileName == "chmod" and (ProcessCommandLine has "-R" or ProcessCommandLine has "--recursive")
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, TargetPath = strcat(FolderPath, "/", FileName), ProcessCommandLine
| order by TimeGenerated desc
pass222.23s0.0041
gpt-4.1-nano
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where ActionType == 'ProcessCreated' and FileName endswith 'chmod'
| where ProcessCommandLine contains '-R' or ProcessCommandLine contains '--recursive'
| extend TargetPath = extract(@'chmod\s+[\S]+\s+(\S+)', 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, InitiatingProcessFileName, ProcessCommandLine, TargetPath
| where isnotempty(TargetPath)
| take 10
pass25.71s0.0007
gpt-4o
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where InitiatingProcessCommandLine contains "chmod" and InitiatingProcessCommandLine contains "-R"
| extend TargetFileOrFolder = extract(@"chmod\s.*\-R\s(\S+)", 1, InitiatingProcessCommandLine)
| project TimeGenerated, DeviceName, TargetFileOrFolder, InitiatingProcessCommandLine
pass330.53s0.0551
gpt-5-high
/tmp/AtomicRedTeam/atomics/T1222.002
let LinuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| project DeviceId, DeviceName;
DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where FileName =~ "chmod"
| where ProcessCommandLine has_any ("-R", "--recursive")
| join kind=inner (LinuxDevices) on DeviceId
// Get everything after the recursive flag
| extend afterR = coalesce(
    extract(@"--recursive(?:\s+|=)(.*)$", 1, ProcessCommandLine),
    extract(@"-R\s+(.*)$", 1, ProcessCommandLine)
)
| where isnotempty(afterR)
// From the substring, remove further options and the mode, leaving the targets
| extend targets_str = extract(@"^(?:\s*-[^\s]+\s+)*\s*(?:--\s+)?(?:[0-7]{3,4}|[ugoa]*[-+=][rwxXstugo]+(?:,[ugoa]*[-+=][rwxXstugo]+)*)\s+(.*)$", 1, tostring(afterR))
| extend targets_str = iff(isempty(targets_str), tostring(afterR), targets_str)
// Best-effort split to list individual targets (quoted paths with spaces may split)
| extend Targets = split(trim(" ", tostring(targets_str)), " ")
| extend FirstTarget = tostring(Targets[0])
| project TimeGenerated, DeviceName, DeviceId, AccountName, ProcessId, FirstTarget, Targets, TargetsText=tostring(targets_str), ProcessCommandLine
| where isnotempty(FirstTarget)
| order by TimeGenerated desc
pass2238.51s0.1162
gpt-5-mini-high
/tmp/AtomicRedTeam/atomics/T1222.002
union DeviceProcessEvents, DeviceEvents, DeviceFileEvents
| extend cmd = tostring(coalesce(ProcessCommandLine, InitiatingProcessCommandLine, tostring(AdditionalFields["CommandLine"])))
| where isnotempty(cmd)
// ensure this is a chmod invocation
| where tolower(cmd) contains "chmod"
// ensure a recursive flag is present (-r or -R or --recursive)
| where tolower(cmd) matches regex @"\bchmod\b.*(?:\s-[^\s]*r[^\s]*\b|--recursive\b)"
// split the command into tokens and normalize
| extend tokens = split(cmd, ' ')
| mv-expand token = tokens to typeof(string)
| extend token = trim('\'"`', tostring(token))
// keep tokens that look like paths and exclude flags/options
| where token != '' and not(token startswith('-'))
  and (token startswith('/') or token startswith('./') or token startswith('~/') or token contains('/'))
// summarize candidate targets per device/process/command
| summarize Targets = make_set(token), TargetCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by DeviceName, ProcessId, InitiatingProcessFileName, InitiatingProcessAccountName, cmd
| project FirstSeen, LastSeen, DeviceName, ProcessId, InitiatingProcessFileName, InitiatingProcessAccountName, cmd, Targets, TargetCount
| order by LastSeen desc
| take 250
pass364.49s0.0155
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0147
gpt-5-mini-medium
no answer
no query generatedfail50.00s0.0211
gpt-5-nano-high
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where InitiatingProcessCommandLine contains "chmod" and (InitiatingProcessCommandLine contains "-R" or InitiatingProcessCommandLine contains "--recursive")
| extend TargetPath = coalesce(
    extract(@"chmod\\s+-R\\s+(?:\\d+\\s+)?([^\\s]+)", 1, InitiatingProcessCommandLine),
    extract(@"chmod\\s+-R\\s+([^\\s]+)", 1, InitiatingProcessCommandLine),
    FolderPath,
    FileName
)
| where isnotempty(TargetPath)
| distinct TargetPath, InitiatingProcessCreationTime, InitiatingProcessId, InitiatingProcessCommandLine, FolderPath, FileName, TimeGenerated
| project InitiatingProcessCreationTime, InitiatingProcessId, InitiatingProcessCommandLine, TargetPath, FolderPath, FileName, TimeGenerated
pass141.97s0.0030
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0088
grok-3-beta
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where ProcessCommandLine has 'chmod' and (ProcessCommandLine has '-R' or ProcessCommandLine has '--recursive')
| join kind=inner DeviceInfo on DeviceId
| where OSPlatform contains 'Linux'
| extend TargetPath = extract('chmod[^>]*[ ]+[^ ]*[ ]+([^ ]+)', 1, ProcessCommandLine)
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, TargetPath
| order by Timestamp desc
pass110.84s0.0435
grok-3-mini-beta
/tmp/AtomicRedTeam/atomics/T1222.002
let LinuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId;
DeviceProcessEvents
| where DeviceId in (LinuxDevices)
| where ProcessCommandLine contains "chmod" and ProcessCommandLine contains "-R"
| extend TargetPath = extract(@"chmod\s+-R\s+\\S+\\s+([^\\s]+)", 1, ProcessCommandLine)  // Extracts the path after chmod -R and its arguments
| project Timestamp, DeviceName, ProcessCommandLine, TargetPath
| order by Timestamp desc
| limit 100  // Limits results for performance; adjust as needed
pass121.76s0.0031
o1-high
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where DeviceId in (DeviceInfo
    | where OSPlatform == "Linux"
    | distinct DeviceId)
| where FileName == "chmod"
| where ProcessCommandLine contains "-R" or ProcessCommandLine contains "--recursive"
// Attempt to capture the last command line argument as the target path:
| extend TargetPath = extract(@"(/\S+)$", 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, AccountName, FolderPath, ProcessCommandLine, TargetPath
| order by TimeGenerated desc
pass140.60s0.2810
o1-low
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId
| join (
    DeviceProcessEvents
    | where ActionType == "ProcessCreated"
    | where FileName =~ "chmod"
    | where ProcessCommandLine has_any ("-R", "--recursive")
) on DeviceId
| project TimeGenerated, DeviceName, FolderPath, ProcessCommandLine
pass138.95s0.2636
o3-high
/tmp/AtomicRedTeam/atomics/T1222.002
// List of Linux devices
let linux_devices = DeviceInfo
| where OSPlatform startswith "Linux"
| project DeviceId;
// Look for chmod commands that used the recursive flag and extract the target path
DeviceProcessEvents
| where DeviceId in (linux_devices)
| where FileName == "chmod"
| where ProcessCommandLine matches regex @"\s-[rR]\b"   // ensure -R / -r was present
| extend SplitCmd = split(ProcessCommandLine, " ")
| extend TargetPathRaw = tostring(SplitCmd[-1])            // last argument should be target file/folder
| extend TargetPath = trim("\"' ", TargetPathRaw)       // remove surrounding quotes or spaces
| project TimeGenerated,
          DeviceName,
          InitiatingProcessAccountName,
          ProcessCommandLine,
          TargetPath
| order by TimeGenerated desc
pass394.40s0.0488
o3-mini-high
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where ProcessCommandLine contains "chmod" and ProcessCommandLine contains "-R"
| extend Target = extract(@"-R\s+(?:[0-7]{3}\s+)?([^\s]+)", 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, ProcessCommandLine, Target
pass271.99s0.0233
o3-mini-low
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where ProcessCommandLine has "chmod" and ProcessCommandLine has "-R"
| extend Target = extract(@"chmod\s+-R\s+(?:[0-7]{3,4}\s+)?(\S+)", 1, ProcessCommandLine)
| project TimeGenerated, DeviceName, ProcessCommandLine, Target
| order by TimeGenerated desc
pass135.76s0.0136
o4-mini-high
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where ProcessCommandLine contains "chmod"
| where ProcessCommandLine matches regex @"chmod\s+(?:-[^\s]*\s+)*-R"
| extend TargetPath = extract(@"chmod\s+(?:-[^\s]*\s+)*-R\s+([^\s]+)", 1, ProcessCommandLine)
| join kind=inner (
    DeviceInfo
    | where OSPlatform == "Linux"
    | project DeviceId
) on DeviceId
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ProcessCommandLine, TargetPath
pass139.50s0.0132
o4-mini-low
/tmp/AtomicRedTeam/atomics/T1222.002
DeviceProcessEvents
| where FileName == "chmod"
| where ProcessCommandLine has "-R"
| extend args = split(ProcessCommandLine, " ")
| extend target = args[array_length(args) - 1]
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ProcessCommandLine, target
pass129.59s0.0108