// question

On Linux systems, an attacker may gain persistence by appending instructions to the global shell profile. Investigate process or file modification events to find evidence of text being added to /etc/profile, and identify the exact command invocation that carried out this change.

Reference KQL answer and model-by-model comparison.

694b3cc8-6a78-4d35-9e74-0123d009e94b
question id
9/23
models correct
1
valid answer
39%
success rate
// reference answer

Human-validated correct answer for this question.

echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
no answer
no query generatedfail50.00s0.0408
gpt-35-turbo
no answer
no query generatedfail50.00s0.0109
gpt-4-turbo-2024-04-09
no answer
no query generatedfail50.00s0.2276
gpt-4.1
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
DeviceFileEvents
| where (FolderPath == '/etc' and FileName == 'profile') or (FolderPath == '/etc/' and FileName == 'profile') or (FolderPath == '/etc/profile') or (FolderPath == '/etc/' and FileName == '/profile')
// Also search where full path is embedded, in case FolderPath isn't split correctly
| union (DeviceFileEvents | where FolderPath == '/etc' and FileName contains 'profile')
| where ActionType in ('FileCreated', 'FileModified', 'FileWritten')
| project TimeGenerated, DeviceName, DeviceId, ActionType, FileName, FolderPath, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessSHA256, ReportId
| order by TimeGenerated desc
pass313.61s0.0346
gpt-4.1-finetuned
no answer
no query generatedfail50.00s0.0456
gpt-4.1-mini
no answer
no query generatedfail50.00s0.0052
gpt-4.1-nano
no answer
no query generatedfail50.00s0.0015
gpt-4o
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
DeviceFileEvents
| where ActionType in ("FileModified", "FileCreated")
| where FolderPath startswith "/etc" and FileName == "profile"
| project TimeGenerated, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, ActionType, DeviceId
pass428.33s0.0432
gpt-5-high
echo
let linux_devices = DeviceInfo
| where OSPlatform == "Linux"
| project DeviceId;
// 1) File modification evidence on /etc/profile with the exact initiating command
let fileMods = DeviceFileEvents
| where DeviceId in (linux_devices)
| where FolderPath == "/etc/profile"
| where ActionType in ("FileCreated", "FileModified", "FileReplaced")
| extend IPCmd = tostring(InitiatingProcessCommandLine)
| extend WriteMethod = case(
    IPCmd matches regex @"\>\>\s*/etc/profile", "append (>>)",
    IPCmd has "/etc/profile" and IPCmd has "tee" and IPCmd has "-a", "append (tee -a)",
    IPCmd has "/etc/profile" and IPCmd matches regex @"\bsed\b.*-i\b", "in-place edit (sed -i)",
    IPCmd has "/etc/profile" and IPCmd has_any ("vim","vi","nano"), "interactive editor",
    "unknown")
| project TimeGenerated, DeviceName, DeviceId, Evidence = "File modification", ActionType, FolderPath,
          WriteMethod, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessId,
          InitiatingProcessParentFileName, InitiatingProcessParentId, InitiatingProcessCreationTime,
          InitiatingProcessCommandLine = IPCmd;
// 2) Process executions that likely append/modify /etc/profile (fallback if file event correlation is missing)
let procAppend = DeviceProcessEvents
| where DeviceId in (linux_devices)
| where ActionType == "ProcessCreated"
| where ProcessCommandLine has "/etc/profile"
| where ProcessCommandLine matches regex @"(>>\s*/etc/profile)|(\btee\s+.*-a\b.*(/etc/profile))|(\bsed\b.*-i\b.*(/etc/profile))|(\b(ed|ex|vim|vi|nano)\b.*(/etc/profile))"
| project TimeGenerated, DeviceName, DeviceId, Evidence = "Process append/modify command", ActionType,
          FolderPath = "/etc/profile", WriteMethod = "Cmdline indicates append/modify",
          InitiatingProcessAccountName = AccountName, InitiatingProcessFileName = FileName,
          InitiatingProcessId = ProcessId, InitiatingProcessParentFileName, InitiatingProcessParentId,
          InitiatingProcessCreationTime = ProcessCreationTime, InitiatingProcessCommandLine = ProcessCommandLine;
