Introduction
Recently a few articles came across my timeline that broached the topic of how agentic workloads impact telemetry ingestion.
In particular Kofisec began to explore how traditional detection controls and measurements might break and antonlovesdnb shared a potentially useful new tool that might help address the significant development time required to bring analysts the telemetry they need during agentic related incidents. (I haven't tested it yet).
Considering these articles and the general direction we are now headed I thought I would share a quick snippet on what I'm seeing in the telemetry.
Process characteristics
Starting with some basic empirical data on the prevalence of claude code
overall the binary
claude.exe
experiences significant diversity in load location where in some cases I am
seeing up to four times the amount of locations used on disk compared to total
installations.
This is a natural product of the claude installation eco system where claude apps utilises the temp directories as both a staging area for MCP servers, plugins and core features but also for direct workload requirements like ‘scratchpads’.
Additionally,
claude.exe
exhibits a high degree of command-line parameter diversity due to its packaged
Electron architecture and support for user-defined switches. In total, I
recorded 228 distinct command-line switches (excluding custom user arguments).
Overall where a value is passed with a switch the values are highly predictable with over 90 percent of the content being a file path inside the users folder.
Switches are characterised by these groups:
- electron app components
- session settings
- max tokens
- disallowed tools
- effort
- app settings
- mcp servers
- enabled feature flags
Parent/Child Relationships
Overall twice as many apps invoke Claude as Claude itself invokes. This is an interesting observation where in general both sides of the coin share a similar general profile of native host based controls and user installed software but the depth of integration into other apps seems to compound the surfaces Claude touches.
child: claude.exe
-
Browsers (SSO callback and ‘magic links’)
-
App Managers (Raycast, Powertoys etc)
Script Interpreters (all of them)
-
Other Agentic Tools (Anti Gravity, Git AI etc)
Interesting Note:
zshinvocations ofclaude.exeinflateauditdingestion volume. If you monitor Linux endpoints, tuning filter rules around these executions will be worthwhile.
parent: claude.exe
Script Interpreters
Browsers
git
-
/usr/bin/security(claude secrets) -
office apps (word, powerpoint etc) - even draw.io (cool use case!)
ssh (remote connections to runners)
Visibility
Thanks to the nature of agentic activity being tied closely with native operating system features EDR products do provide a lot of insight into execution chains and second order effects. However I have noticed this is not necessarily true for Defender for Endpoint (Crowdstike had less of an issue). Throughout my testing the data was sampled too heavily and for the most part you would not be able to reliably use it for detecting abuse of claude.
Putting aside issues with data consistency one fun use case for EDR telemetry is too monitor what packages claude is installing. You can do this easily by chaining together your parent and target process IDs.
let ProcEvents =
DeviceProcessEvents
| where FileName in~ ("powershell.exe", "pip.exe", "python.exe")
| project
Timestamp,
DeviceName,
FileName,
ProcessId,
ProcessCommandLine,
InitiatingProcessId,
InitiatingProcessFileName,
InitiatingProcessCommandLine;
let PowerShell =
ProcEvents
| where FileName =~ "powershell.exe"
| project
DeviceName,
PS_ProcessId = ProcessId,
PS_Time = Timestamp,
PS_CommandLine = ProcessCommandLine,
PS_ParentProcess = InitiatingProcessFileName,
PS_ParentCommandLine = InitiatingProcessCommandLine;
let Pip =
ProcEvents
| where FileName =~ "pip.exe"
| project
DeviceName,
Pip_ProcessId = ProcessId,
Pip_ParentId = InitiatingProcessId,
Pip_Time = Timestamp,
Pip_CommandLine = ProcessCommandLine;
let Python =
ProcEvents
| where FileName =~ "python.exe"
| project
DeviceName,
Python_ProcessId = ProcessId,
Python_ParentId = InitiatingProcessId,
Python_Time = Timestamp,
Python_CommandLine = ProcessCommandLine;
PowerShell
| join kind=inner Pip on DeviceName
| where Pip_ParentId == PS_ProcessId
| join kind=leftouter Python on DeviceName
| where Python_ParentId == Pip_ProcessId
| project
DeviceName,
PS_Time,
PS_ParentProcess,
PS_ParentCommandLine,
PowerShell = PS_CommandLine,
Pip_Time,
Pip = Pip_CommandLine,
Python_Time,
Python = Python_CommandLine
| order by PS_Time desc
CQL
| defineTable(query={
| ParentBaseFileName = claude.exe
| claude_parent := ParentBaseFileName
| powershell_process := FileName
}, include=[*], name="claude_root")
| defineTable(query={
| FileName = pip.exe
| pip := ImageFileName
| pip_target := TargetProcessId
| pip_parent := ParentProcessId
| match(file="claude_root", field=[ParentProcessId], column=TargetProcessId, include=[claude_parent, powershell_process])
}, include=[*], name="pip_process")
| defineTable(query={
| in(field="#event_simpleName", values=["ProcessRollup2"])
| FileName = python.exe
| python := ImageFileName
| python_target := TargetProcessId
| python_parent := ParentProcessId
| match(file="pip_process", field=[ParentProcessId], column=[TargetProcessId], include=[claude_parent, powershell_process, pip])
}, include=[*], name="python")
| in(field="#event_simpleName", values=["ZipFileWritten"])
| ContextBaseFileName = python.exe
| TargetFileName = /Local\\Temp\\pip-unpack/
| zip_target := ContextProcessId
| package_name := file.name
| match(file="python", field=[ContextProcessId], column=TargetProcessId, include=[claude_parent, powershell_process, pip, python])
| format(format="%s
▼\r\n%s
▼\r\n%s
▼\r\n%s
▼\r\n%s", field=[claude_parent, FileName, pip, python, package_name], as=tree) | table([@timestamp, tree, ComputerName, UserName])
Conclusions
The telemetry is shaped about as you would intuitively expect for agentic workloads. I will be working on measuring the total impact agents have on auditd collection and whether its worth making adjustments in the near future and of course considering the repeated npm supply chain compromises. Nevertheless, fun data!