THREAT-C2-CloudflaredQuickTunnelC2 Google Chronicle · YARA-L

Detect Command and Control via Cloudflared Quick Tunnel and Token-Based Cloudflare Tunnel in Google Chronicle

The cloudflared daemon is Cloudflare's legitimate, signed connector for Cloudflare Tunnel. It makes only outbound connections to Cloudflare's edge (region*.v2.argotunnel.com, port 7844 over QUIC or HTTP/2) and Cloudflare then proxies inbound requests back through that connection to any local service. Adversaries abuse two modes. In quick-tunnel mode, `cloudflared tunnel --url http://localhost:PORT` requires no account and returns a random *.trycloudflare.com hostname, giving an attacker a throwaway HTTPS front for a web shell, C2 listener or file server. In token mode, `cloudflared tunnel run --token <JWT>` or `cloudflared service install <token>` binds the host to an attacker-controlled Cloudflare account and can route RDP, SSH or SMB to the operator, persistently if installed as a service. Because the traffic is outbound TLS/QUIC to a high-reputation CDN, it bypasses inbound firewall rules, egress allowlists that trust Cloudflare, and domain-reputation controls. This record differs from T1090.002/T1572 base logic and THREAT-C2-VSCodeTunnelGitHubDeviceCodeC2 in keying on cloudflared-specific indicators: tunnel subcommand arguments, argotunnel/trycloudflare destinations, UDP/TCP 7844, and binaries outside the managed install path. Detection scores three independent signals per host (CLI bootstrap, edge egress, unmanaged binary path) and alerts when two or more coincide.

MITRE ATT&CK

Tactic
Command and Control

YARA-L Detection Query

Google Chronicle (YARA-L)
yaral
rule cloudflared_quick_tunnel_c2 {
  meta:
    author = "Detection Engineering"
    description = "Detects cloudflared tunnel bootstrap (quick tunnel --url or token-based run/service install), a legitimate Cloudflare feature abused to expose a host and carry C2 over outbound-only TLS/QUIC."
    mitre_attack_tactic = "Command and Control"
    mitre_attack_technique = "T1572"
    severity = "HIGH"
    priority = "HIGH"

  events:
    $e.metadata.event_type = "PROCESS_LAUNCH"
    $e.target.process.file.full_path = /(?i)\\cloudflared(\.exe)?$|\/cloudflared$/
    $e.target.process.command_line = /(?i)tunnel.*(--url|--token|\brun\b|service install)/
    $host = $e.principal.hostname

  match:
    $host over 1h

  outcome:
    $tunnel_events = count($e.metadata.id)

  condition:
    $e and $tunnel_events >= 1
}
high severity medium confidence

Chronicle YARA-L rule matching cloudflared process launches with tunnel quick-tunnel, token run or service install arguments.

Data Sources

Google Chronicle SIEMChronicle UDM PROCESS_LAUNCH events (EDR forwarders)

Required Tables

PROCESS_LAUNCH (UDM event type)

False Positives & Tuning

  • Platform or DevOps teams running an approved Cloudflare Tunnel (cloudflared service) to publish internal web applications through Zero Trust, which will show the same token-run command line and argotunnel egress
  • Developers using a quick tunnel (trycloudflare.com) to demo a local web app or test webhooks from a laptop

Other platforms for THREAT-C2-CloudflaredQuickTunnelC2


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1 Cloudflared Quick Tunnel to Local Web Server (Linux)

    Expected signal: Process create for cloudflared with args 'tunnel --url http://localhost:8089'; DNS lookups for api.trycloudflare.com and region*.v2.argotunnel.com; outbound connections to port 7844.

  2. Test 2 Cloudflared Quick Tunnel from Temp Directory (Windows)

    Expected signal: Sysmon Event ID 1 for %TEMP%\cloudflared.exe with tunnel --url; Event ID 22 for *.trycloudflare.com / argotunnel.com; Event ID 3 to port 7844.

  3. Test 3 Cloudflared Token-Based Service Install (Windows, Invalid Token)

    Expected signal: Process create with 'service install <token>'; Windows service creation event 7045 / Sysmon registry events for the Cloudflared service key.


Response Playbook

