// question

On a Linux endpoint, you suspect malicious clearing of the bash history by redirecting from the null device. Explore process or file events to uncover the exact shell command that performed this action.

Reference KQL answer and model-by-model comparison.

b1251c35-dcd3-4ea1-86da-36d27b54f31f
question id
10/23
models correct
1
valid answer
43%
success rate
// reference answer

Human-validated correct answer for this question.

cat /dev/null > ~/.bash_history
// model comparison
ModelAnswerKQL queryStatusTriesTimeCost
gemini-2.5-flash-preview-04-17
sh -c "cat /dev/null > ~/.bash_history"
DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where FileName in ("bash", "sh", "dash", "ksh", "zsh")
| where (ProcessCommandLine contains "/dev/null >" and (ProcessCommandLine contains ".bash_history" or ProcessCommandLine contains ".history"))
    or ProcessCommandLine contains "history -c"
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCreationTime
| order by TimeGenerated desc
pass120.49s0.0115
gpt-35-turbo
no answer
no query generatedfail50.00s0.0109
gpt-4-turbo-2024-04-09
sh -c "cat /dev/null > ~/.bash_history"
DeviceProcessEvents
| where ProcessCommandLine contains 'history' and ProcessCommandLine contains '/dev/null' and ProcessCommandLine contains '>'
| project DeviceName, AccountName, ProcessCommandLine, TimeGenerated
pass215.76s0.0962
gpt-4.1
sh -c "cat /dev/null > ~/.bash_history"
DeviceProcessEvents
| where FileName in~ ("bash", "sh", "zsh", "dash")
| where ProcessCommandLine has_any ("> ~/.bash_history", ": > ~/.bash_history", "/dev/null > ~/.bash_history", "cat /dev/null > ~/.bash_history")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
pass16.64s0.0118
gpt-4.1-finetuned
sh -c "cat /dev/null > ~/.bash_history"
DeviceProcessEvents
| where ProcessCommandLine contains "> ~/.bash_history" or ProcessCommandLine contains " >~/.bash_history" or (ProcessCommandLine contains "bash_history" and ProcessCommandLine contains ">")
| sort by TimeGenerated desc
| project TimeGenerated, DeviceName, ProcessCommandLine, AccountName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| top 10 by TimeGenerated desc
pass532.14s0.0344
gpt-4.1-mini
no answer
no query generatedfail50.00s0.0065
gpt-4.1-nano
no answer
no query generatedfail50.00s0.0019
gpt-4o
sh -c "cat /dev/null > ~/.bash_history"
DeviceProcessEvents
| where InitiatingProcessCommandLine has "bash" and ProcessCommandLine contains "/dev/null"
| summarize arg_max(TimeGenerated, *) by DeviceId, ProcessId
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, ProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessId
pass330.35s0.0390
gpt-5-high
cat /dev/null > ~/.bash_history
let linuxDeviceIds = DeviceInfo
| where OSPlatform =~ "Linux"
| distinct DeviceId;
let SuspectProc = DeviceProcessEvents
| where DeviceId in (linuxDeviceIds)
| extend cmd = tolower(ProcessCommandLine)
| where cmd has "/dev/null"
| where cmd has ".bash_history" or cmd matches regex @"\bhistfile\b"
| extend Technique = case(
    cmd has "ln " and cmd has "/dev/null", "Symlink history to /dev/null",
    cmd has "dd " and cmd has "if=/dev/null", "dd if=/dev/null to history",
    cmd has "cp " and cmd has "/dev/null", "cp /dev/null over history",
    cmd has "cat " and cmd has "/dev/null", "cat /dev/null > history",
    "Other via /dev/null")
| project ProcTime=TimeGenerated, DeviceId, DeviceName, AccountName, FileName, ProcessId, ProcessCommandLine, InitiatingProcessId, InitiatingProcessFileName, InitiatingProcessCommandLine, Technique;
let HistFileOps = DeviceFileEvents
| where DeviceId in (linuxDeviceIds)
| where ActionType in ("FileDeleted","FileCreated","FileModified","FileRenamed")
| where FileName == ".bash_history" or FolderPath has "/.bash_history"
| project FileEventTime=TimeGenerated, DeviceId, DeviceName, FileActionType=ActionType, FolderPath, FileName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, ReportId;
SuspectProc
| join kind=leftouter HistFileOps on DeviceId
| where isnull(FileEventTime) or (FileEventTime between (ProcTime-10m .. ProcTime+10m))
| project ProcTime, DeviceName, AccountName, ProcessCommandLine, Technique, FileEventTime, FileActionType, FolderPath, HistFileEventInitiatorFile=InitiatingProcessFileName1, HistFileEventInitiatorCmd=InitiatingProcessCommandLine1
| order by ProcTime desc
pass2198.99s0.1319
gpt-5-mini-high
no answer
no query generatedfail50.00s0.0208
gpt-5-mini-low
no answer
no query generatedfail50.00s0.0178
gpt-5-mini-medium
cat /dev/null > ~/.bash_history
union isfuzzy=true DeviceProcessEvents, DeviceFileEvents, DeviceEvents
| extend ProcessCommandLine = tostring(coalesce(ProcessCommandLine, InitiatingProcessCommandLine, "")),
         InitiatingProcessCommandLine = tostring(InitiatingProcessCommandLine),
         FileName = tostring(FileName),
         FolderPath = tostring(FolderPath),
         AdditionalFields = tostring(AdditionalFields)
