Suspicious Collection Of AI Assistant Session Data
Query
let LookbackTime = 7d;
let AiToolSensitivePaths = @'\.claude[\\\/](projects|todos|history)[\\\/]|\.claude\.json\b|\.cursor[\\\/](chats|history)[\\\/]|\.continue[\\\/](sessions|history)[\\\/]|\.aider\.chat\.history|\.codeium[\\\/]history[\\\/]|\.windsurf[\\\/]chats[\\\/]';
let ClaudeBootstrap = @'\.claude[\\\/]shell-snapshots[\\\/]snapshot-[a-zA-Z0-9_-]+\.sh';
let ClaudeTemporaryDirectory = @'(?:/private)?/tmp/claude-[a-zA-Z0-9_-]+';
let HomeDirectoryUser = @'(?:[A-Za-z]:\\Users\\|/Users/|/home/)([^\\/"]+)';
let RecursiveEnumeration = @'(-Recurse|-r\b|find\s+\S+\s+-type)';
let SecretKeywords = @'(api[_-]?key|apikey|password|pwd\b|secret|token|auth|credential|cred\b|private[_-]?key|privkey|pkey|access[_-]?key|accesskey|client[_-]?secret|bearer)';
let SearchTool = @'(Select-String|findstr|grep|ripgrep|\brg\b|-Pattern)';
let CollectionAction = @'(Get-Content|\bgc\b|Copy-Item|\bcat\b|\bcp\b|\btype\b|\bcopy\b|Compress-Archive|\btar\b|\bzip\b)';
let AllowlistProcesses = dynamic(["claude.exe", "claude", "code.exe", "msmpeng.exe", "searchindexer.exe", "searchprotocolhost.exe"]);
DeviceProcessEvents
| where TimeGenerated >= ago(LookbackTime)
| extend CleanedCmd = replace_regex(ProcessCommandLine, ClaudeBootstrap, "")
| where CleanedCmd matches regex AiToolSensitivePaths
| where InitiatingProcessFileName !in~ (AllowlistProcesses) and InitiatingProcessParentFileName !in~(AllowlistProcesses) and InitiatingProcessCommandLine !in~(AllowlistProcesses)
| where FileName !in~ (AllowlistProcesses)
| extend HasClaudeTemporaryDirectory = InitiatingProcessCommandLine matches regex ClaudeTemporaryDirectory
| extend IsClaudeCodeWrapper = ProcessCommandLine matches regex ClaudeBootstrap or HasClaudeTemporaryDirectory
| extend PathOwner = tolower(extract(HomeDirectoryUser, 1, CleanedCmd))
| extend AccountNameLower = tolower(AccountName)
| extend AccountLocalPart = tolower(tostring(split(AccountName, "@")[0]))
| extend InitiatingAccountNameLower = tolower(InitiatingProcessAccountName)
| extend PathOwnerMismatch = isnotempty(PathOwner) and isnotempty(AccountNameLower) and PathOwner != AccountNameLower and PathOwner != AccountLocalPart
| extend HasInitiatingAccountMismatch = isnotempty(InitiatingAccountNameLower) and isnotempty(AccountNameLower) and InitiatingAccountNameLower != AccountNameLower
// Exclude known WSL/remote development identities and shared profiles to avoid expected access being treated as a user mismatch.
| where AccountName != "wslg" and PathOwner !in~ ("vscode", "codespace")
| where not(IsClaudeCodeWrapper and not(PathOwnerMismatch))
| extend HasRecursiveEnumeration = CleanedCmd matches regex RecursiveEnumeration
| extend HasSecretKeyword = CleanedCmd matches regex SecretKeywords
| extend HasSearchTool = CleanedCmd matches regex SearchTool
| extend HasCollectionAction = CleanedCmd matches regex CollectionAction
| extend IsHighConfidence = PathOwnerMismatch or (HasCollectionAction and (HasSecretKeyword or HasSearchTool or HasRecursiveEnumeration)) or (HasSecretKeyword and HasSearchTool and HasRecursiveEnumeration)
| where IsHighConfidence
| extend RiskScore =
(iff(HasCollectionAction, 3, 0)) +
(iff(HasSecretKeyword, 2, 0)) +
(iff(HasSearchTool, 1, 0)) +
(iff(HasRecursiveEnumeration, 1, 0)) +
(iff(PathOwnerMismatch, 4, 0)) +
(iff(HasInitiatingAccountMismatch, 1, 0))
| extend Verdict = strcat(
iff(PathOwnerMismatch, "Access to another user's AI assistant data", "Non-AI tool process accessing a sensitive AI assistant path"),
iff(HasCollectionAction, " | File read, copy, or archive action", ""),
iff(HasSecretKeyword, " | Secret keyword in command", ""),
iff(HasSearchTool, " | Pattern search (Select-String/grep/findstr)", ""),
iff(HasRecursiveEnumeration, " | Recursive directory enumeration", "")
)
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessCommandLine, PathOwner, PathOwnerMismatch, HasInitiatingAccountMismatch, IsClaudeCodeWrapper, HasCollectionAction, RiskScore, Verdict
| order by RiskScore desc, TimeGenerated descAbout this query
Explanation
This query is designed to detect suspicious activities related to accessing sensitive data paths associated with AI assistant tools like Claude, Cursor, Continue, Aider, Codeium, or Windsurf. It focuses on identifying non-AI assistant processes that might be trying to access or manipulate this data inappropriately. Here's a simplified breakdown of what the query does:
-
Time Frame: It looks at events from the past 7 days.
-
Sensitive Paths: It identifies specific file paths related to AI assistant tools that are considered sensitive.
-
Allowed Processes: It excludes known legitimate processes (like those related to Claude and some system processes) from being flagged as suspicious.
-
Suspicious Activities: The query checks for:
- Access to sensitive paths by unauthorized processes.
- Actions like reading, copying, or archiving files.
- Searches for secret keywords (like passwords or API keys).
- Recursive directory searches.
- Access to another user's AI assistant data.
-
Confidence and Risk Scoring: It assigns a risk score based on the presence of suspicious activities and mismatches in user accounts. A higher score indicates a higher likelihood of malicious activity.
-
Verdict: It provides a summary of the suspicious activity detected, such as unauthorized access to AI assistant data or file manipulation actions.
-
Output: The query outputs details like the time of the event, device name, account names, process details, and a risk score, sorted by the highest risk first.
Overall, this query helps in identifying potential security threats related to unauthorized access or manipulation of AI assistant session data.