Triage

  1. Confirm which arms fired. A quick-tunnel command line (`--url`) or a `--token` run from a user-writable path is near-conclusive; a service install under Program Files may be an approved Zero Trust connector.
  2. Extract the full command line. For `--url`, note the local service being exposed (e.g. 3389, 445, 8080) — this reveals what the attacker wanted reachable. For `--token`, decode the JWT payload (base64) to obtain the account tag (a) and tunnel ID (t) for takedown requests and cross-host correlation.
  3. Identify the parent process and launching user. A scripted parent (powershell, cmd, wmic, a scheduled task, a web server worker) is far more suspicious than an administrator shell.
  4. Check with platform/network teams whether the organisation operates Cloudflare Tunnel and whether the account tag matches the corporate account.
  5. Look for the binary's origin: download via PowerShell/curl/bitsadmin from github.com/cloudflare/cloudflared releases or an unusual host shortly before execution (T1105).
  6. Search the fleet for the same tunnel ID or token fragment and for other hosts resolving *.argotunnel.com from non-approved processes.

Containment

  1. Isolate the host via EDR network containment; if the tunnel exposes RDP/SSH the attacker may have an interactive session right now.
  2. Kill the cloudflared process and stop/remove the service (`sc stop cloudflared`, `cloudflared service uninstall`), then preserve the binary and token first.
  3. Block the tunnel at the edge: sinkhole *.trycloudflare.com for non-approved segments and restrict outbound 7844/UDP+TCP to approved connector hosts only.
  4. Report the account tag/tunnel ID to Cloudflare abuse so the attacker tunnel is disabled.
  5. Reset credentials for accounts active on the host and any service exposed through the tunnel.

Evidence Collection

  1. Copy cloudflared binary, its hash, and any config (`config.yml`, `cert.pem`, credentials JSON) from %USERPROFILE%\.cloudflared or /etc/cloudflared
  2. Process creation records with full command line and parent chain; Windows service registry key HKLM\SYSTEM\CurrentControlSet\Services\Cloudflared for token-installed persistence
  3. Network logs for connections to 7844 and argotunnel/trycloudflare hostnames, with byte counts and session duration
  4. Proxy/DNS logs for the trycloudflare subdomain to identify what the exposed service was and who accessed it
  5. Authentication logs on the exposed service (RDP 4624 type 10, SSH auth logs) covering the tunnel window

Escalation Criteria

  • !Tunnel exposes RDP, SSH, SMB or a shell-like service and the host is a server, domain controller or holds sensitive data
  • !Token-based tunnel registered to an account that is not the organisation's
  • !Cloudflared found on multiple hosts, or installed as a service for persistence
  • !Evidence of preceding intrusion activity (phishing execution, exploitation, credential dumping) on the host

Investigation Guide

Related Techniques

Forensic Artifacts

  • >cloudflared binary hash and Authenticode signature/OriginalFileName (renamed copies keep OriginalFileName cloudflared.exe)
  • >%USERPROFILE%\.cloudflared\ (cert.pem, <tunnel-id>.json credentials, config.yml) or /etc/cloudflared/
  • >Windows service key HKLM\SYSTEM\CurrentControlSet\Services\Cloudflared whose ImagePath embeds the tunnel token
  • >Sysmon Event ID 1 command line and Event ID 22 DNS records for *.argotunnel.com and *.trycloudflare.com
  • >Sysmon Event ID 3 connections to destination port 7844 from the cloudflared process
  • >Prefetch/Amcache entries for cloudflared.exe from non-standard directories

Tuning Guidance

If the organisation uses Cloudflare Tunnel, inventory approved connector hosts first and exclude those DeviceIds (not subnets or accounts). Better still, allowlist your own account tag by comparing the decoded token in the command line. Quick-tunnel (`--url` / trycloudflare.com) usage has almost no enterprise justification outside developer subnets and should alert without an allowlist. Raise the score threshold to 3 for developer segments; keep it at 2 for servers.


Hunting Queries

Finds non-browser processes resolving or connecting to Cloudflare tunnel infrastructure across 30 days, surfacing cloudflared installs that have not yet been bootstrapped from a monitored command line.

