// question
Linux hosts may log events when new files are added to /var/spool/cron/crontabs. Query those logs for a creation or write action in that directory and determine the file name that was added.
Reference KQL answer and model-by-model comparison.
2d943c18-e74a-44bf-936f-25ade6cccab4
question id
17/23
models correct
1
valid answer
74%
success rate
// reference answer
Human-validated correct answer for this question.
persistevil
// model comparison
| Model | Answer | KQL query | Status | Tries | Time | Cost |
|---|---|---|---|---|---|---|
| gemini-2.5-flash-preview-04-17 | persistevil | DeviceFileEvents | where FolderPath startswith "/var/spool/cron/crontabs" | where ActionType == "FileCreated" or ActionType == "FileWritten" | project Timestamp, DeviceName, FolderPath, FileName, ActionType, InitiatingProcessCommandLine | pass | 1 | 11.16s | 0.0055 |
| gpt-35-turbo | no answer | no query generated | fail | 5 | 0.00s | 0.0109 |
| gpt-4-turbo-2024-04-09 | no answer | no query generated | fail | 5 | 0.00s | 0.2263 |
| gpt-4.1 | persistevil | DeviceFileEvents
| where FolderPath startswith "/var/spool/cron/crontabs"
| where ActionType in ("FileCreated", "FileCreated (CreateFile)", "FileWritten", "FileCreatedOrWritten")
| project TimeGenerated, DeviceName, FolderPath, FileName, ActionType, InitiatingProcessAccountName, InitiatingProcessFileName | pass | 1 | 4.21s | 0.0118 |
| gpt-4.1-finetuned | persistevil | DeviceFileEvents | where (ActionType =~ "FileCreated" or ActionType =~ "FileModified") | where FolderPath contains "/var/spool/cron/crontabs/" | project-reorder Timestamp, ActionType, FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessParentFileName, InitiatingProcessFileName, MD5, SHA256 | extend timestamp = Timestamp, FileNameArray = split(FolderPath, "/") | extend FileName = tostring(FileNameArray[4]) | project-rename MDTimestamp = Timestamp, MDEventTime = timestamp, MDDeviceName = DeviceName, MDFolderPath = FolderPath, MDProcessName = InitiatingProcessCommandLine, MDParentProcessName = InitiatingProcessParentFileName, MDMD5 = MD5, MDOwner = InitiatingProcessAccountName | extend NTDomain = iff(MDOwner contains "@", tostring(split(MDOwner, "@", 1)[0]), MDOwner) | extend NTDomain = iff(MDOwner contains "\\", tostring(split(MDOwner, "\\", 1)[0]), NTDomain) | extend Name = iff(MDOwner contains "@", tostring(split(MDOwner, "@", 0)[0]), MDOwner) | extend Name = iff(MDOwner contains "\\", tostring(split(MDOwner, "@", 0)[1]), Name) | project MDTimestamp, MDEventTime, MDDeviceName, FileName, MDFolderPath, MDProcessName, MDParentProcessName, MDMD5, SHA256, NTDomain ,Name | extend OSPlatform = "Linux" | distinct MDTimestamp , MDEventTime, MDDeviceName, OSPlatform, FileName, MDFolderPath, MDProcessName, MDMD5, SHA256, NTDomain, Name, MDParentProcessName | project MDTimestamp, MDEventTime, MDDeviceName, OSPlatform, FileName, MDFolderPath, MDProcessName, MDMD5, SHA256, NTDomain, Name, MDParentProcessName | extend timestamp = MDTimestamp, Name = tostring(split(Name, "@", 0)[0]) | extend Account_0_Name = Name | extend Account_0_NTDomain = NTDomain | extend Host_0_HostName = MDDeviceName | extend Host_0_OSType = OSPlatform | extend File_0_Name = FileName | extend File_0_Directory = MDFolderPath | extend Process_0_ProcessId = MDProcessName | extend FileHash_0_Algorithm = "MD5" | extend FileHash_0_Value = MDMD5 | extend FileHash_1_Algorithm = "SHA256" | extend FileHash_1_Value = SHA256 | extend Process_1_ProcessId = MDParentProcessName | pass | 3 | 41.24s | 0.0426 |
| gpt-4.1-mini | persistevil | DeviceFileEvents
| where FolderPath startswith "/var/spool/cron/crontabs"
| where ActionType in ("FileCreated", "FileWritten")
| project TimeGenerated, DeviceName, FileName, ActionType, InitiatingProcessAccountName
| order by TimeGenerated desc | pass | 1 | 20.43s | 0.0023 |
| gpt-4.1-nano | no answer | no query generated | fail | 5 | 0.00s | 0.0017 |
| gpt-4o | persistevil | DeviceFileEvents
| where FolderPath contains '/var/spool/cron/crontabs' and ActionType in ('FileCreated', 'FileModified')
| project TimeGenerated, FileName, FolderPath, ActionType | pass | 3 | 19.12s | 0.0403 |
| gpt-5-high | persistevil | let cron_dir = "/var/spool/cron/crontabs";
let linux_devices = DeviceInfo
| where OSPlatform == "Linux"
| project DeviceId;
DeviceFileEvents
| where DeviceId in (linux_devices)
| where ActionType in ("FileCreated", "FileModified", "FileRenamed")
| where FolderPath hasprefix cron_dir or (ActionType == "FileRenamed" and (FolderPath hasprefix cron_dir or coalesce(PreviousFolderPath, "") hasprefix cron_dir))
| extend Operation = case(
ActionType == "FileCreated", "Created",
ActionType == "FileModified", "Modified",
ActionType == "FileRenamed" and not(coalesce(PreviousFolderPath, "") hasprefix cron_dir) and (FolderPath hasprefix cron_dir), "MovedIntoDir",
ActionType == "FileRenamed" and (coalesce(PreviousFolderPath, "") hasprefix cron_dir) and (FolderPath hasprefix cron_dir), "RenamedWithinDir",
"Other")
| where Operation in ("Created", "Modified", "MovedIntoDir")
| extend AddedFileName = FileName, AddedFilePath = strcat(FolderPath, "/", FileName)
| project TimeGenerated, DeviceName, DeviceId, Operation, AddedFileName, AddedFilePath,
InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine,
PreviousFolderPath, PreviousFileName
| order by TimeGenerated desc | pass | 3 | 227.81s | 0.1275 |
| gpt-5-mini-high | persistevil | union isfuzzy=true DeviceFileEvents, DeviceEvents
| where (
tolower(coalesce(FolderPath, "")) startswith "/var/spool/cron/crontabs"
or tolower(coalesce(FileName, "")) contains "/var/spool/cron/crontabs"
or tostring(AdditionalFields) contains "/var/spool/cron/crontabs"
)
| where (
tolower(coalesce(ActionType, "")) contains "creat"
or tolower(coalesce(ActionType, "")) contains "write"
or tolower(coalesce(ActionType, "")) contains "modif"
)
| extend FolderPathLower = tolower(coalesce(FolderPath, "")), FileNameStr = tostring(FileName)
// Build a full path: if FileName is already an absolute path use it, otherwise prefix FolderPath
| extend FullPath = iff(FileNameStr startswith "/", FileNameStr, strcat(FolderPathLower, "/", FileNameStr))
| extend AddedFile = extract(@'([^/]+)$', 1, FullPath)
| project TimeGenerated, DeviceName, AccountName, ActionType, FullPath, AddedFile, InitiatingProcessFileName, InitiatingProcessCommandLine, AdditionalFields
| summarize Count = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), Devices = dcount(DeviceName), DeviceList = make_set(DeviceName, 20) by AddedFile, FullPath
| order by LastSeen desc
| take 200 | pass | 1 | 24.99s | 0.0057 |
| gpt-5-mini-low | persistevil | let targetPath = "/var/spool/cron/crontabs";
let actions = dynamic(["FileCreated","Created","FileCreatedByUser","CreatedFile","Write","Written","Modify","Modified","FileModified","Change","FileWritten","Created"]);
let normalizeFullPath = (folderpath:string, filename:string){
// Prefer FileName when it contains full path, else combine
iif(isnotempty(filename) and filename contains "/", filename, strcat(folderpath, "/", filename))
};
let srcFile = DeviceFileEvents
| where isnotempty(FolderPath) or isnotempty(FileName)
| where (FolderPath contains targetPath) or (FileName contains targetPath) or (isnotempty(FileName) and FileName startswith strcat(targetPath, "/"))
| where ActionType in ("FileCreated","Created","FileCreatedByUser","CreatedFile","Write","Written","Modify","Modified","FileModified","Change","FileWritten","Created")
| extend FullPath = normalizeFullPath(FolderPath, FileName)
| extend Parts = split(FullPath, "/")
| extend CrontabFile = tostring(Parts[array_length(Parts)-1])
| project TimeGenerated, DeviceName, ActionType, CrontabFile, FullPath, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessAccountDomain, RequestAccountName, AdditionalFields;
let srcDev = DeviceEvents
| where isnotempty(FolderPath) or isnotempty(FileName)
| where (FolderPath contains targetPath) or (FileName contains targetPath) or (isnotempty(FileName) and FileName startswith strcat(targetPath, "/"))
| where ActionType in ("FileCreated","Created","CreatedFile","Write","Written","Modify","Modified","Change","FileWritten","Created")
| extend FullPath = normalizeFullPath(FolderPath, FileName)
| extend Parts = split(FullPath, "/")
| extend CrontabFile = tostring(Parts[array_length(Parts)-1])
| project TimeGenerated, DeviceName, ActionType, CrontabFile, FullPath, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName, InitiatingProcessAccountDomain, AdditionalFields;
(srcFile)
| union (srcDev)
| where isnotempty(CrontabFile) and CrontabFile != "" and FullPath contains targetPath
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Count=count(), Devices=make_set(DeviceName), InitiatingProcesses=make_set(InitiatingProcessFileName), InitiatingCmds=make_set(InitiatingProcessCommandLine), Accounts=make_set(coalesce(InitiatingProcessAccountName, InitiatingProcessAccountDomain, RequestAccountName)) by CrontabFile, FullPath, ActionType
| order by LastSeen desc
| take 500 | pass | 5 | 50.42s | 0.0130 |
| gpt-5-mini-medium | no answer | no query generated | fail | 5 | 0.00s | 0.0165 |
| gpt-5-nano-high | no answer | no query generated | fail | 5 | 0.00s | 0.0053 |
| gpt-5-nano-medium | no answer | no query generated | fail | 5 | 0.00s | 0.0062 |
| grok-3-beta | persistevil | DeviceFileEvents
| where ActionType in ('FileCreated', 'FileModified')
| where FolderPath contains '/var/spool/cron/crontabs'
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessAccountName, InitiatingProcessFileName
| order by Timestamp desc | pass | 1 | 12.00s | 0.0223 |
| grok-3-mini-beta | persistevil | DeviceFileEvents
| where FolderPath has "/var/spool/cron/crontabs"
| where ActionType in~ ('FileCreated', 'FileModified')
| project TimeGenerated, DeviceName, FileName, ActionType
| summarize AddedFiles = make_set(FileName), LatestTime = arg_max(TimeGenerated, *) by DeviceName, ActionType
| order by LatestTime desc | pass | 3 | 31.57s | 0.0044 |
| o1-high | persistevil | DeviceFileEvents
| where ActionType in ("FileCreated","FileModified")
| where FolderPath startswith "/var/spool/cron/crontabs"
| project TimeGenerated, DeviceId, DeviceName, ActionType, FolderPath, FileName | pass | 1 | 27.48s | 0.1803 |
| o1-low | persistevil | DeviceFileEvents
| where FolderPath startswith "/var/spool/cron/crontabs"
and ActionType in ("FileCreated", "FileModified")
| project TimeGenerated, DeviceName, ActionType, FolderPath, FileName | pass | 1 | 22.67s | 0.1964 |
| o3-high | persistevil | let cronPath = "/var/spool/cron/crontabs";
DeviceFileEvents
// --- Only Linux endpoints (comment this block if not needed) -------------------
| extend DeviceIdTmp = DeviceId
| join kind=inner (
DeviceInfo
| where OSPlatform =~ "Linux"
| project DeviceId, OSPlatform
) on DeviceId
// -----------------------------------------------------------------------------
| where FolderPath startswith cronPath
| where ActionType in ("FileCreated", "FileWritten", "FileModified", "FileCreate", "FileWrite")
| project TimeGenerated,
DeviceName,
FilePath = strcat(FolderPath, "/", FileName),
ActionType,
InitiatingProcessAccountName,
InitiatingProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessId
| order by TimeGenerated desc | pass | 1 | 35.39s | 0.0279 |
| o3-mini-high | persistevil | union isfuzzy=true DeviceFileEvents, DeviceEvents
| where FolderPath has "/var/spool/cron/crontabs"
| where ActionType in ("FileCreated", "FileWrite", "FileWritten")
| project TimeGenerated, FileName, ActionType, FolderPath | pass | 2 | 26.58s | 0.0180 |
| o3-mini-low | persistevil | union withsource=TableName DeviceFileEvents, DeviceEvents, DeviceProcessEvents | where isnotempty(FolderPath) and tolower(FolderPath) contains "/var/spool/cron/crontabs" | where tolower(ActionType) contains "creat" or tolower(ActionType) contains "write" | project TimeGenerated, FileName, ActionType, FolderPath, TableName | order by TimeGenerated desc | pass | 4 | 85.24s | 0.0311 |
| o4-mini-high | persistevil | DeviceFileEvents
| where FolderPath has "/var/spool/cron/crontabs"
| where ActionType in ("FileCreated", "FileWritten", "FileModified", "FileOpenedWrite")
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), EventCount = count() by FileName
| sort by LastSeen desc | pass | 1 | 22.88s | 0.0070 |
| o4-mini-low | persistevil | DeviceFileEvents
| where FolderPath startswith "/var/spool/cron/crontabs"
| where ActionType in ("FileCreated", "FileWrite", "FileModified")
| project TimeGenerated, DeviceName, FileName, FolderPath, ActionType, InitiatingProcessCommandLine
| sort by TimeGenerated desc | pass | 4 | 105.87s | 0.0336 |