// Filter rows that reference /dev/null and a bash history file (in cmd, filename, folder or additional fields)
| where (
        (ProcessCommandLine contains "/dev/null" or InitiatingProcessCommandLine contains "/dev/null" or AdditionalFields contains "/dev/null")
        and (ProcessCommandLine contains "bash_history" or InitiatingProcessCommandLine contains "bash_history" or FileName contains "bash_history" or FolderPath contains "bash_history" or AdditionalFields contains "bash_history")
    )
    or (
        // file events on a bash_history file where the initiating/observed command referenced /dev/null
        FileName contains "bash_history" and (ProcessCommandLine contains "/dev/null" or InitiatingProcessCommandLine contains "/dev/null" or AdditionalFields contains "/dev/null")
    )
    or (
        // catch common overwrite/truncate patterns that target bash_history even if /dev/null token isn't on the same row
        (ProcessCommandLine matches regex @"(?i).*\b(cat|cp|truncate|:|echo|printf|>|>>).*(bash_history|\\.bash_history|~/\\.bash_history).*" )
        or (InitiatingProcessCommandLine matches regex @"(?i).*\b(cat|cp|truncate|:|echo|printf|>|>>).*(bash_history|\\.bash_history|~/\\.bash_history).*" )
    )
| extend evidenceCmd = iff(strlen(ProcessCommandLine) > 0, ProcessCommandLine, InitiatingProcessCommandLine)
// extract short snippets to show the redirection or command that affected the history file
| extend devnull_snippet = extract(@"(?i)[^;\n]{0,200}(/dev\/null[^;\n]{0,200})", 0, evidenceCmd),
         redirect_snippet = extract(@"(?i)[^;\n]{0,200}(?:cat|cp|truncate|:|echo|printf|>|>>)[^;\n]{0,200}(?:bash_history|\\.bash_history|~\/\\.bash_history)[^;\n]{0,200}", 0, evidenceCmd)
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountName, ProcessId, InitiatingProcessId, ProcessCommandLine = evidenceCmd, InitiatingProcessCommandLine, FileName, FolderPath, ActionType, devnull_snippet, redirect_snippet, AdditionalFields, SourceSystem, ReportId
| order by TimeGenerated desc
| take 200
pass258.26s0.0154
gpt-5-nano-high
no answer
no query generatedfail50.00s0.0124
gpt-5-nano-medium
export HISTFILE="/dev/null"
let tFrom = ago(1d);
let tTo = now();

let procHits = DeviceProcessEvents
| where TimeGenerated between (tFrom .. tTo)
| where (InitiatingProcessCommandLine contains "bash" or ProcessCommandLine contains "bash")
| where (
    InitiatingProcessCommandLine contains "/dev/null" or ProcessCommandLine contains "/dev/null" 
    or InitiatingProcessCommandLine contains "> ~/.bash_history" or InitiatingProcessCommandLine contains ">/.bash_history" 
    or ProcessCommandLine contains "> ~/.bash_history" or ProcessCommandLine contains ">/.bash_history" 
    or InitiatingProcessCommandLine contains "cat /dev/null" 
    or InitiatingProcessCommandLine contains "truncate -s 0 ~/.bash_history" 
    or InitiatingProcessCommandLine contains "history -c" 
) 
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, InitiatingProcessCommandLine, ProcessCommandLine, InitiatingProcessFolderPath, FolderPath, FileName, InitiatingProcessId, ProcessId;

let fileHits = DeviceFileEvents
| where TimeGenerated between (tFrom .. tTo)
| where (FolderPath contains ".bash_history" or FileName contains ".bash_history" or FileName contains "bash_history")
| project TimeGenerated, DeviceName, FolderPath, FileName, ActionType, InitiatingProcessCommandLine;

