GitSpawn: AI Coding Agent Git-Config RCE
.git/config (via core.fsmonitor, core.hooksPath, attr.tree, or clean/process filters) makes an AI coding agent's routine background git status/git diff execute an attacker-supplied program: as the logged-in user, outside the agent's sandbox, with no approval prompt.Executive Summary
On 2026-09-01, Manifold Security disclosed GitSpawn: a vulnerability class affecting seven AI coding agents (Claude Code, OpenAI Codex, Cursor, Goose, Hermes Agent, Qwen Code, Grok Build) across eight distinct findings. Every one of these agents gathers project context by quietly running git status / git diff in the background: often before the user has typed a prompt, accepted a workspace-trust dialog, or authenticated. Git supports core.fsmonitor, a legitimate performance setting naming a helper program that git launches automatically whenever it refreshes its index. That value is read straight out of the repository's own .git/config. A malicious repository can set it (or the related core.hooksPath, attr.tree, or a clean/process filter command) to an attacker-chosen program, and the moment the agent's background git command touches that repo, the program runs: as the logged-in user, outside the agent's sandbox, and invisible to the agent's own permission/approval system.
Four of the eight findings were still unpatched at publication (Claude Code's second "ultrareview" path, Hermes Agent, Qwen Code, Grok Build). Two formal CVEs anchor the disclosure: CVE-2026-19592 (OpenAI Codex CLI/Desktop, CVSS 7.3, fixed 0.131.0) and CVE-2026-72718 (Goose, fixed 1.44.0). A related, separately-tracked Claude Code vulnerability, CVE-2026-55607 (git-worktree path confusion abusing the same fsmonitor execution sink, CVSS 8.8, fixed 2.1.163), shares the mechanism but a different delivery chain (requires a clone plus prompt-injection content, not merely opening a received folder): see §2 for the reconciliation.
Delivery reality: this cannot arrive via an ordinary git clone, fetch, or pull, since none of those transmit a hostile .git/config. The repository has to arrive as files with its .git directory already inside, exactly how developers routinely receive projects: a zipped project handoff from a contractor, a synced cloud-drive share, a "bug report" archive from a stranger, or a USB stick.
Defender priority: there are no published C2 domains, IPs, or file hashes for this research: the atomic surface is configuration strings and affected agent version numbers, not infrastructure, and the sensor cannot see the contents of a git config file it did not observe being written. The hunt is therefore behavioral: unexpected process ancestry (agent → git → shell/interpreter/network tool), archive extraction immediately preceding a git/agent launch, and the follow-on credential-access and persistence an attacker would chase once code execution lands. Patch/version-pin every affected agent (§12) and inspect .git/config in any repository received as files before opening it in an agent.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| Primary | Manifold Security: "GitSpawn" (2026-09-01) | Original disclosure: mechanism, 8 findings / 7 agents, delivery vector, vendor + recipient mitigation language | Yes |
| News | The Hacker News (2026-09-02) | Per-agent affected/fixed version table; timeline; core.hooksPath/attr.tree named as additional sinks | Yes |
| News | Shattered.io | Corroborates patch-status table and "does not survive git clone" framing | Yes |
| News | ByteIota | Corroborates mechanism and agent list. Two claims dropped: an uncorroborated "FakeGit campaign, 7,600+ poisoned repos" statistic and a Codex CLI "0.152.1+" patch figure that conflicts with the formal CVE record (08) and The Hacker News (02): neither ships anywhere in this pack | Partial |
| News | Cybersecuritynews.com | Corroborates mechanism, "cannot arrive via clone/fetch/pull" delivery-reality language (used near-verbatim in §1/§3), 77M+ monthly npm downloads context for Claude Code | Yes |
| News | GBHackers | Third corroboration of per-agent affected/fixed version numbers | Yes |
| Vendor CVE | GitLab Advisory DB: CVE-2026-55607 | Claude Code sandbox escape via git-worktree path confusion; CVSS 8.8; affected 2.1.38–2.1.162, fixed 2.1.163; requires clone + prompt injection (different delivery chain than the core GitSpawn disclosure: see reconciliation below) | Yes, with caveat |
| Vendor CVE | OpenCVE: CVE-2026-19592 | Authoritative Codex CLI/Desktop affected + fixed version numbers, CVSS 7.3, CWE-15 | Yes |
Reconciliation note: CVE-2026-55607 is related but distinct: Manifold's GitSpawn disclosure says Claude Code's core.fsmonitor path was fixed in 2.1.196 (with a second "ultrareview" path reportedly still live at 2.1.252). CVE-2026-55607 was fixed earlier, in 2.1.163, via a different delivery chain: cloning a malicious repo containing prompt-injection content that steers the agent into vulnerable worktree operations, not simply opening a received folder. This pack cites CVE-2026-55607 for its fsmonitor-abuse detection and hardening value (§6, §9, §12) while keeping its version numbers and delivery chain clearly separate from the GitSpawn-labeled Claude Code findings.
IOC provenance note: no cited source publishes atomic infrastructure indicators (attacker IPs, C2 domains, malware hashes) for GitSpawn: the "indicators" are affected-version strings and the git config keys themselves. §10's Falcon IOC Management CSV block is therefore placeholder-only by design; §4 and §10's behavioral/config blocks are the operative content.
Hunt Brief & Attack Chain
The exploit itself is nothing more than a few text lines in a file the victim already has on disk. All of the detectable behavior happens after an AI coding agent's routine background git command reads that file and launches whatever it names.
Hunt hypotheses (highest fidelity first)
| # | Hypothesis | MITRE | Telemetry | Conf |
|---|---|---|---|---|
| H1 | An AI coding agent binary is the grandparent of git, which is the parent of an interactive shell or scripting interpreter: the core fsmonitor/hooksPath/attr.tree execution chain | T1059 / T1195 | ProcessRollup2 | High |
| H2 | git spawns a child that is not one of its own known helpers: curl/wget, a LOLBin, or a shell: matching a config-named helper program rather than a normal git subprocess | T1059 / T1105 | ProcessRollup2 | High |
| H3 | git is invoked with an explicit -c core.fsmonitor=/-c core.hooksPath=/-c attr.tree= override on the command line (attacker automation, or: legitimately: the vendor-recommended mitigation check) | T1059 | ProcessRollup2 | Medium |
| H4 | An archive-extraction utility (tar/unzip/7z/ditto/Expand-Archive) runs immediately before a git or AI-agent process launches on the same host: the only way this payload can be delivered | T1204.002 / T1027 | ProcessRollup2 | Medium |
| H5 | A process descended from git accesses SSH keys, cloud credential files, or git-credential stores shortly after the execution chain fires | T1552.001 / T1552.004 | ProcessRollup2 | High |
| H6 | git is used to create a worktree named/pathed as .git (CVE-2026-55607-class path confusion) ahead of an fsmonitor execution | T1036.005 / T1211 | ProcessRollup2 | High |
| H7 | A shell/interpreter descended from git makes an outbound network connection: staging a second-stage payload or reaching back to attacker infrastructure | T1105 / T1071 | ProcessRollup2 + NetworkConnectIP4 | Medium |
| H8 | A process descended from git or an AI agent rewrites a shell profile / dotfile (.zshenv, .bash_profile, .profile) for persistence: the exact technique CVE-2026-55607 used to escape the sandbox | T1546.004 | ProcessRollup2 | High |
| H9 | A Windows Run-key or scheduled-task entry is created within minutes of a suspicious git-ancestry process on the same host | T1547.001 / T1053.005 | Reg/Asep + ScheduledTaskRegistered | Medium |
Attack chain
| Step | Action | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Prepare | Attacker crafts a repo whose .git/config names a helper under core.fsmonitor (or hooksPath/attr.tree/filter) | None on the victim's EDR: happens on attacker infrastructure | No detectable telemetry pre-delivery |
| 2 · Deliver | Repo handed off as a zip/synced-folder/USB with .git intact: not a clone/fetch/pull | Archive-extraction process; file-system write of the folder | H4: extraction immediately preceding git/agent launch |
| 3 · Trigger | Victim opens the folder in an AI coding agent; the agent runs a background git status/git diff for context | ProcessRollup2 (git launch) | H3: explicit config override on the git command line, where visible |
| 4 · Execute | git reads .git/config, launches the named helper: the RCE moment: as the logged-in user, outside the agent sandbox | ProcessRollup2 (agent → git → shell/interpreter) | H1/H2/H6: the core execution-chain hunts |
| 5 · Stage/Callback | Spawned shell/interpreter reaches out for a second-stage payload or C2 | NetworkConnectIP4 from a git-descended shell | H7 |
| 6 · Harvest/Persist | Credential-file access, dotfile rewrite, or Run-key/scheduled-task creation | ProcessRollup2 / Reg / Asep / ScheduledTaskRegistered | H5/H8/H9 |
Consolidated IOC Table
No cited source publishes atomic infrastructure indicators (attacker IPs, C2 domains, malware file hashes) for GitSpawn. The "indicators" that exist are affected-component version numbers and the git configuration keys the attack abuses: both are config/behavioral, and both trace directly to the saved source snapshots. Atomic-IOC rows are intentionally omitted rather than fabricated.
| Type | Value / Pattern | Confidence | Action | Context |
|---|---|---|---|---|
| Config key | core.fsmonitor naming a program in a received repo's .git/config | High | hunt | Primary GitSpawn sink (source 01/02) |
| Config key | core.hooksPath, attr.tree, or a clean/process filter command in .git/config / .gitattributes | Medium | hunt | Additional sinks reported by Manifold (source 01/02) |
| Version | Claude Code (fsmonitor path) < 2.1.196 | High | detect | Fixed 2.1.196; ultrareview path unpatched at 2.1.252 (source 01/02/03) |
| Version | Claude Code 2.1.38 – 2.1.162 (CVE-2026-55607) | High | detect | Fixed 2.1.163; worktree path-confusion, CVSS 8.8 (source 07) |
| Version | Goose < 1.44.0 (CVE-2026-72718) | High | detect | Fixed 1.44.0, severity 7.0 (source 01/02/06) |
| Version | Codex CLI 0.102.0 – 0.130.0 (CVE-2026-19592) | High | detect | Fixed 0.131.0, CVSS 7.3 (source 02/08) |
| Version | Codex Desktop macOS ≤ 26.513.31313 / Windows ≤ 26.513.40821 | High | detect | Fixed 26.519.22136 (macOS) / 26.519.21041 (Windows) (source 02/08) |
| Version | Hermes Agent 0.18.2 – 0.21.0 (CVE-2026-71963) | Medium | hunt | Unpatched at publication, vendor unresponsive (source 01/02/06) |
| Version | Qwen Code 0.19.6 – 0.22.3 | Medium | hunt | Unpatched at publication, no CVE assigned (source 01/02/06) |
| Version | Grok Build 0.2.93 – 1.0.13 | Medium | hunt | Unpatched at publication, no CVE assigned (source 01/02/06) |
| Behavior | Archive-extraction process immediately followed by a git/agent launch on the same host | Medium | hunt | The only viable delivery path (source 01/02/05) |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Windows developer endpoints (Claude Code, Codex, Cursor, Grok Build, etc. under Node/Electron) | ProcessRollup2, NetworkConnectIP4, AsepValueUpdate/RegGenericValueUpdate, ScheduledTaskRegistered | Critical | Low: Falcon-native |
| macOS developer endpoints | ProcessRollup2 (zsh/bash/osascript ancestry), NetworkConnectIP4 | Critical | Low: Falcon-native; sandboxed-app nuances per CVE-2026-55607 |
| Linux developer endpoints | ProcessRollup2 (sh/bash/zsh ancestry), NetworkConnectIP4 | Critical | Low: Falcon-native |
The repository's own .git/config contents | None: Falcon does not inspect file contents at rest | Critical | High: sensor sees the process that reads/writes the file, not its contents; no CQL query in §8 can confirm what value core.fsmonitor actually held |
| Archive/zip handling (email gateway, cloud-drive sync, USB) | Process telemetry for the extraction tool only | Medium | Medium: depends on endpoint coverage of the sync/USB path |
There is no attacker infrastructure to hunt for and no C2 domain/IP/hash to import. The single most valuable action for this pack is inventorying which AI coding agents run on which endpoints, at which versions (§10's audit block): that inventory is what turns §8's queries from "any git process" into "a specific unpatched agent's git process."
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Initial Access / Execution | T1195 Supply Chain Compromise / T1059 Command & Scripting Interpreter | Agent's background git command triggers an attacker-named fsmonitor/hooksPath helper | Q1 |
| Execution | T1059 (LOLBins / interpreters spawned by git) | git's child process is not a known git helper | Q2 |
| Execution | T1059 (explicit config override) | git invoked with -c core.fsmonitor=/hooksPath/attr.tree | Q3 |
| Initial Access | T1204.002 Malicious File / T1027 Obfuscated Files | Archive extraction delivering a .git directory just ahead of a git/agent launch | Q4 |
| Credential Access | T1552.001 Credentials in Files / T1552.004 Private Keys | Process descended from git touches SSH keys / cloud credential files | Q5 |
| Defense Evasion / Persistence | T1036.005 Match Legitimate Name / T1211 Exploitation for Defense Evasion | Worktree created named/pathed .git (CVE-2026-55607-class) | Q6 |
| Command & Control | T1105 Ingress Tool Transfer / T1071 Application Layer Protocol | Outbound connection from a shell/interpreter descended from git | Q7 |
| Persistence | T1546.004 Unix Shell Configuration Modification | Dotfile (.zshenv/.bash_profile/.profile) rewritten by a git/agent-descended process | Q8 |
| Persistence | T1547.001 Registry Run Keys / T1053.005 Scheduled Task | Run-key or scheduled-task creation near a suspicious git-ancestry process | Q9 |
Native Audit-Log Hunts (non-CQL)
These checks give you ground truth on exactly what Falcon cannot see: the actual contents of a repository's .git/config, and which AI coding agent build is installed. Run before, or alongside, the CQL in §8.
Inspect a received repository before opening it in an agent (any platform)
git config --get core.fsmonitor git config --get core.hooksPath git config --get attr.tree grep -nE "fsmonitor|hooksPath|\[filter" .git/config .gitattributes 2>/dev/null # Any output naming an external program is a hold-and-review, not an open-in-agent.
Vendor-recommended safe context-gathering (per Manifold / The Hacker News)
# Strip the sink on any ad hoc background git command: git -c core.fsmonitor=false status git -c core.fsmonitor=false diff # Set a permanent default until every agent in use is confirmed patched: git config --global core.fsmonitor false
Inventory installed AI coding agent versions (run fleet-wide via RMM/MDM)
claude --version codex --version cursor --version goose --version grok-build --version 2>/dev/null qwen-code --version 2>/dev/null # Compare output against the affected/fixed table in Section 4 and the audit block in Section 10.
macOS-specific: confirm sandbox/seatbelt profile is current (CVE-2026-55607 surface)
- Confirm Claude Code is at 2.1.163+ (CVE-2026-55607) and 2.1.196+ (GitSpawn core.fsmonitor path): both are required; they are different fixes.
- Review
~/.zshenv,~/.zprofile, and~/.bash_profilefor unexpected recent modification timestamps.
CrowdStrike LogScale CQL Hunt Queries
// TUNING: exclusion: read it before you deploy.Looks for: the exact GitSpawn execution chain: an AI coding agent binary as the grandparent, git as the parent, and a shell or scripting interpreter as the child. This is the moment core.fsmonitor/core.hooksPath/attr.tree actually fires. FP: git hooks, aliases, and editor integrations legitimately spawn shells; tune the agent-binary and shell lists to your fleet's actual toolset.
// HUNT: AI coding agent -> git -> shell/interpreter (GitSpawn core execution chain) // MITRE: T1059, T1195 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: legitimate git hooks, shell aliases, editor/IDE git integrations spawning a shell // TUNING: narrow GrandparentBaseFileName to the agent binaries actually deployed in your fleet; exclude known internal hook scripts by ParentCommandLine #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | GrandparentBaseFileName=/^(claude|claude-code|node|codex|codex-cli|cursor|cursor-agent|goose|goosed|hermes|hermes-agent|qwen-code|qwen|grok-build|grok)(\.exe)?$/i | ParentBaseFileName=/^git(\.exe)?$/i | FileName=/^(cmd|powershell|pwsh|wscript|cscript|mshta|rundll32|regsvr32)\.exe$|^(sh|bash|zsh|dash|osascript|python3?|perl|ruby|node)$/i | table([ComputerName, UserName, GrandparentBaseFileName, ParentBaseFileName, FileName, CommandLine, ParentCommandLine], limit=200)
Looks for: git launching a child that is neither an interactive shell (covered by Q1) nor one of git's own known helper binaries: a download tool, LOLBin, or scripting host, which is what a core.fsmonitor/hooksPath program typically resolves to when it isn't a plain shell. FP: credential-manager helpers and SSH askpass programs; the explicit helper exclusion keeps this narrow, but confirm your environment's git-credential-manager binary name is covered.
// HUNT: git spawns a child that is not a known git helper (fsmonitor/hooksPath payload) // MITRE: T1059, T1105 // CONF: high FP: medium COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: git-credential-manager/askpass helpers not covered by the exclusion list below // TUNING: add your credential-manager/askpass binary name to the FileName!= exclusion before deploying as a Detect IOA #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^git(\.exe)?$/i | FileName=/^(curl|wget|certutil|bitsadmin|mshta|rundll32|regsvr32|powershell|pwsh|cmd)\.exe$|^(curl|wget|sh|bash|zsh|python3?|osascript|nc|ncat|socat)$/i | FileName!=/^git-(remote-https|remote-http|credential-.*|upload-pack|receive-pack|shell|sh)(\.exe)?$/i | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, ParentCommandLine], limit=200)
Looks for: a git command line that sets core.fsmonitor, core.hooksPath, or attr.tree via -c: could be attacker automation staging the sink, or an analyst/engineer legitimately running the vendor-recommended git -c core.fsmonitor=false status mitigation. FP: the vendor mitigation itself will match; triage by whether the value is being set to a program (bad) or disabled (expected/good).
// HUNT: git invoked with -c core.fsmonitor=/-c core.hooksPath=/-c attr.tree= on the command line // MITRE: T1059 // CONF: medium FP: medium COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: the vendor-recommended mitigation "git -c core.fsmonitor=false status" matches this pattern and is expected/benign // TUNING: exclude CommandLine containing "fsmonitor=false" to isolate the risky (non-"false") sets from the benign mitigation runs #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^git(\.exe)?$/i | CommandLine=/-c\s+(core\.fsmonitor|core\.hooksPath|attr\.tree)\s*=/i | table([ComputerName, UserName, ParentBaseFileName, FileName, CommandLine], limit=200)
Looks for: the only viable delivery path for GitSpawn: a repository arriving as an archive (zip, tar, 7z) that already contains a .git directory, unpacked immediately before a git or agent process launches on the same host. This cannot happen via git clone. FP: high: routine project setup, dependency installs, and CI runners unpacking build artifacts all match; the join correlates by host only, not by directory, so run it over a narrow window.
// HUNT: Archive extraction immediately followed by git or AI-agent process launch (delivery vector) // MITRE: T1204.002, T1027 // CONF: medium FP: high COST: medium | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 // FALSE POSITIVES: routine project setup (dependency installs unpacking archives), CI runners unzipping build artifacts, legitimate contractor handoffs // TUNING: run over a narrow window (console time picker, 30-60 min) so the join only correlates temporally close events; exclude CI/build-agent hosts by ComputerName and known software-distribution paths by CommandLine // LOOKBACK: run over 30-60m, not the default -- see TUNING #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^(claude|claude-code|node|codex|cursor|goose|hermes|qwen-code|grok-build)(\.exe)?$/i | join(query={ #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(tar|unzip|7z|7za|ditto|winrar)(\.exe)?$/i OR FileName=/^Expand-Archive$/i | rename(field=FileName, as=ExtractTool) | rename(field=CommandLine, as=ExtractCommandLine) | table([aid, ExtractTool, ExtractCommandLine]) }, field=aid, include=[ExtractTool, ExtractCommandLine], mode=inner) | table([ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, GrandparentBaseFileName, ExtractTool, ExtractCommandLine], limit=200)
Looks for: the natural next step after landing code execution through GitSpawn: reading SSH private keys, cloud CLI credential files, npm/git credential stores, or netrc. FP: low; legitimate tooling rarely references these exact paths on the command line.
// HUNT: Credential/secret file access by a process descended from git (post-compromise credential harvesting) // MITRE: T1552.001, T1552.004, T1555 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^git(\.exe)?$/i | (CommandLine="*.ssh/id_rsa*" OR CommandLine="*.ssh\\id_rsa*" OR CommandLine="*.ssh/id_ed25519*" OR CommandLine="*.aws/credentials*" OR CommandLine="*.aws\\credentials*" OR CommandLine="*.config/gcloud*" OR CommandLine="*.npmrc*" OR CommandLine="*.netrc*" OR CommandLine="*.git-credentials*") | table([ComputerName, UserName, ParentBaseFileName, GrandparentBaseFileName, FileName, CommandLine], limit=200)
Looks for: a git worktree created with a name/path of .git, which git tooling then interprets as a real git directory rather than a working tree: the CVE-2026-55607 technique that abuses fsmonitor execution via symlink/path confusion. FP: low; legitimate workflows do not name a worktree .git.
// HUNT: Git worktree created with a name/path of ".git" (worktree/symlink path confusion) // MITRE: T1036.005, T1211 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^git(\.exe)?$/i | CommandLine=/worktree\s+add/i | (CommandLine="*worktree add .git*" OR CommandLine="*worktree add \".git\"*" OR CommandLine="*worktree add './.git'*") | table([ComputerName, UserName, ParentBaseFileName, GrandparentBaseFileName, CommandLine], limit=200)
Looks for: a shell or scripting interpreter descended from git that also makes an outbound connection off the internal network: staging a second-stage payload after the RCE moment. FP: high: this correlates by host, not by exact process, so any unrelated outbound traffic from the host in the same window will match; narrow the window and exclude known-good destinations before trusting hit volume.
// HUNT: Outbound network connection from a shell/interpreter descended from git (post-RCE staging/C2) // MITRE: T1105, T1071 // CONF: medium FP: high COST: medium | REQUIRES: ProcessRollup2, NetworkConnectIP4 // FALSE POSITIVES: host-level correlation, not process-level -- any unrelated outbound traffic in the same window will match // TUNING: run over a narrow window (console time picker, 15-30 min); exclude allow-listed corporate-egress destinations by RemoteAddressIP4 // LOOKBACK: run over 15-30m, not the default -- see TUNING #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | FileName=/^(cmd|powershell|pwsh)\.exe$|^(sh|bash|zsh|dash|osascript|python3?|perl|ruby|node|curl|wget)$/i | ParentBaseFileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^git(\.exe)?$/i | join(query={ #event_simpleName=NetworkConnectIP4 | !cidr(RemoteAddressIP4, subnet="10.0.0.0/8") | !cidr(RemoteAddressIP4, subnet="172.16.0.0/12") | !cidr(RemoteAddressIP4, subnet="192.168.0.0/16") | !cidr(RemoteAddressIP4, subnet="127.0.0.0/8") | table([aid, RemoteAddressIP4, RemotePort]) }, field=aid, include=[RemoteAddressIP4, RemotePort], mode=inner) | table([ComputerName, UserName, FileName, ParentBaseFileName, GrandparentBaseFileName, CommandLine, RemoteAddressIP4, RemotePort], limit=200)
Looks for: a process descended from git or an AI coding agent that references a shell profile / dotfile on its command line: the exact technique the public CVE-2026-55607 writeup describes (overwriting ~/.zshenv) to gain persistence and code execution outside the sandbox. FP: low; legitimate dotfile edits by these ancestry chains are rare.
// HUNT: Shell-profile / dotfile modification by a process descended from git or an AI coding agent // MITRE: T1546.004 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2, SyntheticProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | (ParentBaseFileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^(claude|claude-code|node|codex|cursor|goose|hermes|qwen-code|grok-build)(\.exe)?$/i) | (CommandLine="*.zshenv*" OR CommandLine="*.zshrc*" OR CommandLine="*.zprofile*" OR CommandLine="*.bash_profile*" OR CommandLine="*.bashrc*" OR CommandLine="*.profile*") | table([ComputerName, UserName, ParentBaseFileName, GrandparentBaseFileName, FileName, CommandLine], limit=200)
Looks for: a registry Run-key/Asep value or scheduled task created on the same host, in the same narrow window, as a process descended from git: the Windows persistence parallel to Q8. FP: medium; installers and legitimate software updates create Run keys and tasks constantly, so this must be scoped tightly. ⚠ validate in tenant: parent-process linkage on Asep/registry events varies by sensor version; this query correlates by host and time window, not a direct process-id join.
// HUNT: Registry Run-key or scheduled-task persistence created shortly after a suspicious git-ancestry process // MITRE: T1547.001, T1053.005 // CONF: medium FP: medium COST: medium | REQUIRES: AsepValueUpdate or RegGenericValueUpdate, ScheduledTaskRegistered, ProcessRollup2 // FALSE POSITIVES: legitimate installers and software updates create Run keys/scheduled tasks constantly // TUNING: run over a narrow window (console time picker, 15-30 min); exclude known installer/update ComputerName groups (e.g. patch-management windows) // LOOKBACK: run over 15-30m, not the default -- see TUNING // NOTE: AsepValueUpdate/RegGenericValueUpdate field availability is sensor-version dependent -- validate in your tenant before deploying as a Detect IOA #event_simpleName=/AsepValueUpdate|RegGenericValueUpdate|ScheduledTaskRegistered/ | join(query={ #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | ParentBaseFileName=/^git(\.exe)?$/i OR GrandparentBaseFileName=/^git(\.exe)?$/i | rename(field=CommandLine, as=GitAncestryCommandLine) | table([aid, GitAncestryCommandLine]) }, field=aid, include=[GitAncestryCommandLine], mode=inner) | table([ComputerName, UserName, RegObjectName, RegStringValue, TaskName, TaskExecutable, GitAncestryCommandLine], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA Name | Pattern | Exclusions | Deployment |
|---|---|---|---|
| GitSpawn Execution Chain | Process · agent-binary grandparent → git parent → shell/interpreter child | Known internal git hooks, editor git integrations | Detect → promote from Q1 after a benign-baseline pass |
| Git Non-Helper Child | Process · git parent spawning curl/wget/LOLBin, excluding known git helpers | Credential-manager/askpass helpers | Detect from Q2 after tuning the exclusion list |
| Worktree Path Confusion | Process · git CommandLine worktree add .git | None recommended | Prevent/Detect: high confidence (Q6) |
| Post-Git Credential Access | Process · descendant of git touching SSH keys/cloud creds/netrc | None recommended | Detect (high severity) from Q5 |
| Post-Git Dotfile Rewrite | Process · descendant of git/agent referencing shell profile files | None recommended | Detect from Q8 |
Q3, Q4, Q7, and Q9 are best run as scheduled searches / investigate-only, not endpoint Prevent IOAs: Q3 matches the vendor's own recommended mitigation command, and Q4/Q7/Q9 use host-level time-window correlation that needs a tenant-specific tuning pass before it can safely block.
L1 triage checklist (for a Q1/Q2/Q6 hit)
- Confirm the AI coding agent's installed version against the affected/fixed table in §4: is this endpoint actually running a vulnerable build?
- Pull the full command line and working directory of the flagged git process; identify the repository path.
- If the repository is reachable, inspect its
.git/configand.gitattributesforcore.fsmonitor,core.hooksPath,attr.tree, or filter commands (§7). - Identify how the repository arrived: git log/reflog will show no clone history for a files-delivered repo; check Downloads, sync-folder, or USB provenance.
- Check the flagged host for Q5 (credential access), Q7 (outbound connections), Q8/Q9 (persistence) hits in the same window.
- If the helper program executed, treat the endpoint as compromised: rotate any credentials the logged-in user could reach (SSH keys, cloud tokens, git-credential store, saved API tokens).
- Isolate the host via Falcon RTR pending investigation; preserve the offending repository folder for analysis rather than deleting it.
- Patch/upgrade the agent to a fixed version (§12) before returning the host to service.
Pivot queries
- Host-scoped: re-run Q1, Q5, Q7, Q8, Q9 filtered to the single flagged
aidover the preceding 24h to reconstruct the full session. - Environment-wide: re-run Q4 (archive-extraction → git/agent) across all developer endpoints for the prior 7 days to check for other deliveries of the same repository.
Machine-Readable IOC Appendix
No atomic infrastructure IOCs are published for GitSpawn, so the Falcon IOC-Management block below uses REPLACE_WITH_* placeholders: populate only from confirmed incident evidence (a recovered helper binary, observed C2). The behavioral, config-key, and version-audit blocks are the operative content for this pack.
type,value,action,severity,expiration,description,tags sha256,REPLACE_WITH_HELPER_BINARY_HASH,detect,high,2027-03-25,GitSpawn fsmonitor/hooksPath helper (confirm in incident),campaign:GitSpawn domain,REPLACE_WITH_C2_DOMAIN,detect,high,2026-12-25,GitSpawn post-exploit C2 (confirm in incident),campaign:GitSpawn ipv4,REPLACE_WITH_C2_IP,detect,high,2026-12-25,GitSpawn post-exploit callback (confirm in incident),campaign:GitSpawn # NOTE: no atomic infrastructure IOCs were published for GitSpawn -- do NOT import blank rows. # ACTION COLUMN: every row ships at "detect", matching the Section 4 action vocabulary. # A hash you recovered from your own confirmed incident is the only row worth raising to # "prevent", and only after you have verified the binary is not a renamed legitimate tool. # Never raise the domain or ipv4 rows to "prevent" on one observation: post-exploit # callbacks land on shared and reassigned hosting, so a prevent-mode block there can cut # legitimate traffic. No gate downstream re-checks this column, so it is yours to own.
# GitSpawn behavioral indicators # 1. AI coding agent binary as grandparent of git, which is parent of a shell/interpreter # 2. git spawning a child that is not one of its documented helper binaries # 3. git invoked with an explicit "-c core.fsmonitor=" / "-c core.hooksPath=" / "-c attr.tree=" override # 4. Archive-extraction tool run immediately before a git/agent process launch, same host # 5. Process descended from git touching SSH keys, cloud credential files, or git-credential stores # 6. git worktree created named/pathed ".git" (path-confusion, CVE-2026-55607-class) # 7. Outbound network connection from a shell/interpreter descended from git # 8. Shell-profile/dotfile (.zshenv, .bash_profile, .profile) rewritten by a git/agent-descended process
# Settings a repository's own .git/config can weaponize (GitSpawn sinks):
core.fsmonitor # names a helper git runs automatically on every index refresh
core.hooksPath # redirects git's hook directory to an attacker-controlled path
attr.tree # names a tree-ish used to resolve .gitattributes -- can be abused
# in combination with clean/process/smudge filter commands
filter.<name>.clean # arbitrary command run by "git add" for matching paths
filter.<name>.process # arbitrary long-running command run by git for matching paths
# Presence of ANY of these naming an executable/script in a received repo = hold and review.
# Per-endpoint audit: is the installed agent version fixed for GitSpawn? claude --version # need 2.1.196+ (fsmonitor path) AND 2.1.163+ (CVE-2026-55607, worktree) codex --version # need 0.131.0+ (CLI) / 26.519.22136+ (macOS) / 26.519.21041+ (Windows) goose --version # need 1.44.0+ (CVE-2026-72718) cursor --version # confirm patched build per vendor release notes (no version disclosed publicly) qwen-code --version # UNPATCHED at publication -- treat as vulnerable regardless of version grok-build --version # UNPATCHED at publication -- treat as vulnerable regardless of version hermes-agent --version # UNPATCHED at publication -- treat as vulnerable regardless of version # Any repo-inspection command from the "grab-keys" block above should run before opening # a received (non-cloned) folder in any of the above, especially the still-unpatched agents.
Detection Validation Gates
| Gate | Check | Pass Criteria |
|---|---|---|
| Telemetry ready | ProcessRollup2/SyntheticProcessRollup2 flowing from all developer-endpoint platforms (Windows/macOS/Linux); NetworkConnectIP4 present | All three platforms represented in a 24h sample; EDR latency < 5 min |
| Agent inventory ready | Fleet-wide agent/version audit (§10 grab-audit) completed and mapped to §4's affected/fixed table | Every developer endpoint's agent + version documented |
| Benign baseline | Run Q1/Q2 over a known-clean week; catalog legitimate hook/editor-integration ancestry chains | FP set documented and added to each query's exclusion list |
| Positive test | Q1/Q6/Q8 fire on a lab host running a benign script that reproduces the exact ancestry shape (agent-launched shell under git; a worktree literally named test-dot-git; a dotfile edit under a git-descended shell): never a functional fsmonitor payload | Each query returns the seeded activity |
| Promotion | Q1, Q2, Q6, Q5, Q8 → Detect IOA after baseline; Q3/Q4/Q7/Q9 stay scheduled-search/investigate-only | Promoted with documented exclusions per §9 |
Hardening: Tiered
- Disable the fsmonitor sink globally on every developer endpoint:
git config --global core.fsmonitor false: until every AI coding agent in use is confirmed on a fixed build. (MITRE M1042 Disable or Remove Feature or Program · Manifold/The Hacker News vendor mitigation guidance) - Version-pin and upgrade every affected agent to the fixed builds in §4/§10 (Claude Code 2.1.196+ and 2.1.163+, Codex CLI 0.131.0+, Goose 1.44.0+); treat Hermes Agent, Qwen Code, and Grok Build as unpatched regardless of version until vendor fixes ship. (MITRE M1051 Update Software · CVE-2026-19592 / CVE-2026-72718 / CVE-2026-55607 fix records)
- Instruct developers to inspect
.git/configbefore opening any repository received as files (zip, cloud-drive share, USB, "bug report" archive): never a normal clone/fetch/pull: using the audit commands in §7/§10. (MITRE M1017 User Training · ⚠ best-practice, no formal benchmark)
- Deploy Q1/Q2/Q6 as scheduled searches (promoting to Detect IOAs after the benign-baseline pass in §11) so the execution chain is caught even if an agent is missed during the version-pin rollout. (MITRE M1040 Behavior Prevention on Endpoint)
- Route archive/zip handoffs (contractor deliverables, "bug report" attachments) through a scan-and-quarantine step that flags any archive containing a
.gitdirectory for manual config review before it reaches a developer's agent. (MITRE M1038 Execution Prevention · ⚠ best-practice, no formal benchmark) - Set
safe.directoryscoping so git does not implicitly trust arbitrary extracted/synced folders, reducing the surface that silently triggers background context-gathering commands. (MITRE M1042 · git-scm.com safe.directory documentation)
- Standardize on agent versions via a managed software catalog with automatic update enforcement for AI coding agents, closing the "which build is actually installed" gap that made this class of bug persist across releases. (MITRE M1051 · CIS Control 2: Inventory and Control of Software Assets)
- Application allowlisting / execution control on developer endpoints for the helper-program shape this bug abuses: an unsigned or unexpected binary/script launched as a child of git should require explicit approval. (MITRE M1038 · NIST SP 800-167 Application Whitelisting)
- Sandbox or VM-isolate first-time-seen repositories (contractor handoffs, external contributions received as files) before opening them in any AI coding agent, so a live fsmonitor/hooksPath payload cannot reach the primary developer identity/credential set even if it fires. (MITRE M1048 Application Isolation and Sandboxing · NIST SP 800-207 Zero Trust Architecture)
Deployable Playbooks
Playbook A: Disable the fsmonitor sink fleet-wide (Windows / macOS / Linux, shell)
Prerequisites: git 2.x installed; ability to push a global gitconfig change via RMM/MDM/Ansible to developer endpoints. Reboot required: none. Rollback: re-enable with git config --global core.fsmonitor true (or remove the line from the global gitconfig) once every agent in the fleet is confirmed patched.
# 1. Push to every developer endpoint's per-user git global config git config --global core.fsmonitor false # 2. Verify it took effect git config --get core.fsmonitor # expect: false # 3. (Optional, stronger) also neutralize the other reported sinks globally where supported by your git version: git config --global core.hooksPath "" # Rollback: git config --global --unset core.fsmonitor # git config --global --unset core.hooksPath
Playbook B: Version-pin and upgrade affected agents
Prerequisites: package-manager or installer access for each agent (npm/pip/brew/winget/direct installer); a maintenance window is not required since these are user-space CLI tools, but active agent sessions should be closed first. Reboot required: none for CLI agents; Codex Desktop / Cursor (Electron apps) require the application to restart, not the OS. Rollback: pin back to the prior version only if the upgrade breaks a workflow: re-exposes the vulnerability, so pair any rollback with Playbook A staying active.
# Claude Code -- confirm BOTH fixes are present (they are different bugs): npm install -g @anthropic-ai/claude-code@latest # need >=2.1.196 AND >=2.1.163 claude --version # OpenAI Codex CLI -- need >=0.131.0 npm install -g @openai/codex@latest codex --version # Goose -- need >=1.44.0 # (use your platform's Goose install/update mechanism; verify via) goose --version # Unpatched at publication -- Hermes Agent, Qwen Code, Grok Build: # no fixed version exists yet. Apply Playbook A (disable core.fsmonitor) and # Playbook C (repo intake review) as compensating controls until a fix ships. # Rollback: revert to the previously pinned package version via your package # manager's install-specific-version syntax if the new build breaks a workflow.
Playbook C: Repository intake review (any platform received as files, not cloned)
Prerequisites: none beyond a shell and git itself; suitable for a pre-commit-style local script or a SOAR/EDR RTR script triggered on archive extraction. Reboot required: none. Rollback: none required: this is a read-only inspection step with no persistent system change.
#!/usr/bin/env bash # Run against any repository that arrived as a zip/tar/synced-folder/USB copy # (never needed for a plain "git clone" -- that path cannot carry these settings). REPO_DIR="$1" cd "$REPO_DIR" || exit 1 echo "== Checking $REPO_DIR/.git/config and .gitattributes for named-program sinks ==" grep -nE "fsmonitor|hooksPath|\[filter" .git/config .gitattributes 2>/dev/null if [ $? -eq 0 ]; then echo "HOLD: this repo's config names an external program -- review before opening in any AI coding agent." exit 1 fi echo "No known GitSpawn sink strings found. Still confirm agent version against Section 4/10 before proceeding."
Playbook D: Safe-directory scoping (git >=2.35.2, all platforms)
Prerequisites: git version supporting safe.directory (2.35.2+, effectively all currently-supported git releases). Reboot required: none. Rollback: git config --global --unset-all safe.directory restores git's prior implicit-trust behavior.
# Scope trust to explicitly-approved project roots instead of trusting every # directory git is pointed at (reduces the set of folders where an agent's # background git command will silently operate on an unreviewed .git/config): git config --global --add safe.directory /Users/*/projects/* git config --global --add safe.directory C:/Users/*/source/* # Verify: git config --get-all safe.directory # Rollback: git config --global --unset-all safe.directory
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the flagged host via Falcon RTR; suspend the offending AI-agent process tree without deleting the source repository folder | SOC / EDR | Containment event; process tree snapshot |
| Eradicate | Kill the helper-program process tree; preserve (do not delete) the malicious repository's .git/config and .gitattributes for analysis; upgrade the agent to a fixed build (§12/§13B); apply core.fsmonitor=false fleet-wide if not already (§13A) | Platform / SOC | Preserved config file copy; agent version post-upgrade; git-config audit output |
| Recover | Rotate SSH keys, cloud CLI credentials, npm/git-credential tokens, and any API tokens reachable by the logged-in user on the affected host; re-run Q5/Q8/Q9 to confirm no residual credential access or persistence | IAM / SOC | Credential-rotation record; clean re-run of Q5/Q8/Q9 |
| Monitor | Run Q1–Q9 as scheduled searches for 30 days across all developer endpoints; watch for repeat delivery of the same or a similarly-crafted repository | SOC | Scheduled-search results; repeat-offender tracking |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1195 / T1059 GitSpawn execution chain | Agent → git → shell/interpreter | Q1 | Sched. search → Detect | Good |
| T1059 / T1105 Non-helper git child | git spawns non-helper binary | Q2 | Detect | Good |
| T1059 Config override on command line | Explicit -c core.fsmonitor=/hooksPath/attr.tree | Q3 | : | Partial: also matches the benign vendor mitigation |
| T1204.002 / T1027 Delivery via archive | Extraction → git/agent launch | Q4 | : | Partial: host-level correlation, high FP without tuning |
| T1552.001 / .004 Credential access | Git-descended process reads SSH/cloud creds | Q5 | Detect | Good |
| T1036.005 / T1211 Worktree path confusion | Worktree named/pathed .git | Q6 | Prevent/Detect | Good |
| T1105 / T1071 Post-RCE staging/C2 | Outbound from a git-descended shell | Q7 | : | Partial: host-level correlation, high FP without tuning |
| T1546.004 Dotfile persistence (macOS/Linux) | Shell-profile rewrite by git/agent descendant | Q8 | Detect | Good |
| T1547.001 / T1053.005 Windows persistence | Run-key/scheduled-task near git ancestry | Q9 | : | Partial: Asep/registry parent linkage is tenant/sensor-version dependent, ⚠ validate |
| T1195 The exploit trigger itself (git reading a malicious .git/config) | fsmonitor/hooksPath/attr.tree value being read from disk | : | : | GAP: Falcon does not inspect file contents at rest; only the resulting process launch (Q1/Q2/Q3) is visible |
Known gap: there is no way to detect a malicious .git/config value purely from its presence on disk with EDR telemetry: the sensor sees the process that reads it (the git launch) and the process it spawns, not the file's contents. This pack closes that gap behaviorally (§8 Q1–Q3) rather than via file-content inspection, and closes it operationally via the intake-review playbook (§13C) that DOES read the file. Validation gates for all CQL are in §11.
Hunt Summary Ticket
TITLE: GitSpawn -- AI coding agent RCE via a received repository's own .git/config
SEVERITY: HIGH (sandbox-escape RCE, CVSS up to 8.8 across component CVEs; no confirmed ITW)
SCOPE: All Windows/macOS/Linux developer endpoints running Claude Code, OpenAI Codex,
Cursor, Goose, Hermes Agent, Qwen Code, or Grok Build
HYPOTHESIS: A repository received as files (zip/sync/USB, never a clone) with a malicious
core.fsmonitor/hooksPath/attr.tree setting causes an AI coding agent's background
git command to execute an attacker-named helper as the logged-in user, outside
the agent sandbox, followed by credential access and/or persistence.
QUERIES: Q1 execution chain - Q2 non-helper child - Q3 config override - Q4 archive delivery
- Q5 credential access - Q6 worktree confusion - Q7 outbound stage/C2
- Q8 dotfile persistence (macOS/Linux) - Q9 Run-key/task persistence (Windows)
DO FIRST: 1) Fleet-wide agent/version audit against Section 4/10; flag every endpoint
running an unpatched build (all of Hermes Agent, Qwen Code, Grok Build
are unpatched regardless of version)
2) Push "git config --global core.fsmonitor false" fleet-wide (Playbook A)
3) Run Q1/Q2/Q6 across all developer endpoints for the prior 7 days
FINDINGS: <record execution-chain hits, credential-access hits, and any persistence found>
GAPS: No EDR visibility into .git/config file contents -- only the resulting process
launch is detectable. No atomic infrastructure IOCs published for this research.
ACTIONS: Disable fsmonitor sink + version-pin/upgrade agents + repo-intake review process
+ credential rotation on any confirmed-executed host + 30-day scheduled hunt
OWNER: HuntPack
VERSION: v0.1 · 2026-09-25
Changelog
References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| Primary | Manifold Security: "GitSpawn" | Original disclosure: mechanism, 8 findings/7 agents, delivery vector, mitigation language | 2026-09-25 |
| News | The Hacker News | Per-agent affected/fixed version table, disclosure timeline, additional sink names | 2026-09-25 |
| News | Shattered.io | Corroborates patch-status table and clone-immunity framing | 2026-09-25 |
| News | ByteIota | Corroborates mechanism/agent list only: two uncorroborated claims from this source were dropped, see §2 | 2026-09-25 |
| News | Cybersecuritynews.com | Mechanism detail, "cannot arrive via clone/fetch/pull" delivery-reality language | 2026-09-25 |
| News | GBHackers | Third corroboration of per-agent affected/fixed version numbers | 2026-09-25 |
| Vendor CVE | GitLab Advisory DB: CVE-2026-55607 | Claude Code worktree path-confusion sandbox escape: affected/fixed versions, CVSS 8.8, CWE-22/59/78 | 2026-09-25 |
| Vendor CVE | OpenCVE: CVE-2026-19592 | OpenAI Codex CLI/Desktop affected/fixed versions, CVSS 7.3, CWE-15 | 2026-09-25 |
Generated 2026-09-25 · HuntPack pipeline (web-hunter-v2 → threat-hunter-v2 → crowdstrike-logscale-v3 → alert-builder-v2 → threat-hardening-v2 → review-v2) · Defensive content only.