Hunting — KQL
kql
// Hunt: any host contacting Cloudflare tunnel edge or quick-tunnel domains by a non-browser process
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where RemoteUrl has_any ("argotunnel.com", "trycloudflare.com") or RemotePort == 7844
| where InitiatingProcessFileName !in~ ("chrome.exe", "msedge.exe", "firefox.exe")
| summarize Connections = count(), Procs = make_set(InitiatingProcessFileName, 10), Urls = make_set(RemoteUrl, 10), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName
| sort by Connections desc
Hunting — SPL
spl
index=windows sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=22
| eval q=lower(QueryName)
| where like(q, "%.argotunnel.com") OR like(q, "%.trycloudflare.com")
| stats count, values(Image) as Images, min(_time) as FirstSeen, max(_time) as LastSeen by ComputerName, q
| sort - count

Atomic Red Team Tests

Test 1 Cloudflared Quick Tunnel to Local Web Server (Linux)
linux

Starts a throwaway local HTTP server and exposes it with a cloudflared quick tunnel, exercising CLI and edge-egress arms. Requires cloudflared installed; no Cloudflare account needed.

Command

bash
command -v cloudflared >/dev/null || { echo 'cloudflared not installed; skipping'; exit 0; }
python3 -m http.server 8089 --directory /tmp >/dev/null 2>&1 &
echo $! > /tmp/atomic_http.pid
timeout 30 cloudflared tunnel --url http://localhost:8089 --no-autoupdate >/tmp/atomic_cf.log 2>&1 || true
grep -o 'https://[a-z0-9-]*\.trycloudflare\.com' /tmp/atomic_cf.log | head -1

Cleanup

bash
pkill -f 'cloudflared tunnel --url' 2>/dev/null; kill $(cat /tmp/atomic_http.pid) 2>/dev/null; rm -f /tmp/atomic_http.pid /tmp/atomic_cf.log; true

Expected Telemetry

Process create for cloudflared with args 'tunnel --url http://localhost:8089'; DNS lookups for api.trycloudflare.com and region*.v2.argotunnel.com; outbound connections to port 7844.

Expected Detection

CLI and edge-egress arms both fire, giving TunnelScore >= 2 and an alert on the host.

Test 2 Cloudflared Quick Tunnel from Temp Directory (Windows)
windows

Runs cloudflared.exe from a user-writable temp path with the quick-tunnel flag to trigger all three arms (CLI, unmanaged path, edge egress). Requires a cloudflared.exe copied to %TEMP%.

Command

powershell
$exe = Join-Path $env:TEMP 'cloudflared.exe'
if (-not (Test-Path $exe)) { Write-Host 'Place cloudflared.exe in %TEMP% first; skipping'; exit 0 }
Start-Process -FilePath $exe -ArgumentList 'tunnel','--url','http://localhost:8080','--no-autoupdate' -WindowStyle Hidden
Start-Sleep -Seconds 25
Write-Host 'cloudflared quick tunnel test complete'

Cleanup

powershell
Get-Process -Name cloudflared -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $env:TEMP 'cloudflared.exe') -Force -ErrorAction SilentlyContinue

Expected Telemetry

Sysmon Event ID 1 for %TEMP%\cloudflared.exe with tunnel --url; Event ID 22 for *.trycloudflare.com / argotunnel.com; Event ID 3 to port 7844.

Expected Detection

All three arms fire (TunnelScore = 3); KQL, SPL, EQL and YARA-L rules alert.

Test 3 Cloudflared Token-Based Service Install (Windows, Invalid Token)
windows

Attempts `cloudflared service install` with a deliberately invalid token to produce the persistence command line and service creation artifacts without binding to any real account. Run only on a lab host.

Command

powershell
$exe = (Get-Command cloudflared -ErrorAction SilentlyContinue).Source
if (-not $exe) { Write-Host 'cloudflared not installed; skipping'; exit 0 }
& $exe service install eyJhIjoiMDAwMCIsInQiOiIwMDAwIiwicyI6IjAwMDAifQ== 2>&1 | Out-Host

Cleanup

powershell
& (Get-Command cloudflared).Source service uninstall 2>$null
sc.exe delete Cloudflared 2>$null

Expected Telemetry

Process create with 'service install <token>'; Windows service creation event 7045 / Sysmon registry events for the Cloudflared service key.

Expected Detection

CLI arm fires on service install; combine with edge DNS from the service start to reach TunnelScore 2.

Related Detections

Detection Variants (1)

Different telemetry and tradecraft for the same technique — pick the one that matches the data you collect.