union fileMods, procAppend
| order by TimeGenerated desc
fail1142.49s0.0808
gpt-5-mini-high
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
let targetFile = "/etc/profile";
// File events that touch /etc/profile
let fileEvents = (
    union DeviceFileEvents, DeviceEvents
    | where isnotempty(FileName)
    | where FileName == "profile"
    | where tostring(FolderPath) contains "/etc"
    // filter to likely write/modify actions
    | where (tolower(ActionType) contains "write") or (tolower(ActionType) contains "create") or (tolower(ActionType) contains "modify") or (tolower(ActionType) contains "changed") or (tolower(ActionType) contains "truncate") or (tolower(ActionType) contains "overwrite")
    | extend FilePath = strcat(FolderPath, '/', FileName)
    | project TimeGenerated, DeviceId, DeviceName, ActionType, FilePath, InitiatingProcessId, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, AdditionalFields
);

// Process events that reference /etc/profile in their command line (common append/persistence patterns)
let procRefs = (
    DeviceProcessEvents
    | where isnotempty(ProcessCommandLine)
    | where tolower(ProcessCommandLine) contains "/etc/profile"
          or tostring(ProcessCommandLine) matches regex @"(?i)(>>\s*/etc/profile|tee\s+-a\s+/etc/profile|cat\s+>>\s*/etc/profile|printf\s+.+>>\s*/etc/profile|echo\s+.+>>\s*/etc/profile|sed\s+-i)"
    | extend FilePath = "" // normalize column for union later
    | project TimeGeneratedProc = TimeGenerated, DeviceId, DeviceName, ProcessId, ProcessCommandLine, ProcessCreationTime, FilePath
);

// Process events to join with file events (to recover exact initiating process command)
let procs = (
    DeviceProcessEvents
    | project ProcTime = TimeGenerated, DeviceId, DeviceName, ProcessId, ProcessCommandLine, ProcessCreationTime, Account=AccountName, ProcessFileName=FileName
);

// Join file events to the initiating process (if any) and union with direct process references
fileEvents
| join kind=leftouter (
    procs
) on $left.InitiatingProcessId == $right.ProcessId and $left.DeviceId == $right.DeviceId
| extend InitiatorCommandLine = coalesce(InitiatingProcessCommandLine, ProcessCommandLine), InitiatorProcessId = InitiatingProcessId, InitiatorProcessFile = coalesce(InitiatingProcessFileName, ProcessFileName), InitiatorAccount = coalesce(InitiatingProcessAccountName, Account)
| project TimeGenerated, DeviceName, DeviceId, FilePath, ActionType, InitiatorProcessId, InitiatorProcessFile, InitiatorCommandLine, InitiatorAccount, ProcessCreationTime
| union (
    procRefs
    | project TimeGenerated = TimeGeneratedProc, DeviceName, DeviceId, FilePath = "/etc/profile", ActionType = "Process referencing /etc/profile", InitiatorProcessId = ProcessId, InitiatorProcessFile = "(process)", InitiatorCommandLine = ProcessCommandLine, InitiatorAccount = "(from proc event)", ProcessCreationTime
)
| order by TimeGenerated desc
| take 200
pass138.85s0.0103
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0166
gpt-5-mini-medium
no answer
no query generatedfail50.00s0.0195
gpt-5-nano-high
no answer
no query generatedfail50.00s0.0069
gpt-5-nano-medium
no answer
no query generatedfail50.00s0.0075
grok-3-beta
echo
let linuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| project DeviceId, DeviceName, OSPlatform;
DeviceFileEvents
| where FolderPath has "/etc/profile" or FileName has "profile"
| where ActionType in ("FileModified", "FileCreated", "FileRenamed")
| join kind=inner (linuxDevices) on DeviceId
| project TimeGenerated, DeviceName, ActionType, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| union (
    DeviceProcessEvents
    | where ProcessCommandLine has "/etc/profile" or InitiatingProcessCommandLine has "/etc/profile"
    | join kind=inner (linuxDevices) on DeviceId
    | project TimeGenerated, DeviceName, ActionType, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
)
| order by TimeGenerated desc
fail328.63s0.0799
grok-3-mini-beta
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
DeviceFileEvents
| where ActionType in~ ('FileModified', 'FileCreated')
| join kind=inner (DeviceInfo | where OSPlatform == 'Linux' | project DeviceId, DeviceName) on DeviceId
| where FolderPath has '/etc/profile' or (FolderPath has '/etc' and FileName has 'profile')
| project TimeGenerated, DeviceName, FolderPath, FileName, InitiatingProcessCommandLine, InitiatingProcessFileName
| order by TimeGenerated desc
| limit 100
pass562.98s0.0080
o1-high
no answer
no query generatedfail50.00s0.8207
o1-low
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
let LinuxDevices = DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId;
DeviceFileEvents
| where DeviceId in (LinuxDevices)
| where ActionType in ("FileModified","FileCreated")
| where FolderPath has "/etc" and FileName == "profile"
| project TimeGenerated,
          DeviceName,
          FolderPath,
          FileName,
          ActionType,
          InitiatingProcessAccountDomain,
          InitiatingProcessAccountName,
          InitiatingProcessCommandLine,
          InitiatingProcessParentFileName,
          InitiatingProcessParentId