procHits
| union fileHits
| order by TimeGenerated desc
| take 200
fail264.21s0.0046
grok-3-beta
no answer
no query generatedfail50.00s0.0938
grok-3-mini-beta
cat /dev/null > ~/.bash_history
let LinuxDevices = DeviceInfo | where OSPlatform == "Linux" | distinct DeviceId;
DeviceProcessEvents
| where DeviceId in (LinuxDevices)
| where ProcessCommandLine contains "history" and ProcessCommandLine contains "-c" and ProcessCommandLine contains "/dev/null"
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, ProcessId
| order by TimeGenerated desc
pass119.71s0.0018
o1-high
cat
union DeviceProcessEvents, DeviceFileEvents
| where TimeGenerated >= ago(30d)
// Restrict to Linux devices
| join kind=inner (
    DeviceInfo
    | where OSPlatform =~ "Linux"
    | distinct DeviceId
) on DeviceId
// Filter for command lines referencing /dev/null and bash_history,
// or file references indicating .bash_history modifications.
| where ( (isnotempty(ProcessCommandLine) and ProcessCommandLine has_all ("/dev/null", "bash_history"))
       or (isnotempty(InitiatingProcessCommandLine) and InitiatingProcessCommandLine has_all ("/dev/null", "bash_history"))
       or FileName == ".bash_history")
// Return key data fields
| project TimeGenerated, DeviceName, ActionType,
          ProcessCommandLine, InitiatingProcessCommandLine,
          FolderPath, FileName
// Sort and limit to reduce result size
| order by TimeGenerated desc
| limit 1000
fail390.57s0.6543
o1-low
cat/dev/null>~/.bash_history
DeviceInfo
| where OSPlatform == "Linux"
| distinct DeviceId, DeviceName
| join kind=inner (
    DeviceProcessEvents
    | where ActionType == "ProcessCreated"
    // Adjust to match desired pattern of suspicious commands
    | where ProcessCommandLine has "/dev/null"
    | where ProcessCommandLine has_any ("bash_history", ".bash_history")
) on DeviceId
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessCommandLine, ProcessCommandLine
| order by TimeGenerated desc
| limit 100
fail274.46s0.5151
o3-high
cat /dev/null > ~/.bash_history
// ---- hunting: suspicious bash-history wipe by redirecting /dev/null ----
let startTime = ago(7d);   //  <-- portal will overwrite
let endTime   = now();     //  <-- portal will overwrite
// Pull Linux devices only
let LinuxDevices = DeviceInfo
    | where OSPlatform == "Linux"
    | project DeviceId;
// 1. Process executions that redirect /dev/null into .bash_history
let ProcWipe = DeviceProcessEvents
    | where TimeGenerated between (startTime .. endTime)
    | where DeviceId in (LinuxDevices)
    // classical pattern: both tokens should exist in the same command line
    | where ProcessCommandLine contains "/dev/null" and ProcessCommandLine contains ".bash_history"
    | extend Evidence = "Process redirected /dev/null into bash_history"
    | project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, FileName, FolderPath, Evidence;
// 2. Low-level file activity against the bash_history file that indicates truncate/delete/overwrite
let FileWipe = DeviceFileEvents
    | where TimeGenerated between (startTime .. endTime)
    | where DeviceId in (LinuxDevices)
    | where FileName == ".bash_history" or FolderPath endswith ".bash_history"
    | where ActionType in ("FileTruncated", "FileOverwritten", "FileDeleted", "FileRenamed", "FileCreated")
    | extend Evidence = strcat("File event: ", ActionType)
    | project TimeGenerated, DeviceName, AccountName = InitiatingProcessAccountName,
              ProcessCommandLine = InitiatingProcessCommandLine,
              FileName, FolderPath, Evidence;
// 3. Combine and sort
union ProcWipe, FileWipe
| sort by TimeGenerated desc
pass151.71s0.0291
o3-mini-high
cat
union withsource=TableName DeviceProcessEvents, DeviceFileEvents
| extend CommandLine = iif(isnotempty(InitiatingProcessCommandLine), InitiatingProcessCommandLine, ProcessCommandLine)
| where CommandLine contains "/dev/null" and CommandLine contains "history"
| project TimeGenerated, DeviceName, TableName, CommandLine, FileName, FolderPath, ActionType
| sort by TimeGenerated desc
| limit 200
fail2102.21s0.0229
o3-mini-low
cat
DeviceProcessEvents
| where ProcessCommandLine contains "/dev/null"
| where ProcessCommandLine contains "bash_history"
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessCommandLine
| sort by TimeGenerated desc
fail159.58s0.0172
o4-mini-high
no answer
no query generatedfail50.00s0.0463
o4-mini-low
cat /dev/null > ~/.bash_history
let proc = DeviceProcessEvents
| where ProcessCommandLine contains "/dev/null" and ProcessCommandLine contains "bash_history"
    or ProcessCommandLine contains "history -c"
| project TimeGenerated, DeviceName, User = InitiatingProcessAccountName, CommandLine = ProcessCommandLine;

let file = DeviceFileEvents
| where FileName endswith ".bash_history" and ActionType in ("FileCreated", "FileModified")
| project TimeGenerated, DeviceName, User = InitiatingProcessAccountName, CommandLine = InitiatingProcessCommandLine, File = FileName, ActionType;

proc
| union file
| sort by TimeGenerated desc
pass142.52s0.0132