| order by TimeGenerated desc
pass291.99s0.4921
o3-high
no answer
no query generatedfail50.00s0.1422
o3-mini-high
sh-c "echo '#HellofromAtomicRedTeamT1546.004'>>/etc/profile"
let fileModEvents =
    DeviceFileEvents
    | where FileName == "profile"
    | where FolderPath has "/etc"
    // Look for modification or append actions (case-insensitive) or additional fields mentioning append
    | where ActionType has_any ("Modified", "modified", "Append", "append")
          or tostring(AdditionalFields) has_cs "append"
    | project TimeGenerated, DeviceName, FolderPath, FileName, CommandLine = InitiatingProcessCommandLine, ActionType, AdditionalFields;

let processModEvents =
    DeviceProcessEvents
    // Search for redirection operator (>>), allowing any whitespace between >> and /etc/profile
    | where coalesce(ProcessCommandLine, InitiatingProcessCommandLine) matches regex @".*>>\s*/etc/profile.*"
    | project TimeGenerated, DeviceName, FolderPath, FileName, CommandLine = coalesce(ProcessCommandLine, InitiatingProcessCommandLine), ActionType;

fileModEvents
| union processModEvents
| sort by TimeGenerated desc
pass488.62s0.0417
o3-mini-low
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
union
(
    // File modification events on /etc/profile
    DeviceFileEvents
    | where (FolderPath == "/etc" or FolderPath has "/etc")
          and FileName =~ "profile"
          and ActionType contains "Modified"
    | extend CommandInvocation = InitiatingProcessCommandLine, Source = "FileModificationEvent"
),
(
    // Process events with command line that appends to /etc/profile using redirection
    DeviceProcessEvents
    | where ProcessCommandLine contains ">>" and ProcessCommandLine contains "/etc/profile"
    | extend CommandInvocation = ProcessCommandLine, Source = "ProcessEvent"
)
| project TimeGenerated, DeviceName, FolderPath, FileName, ActionType, CommandInvocation, Source
| order by TimeGenerated desc
pass345.50s0.0307
o4-mini-high
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
let LinuxDeviceIds = DeviceInfo
| where OSPlatform startswith "Linux"
| distinct DeviceId;

let FileMods = DeviceFileEvents
| where DeviceId in (LinuxDeviceIds)
| where FolderPath has "/etc" and FileName == "profile"
| where ActionType has "Modified" or ActionType has "Overwritten"
| extend FilePath = strcat(FolderPath, "/", FileName)
| project TimeGenerated, DeviceId, DeviceName, FilePath, ActionType, InitiatingProcessId;

let FileModCommands = FileMods
| join kind=leftouter (
    DeviceProcessEvents
    | where DeviceId in (LinuxDeviceIds)
    | project DeviceId, ProcessId, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, ProcessCreationTime, ProcessCommandLine
  ) on DeviceId, $left.InitiatingProcessId == $right.ProcessId
| project TimeGenerated, DeviceName, FilePath, ActionType, ProcessCreationTime, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, ProcessCommandLine;

let DirectAppends = DeviceProcessEvents
| where DeviceId in (LinuxDeviceIds)
| where ProcessCommandLine has ">> /etc/profile" or ProcessCommandLine has "tee -a /etc/profile"
| project TimeGenerated, DeviceName, FilePath = "/etc/profile", ActionType = "Direct Append", ProcessCreationTime, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, ProcessCommandLine;

FileModCommands
| union DirectAppends
| sort by TimeGenerated desc
pass2149.70s0.0337
o4-mini-low
sh -c "echo '# Hello from Atomic Red Team T1546.004' >> /etc/profile"
let fileModifications = DeviceFileEvents
  | where ActionType == "FileModified"
  | where FolderPath == "/etc" and FileName == "profile"
  | project TimeGenerated, SourceEvent = "FileModified", DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessId, ProcessCommandLine = InitiatingProcessCommandLine;
let processWrites = DeviceProcessEvents
  | where ProcessCommandLine matches regex @"(>>|tee).*?/etc/profile"
  | project TimeGenerated, SourceEvent = "ProcessWrite", DeviceName, InitiatingProcessAccountName = AccountName, InitiatingProcessFileName = FileName, InitiatingProcessId = ProcessId, ProcessCommandLine;
union fileModifications, processWrites
| sort by TimeGenerated desc
pass1623.79s0.0101