- Honeypot
- Catching in the Wild
- Network Analysis
Catching Cloudflare WARP Leaking Real IPs Through Tor
A visitor reached a Beelzebub honeypot emulating Ollama through six Tor exit nodes across four countries. Every request carried five Cloudflare headers, including the visitor's real IP. The undocumented header Cf-Warp-Tag-Id persisted across two sessions from two different source IPs, linking an anonymous VPS to a residential broadband connection two hours apart.
Zachary Gardner
Cybersecurity Researcher
TL;DR
- A visitor reached a Beelzebub honeypot emulating Ollama through six Tor exit nodes across four countries. Every request carried five Cloudflare headers, including the visitor’s real IP.
- The undocumented header Cf-Warp-Tag-Id persisted across two sessions from two different source IPs, linking an anonymous VPS to a residential broadband connection two hours apart.
- Cloudflare’s managed WARP Gateway injects attribution headers at the HTTP layer before traffic enters Tor. Tor transports TCP and does not inspect HTTP content. The headers survive intact.
- This is an architectural mismatch, not a Tor vulnerability. Any HTTP-level proxy that stamps identifying headers upstream of a TCP-layer anonymizer will leak them through it.
- Prior art: SANS ISC Diary 32532 (Ullrich, Dec 2025). This post extends that work with cross-IP correlation, Tor survival analysis, and evidence from a non-Cloudflare destination.
Background
Cloudflare WARP ships in two tiers. The consumer tier (1.1.1.1 app) encrypts DNS traffic to Cloudflare’s edge. The managed tier (Zero Trust / Gateway) adds HTTP-level inspection, TLS termination, and policy enforcement.
In December 2025, Johannes Ullrich at SANS ISC observed Cf-Warp-Tag-Id in honeypot scanning traffic and framed it as CDN bypass (https://isc.sans.edu/diary/32532). A 2023 test (https://github.com/szepeviktor/cloudflare-warp-http-headers) concluded WARP “adds no extra headers,” but that test used the consumer app. Managed WARP through Gateway behaves differently. Cloudflare does not document the distinction, and Cf-Warp-Tag-Id does not appear in their official headers reference (https://developers.cloudflare.com/fundamentals/reference/http-headers/).
The Honeypot Setup
The sensor runs Beelzebub with protocol: "http" emulating Ollama on port 11434. Static handlers serve /api/tags and /api/version. The LLMHoneypot plugin handles /api/generate and /api/chat with dynamic responses.
Decision Rationale
| Decision | Rationale |
|---|---|
| Port 11434 | Default Ollama binding. Scanners expect it. |
Static /api/tags | Passes scanner validation without a running model. |
| LLM plugin on inference endpoints | Dynamic responses sustain engagement past initial probes. |
Beelzebub’s HTTP handler logs the full request header map in the HeadersMap field of every trace event. Non-standard headers, including Cloudflare’s, are captured automatically. No additional configuration required.
apiVersion: "v1"
protocol: "http"
address: ":11434"
description: "Ollama LLM inference server"
commands:
- regex: "^/api/tags$"
handler: |
{"models":[{"name":"llama3:latest","model":"llama3:latest",
"modified_at":"2024-12-15T10:30:00Z","size":4661211808,
"digest":"sha256:abc123","details":{"format":"gguf",
"family":"llama","parameter_size":"8.0B",
"quantization_level":"Q4_0"}}]}
headers:
- "Content-Type: application/json"
statusCode: 200
- regex: "^/api/version$"
handler: '{"version":"0.5.4"}'
headers:
- "Content-Type: application/json"
statusCode: 200
- regex: "^/$"
handler: "Ollama is running"
statusCode: 200
- regex: "^/api/(generate|chat)$"
plugin: "LLMHoneypot"
headers:
- "Content-Type: application/json"
statusCode: 200
The port has no Cloudflare infrastructure in the request path. No Tunnel, no DNS proxying, no reverse proxy.
Timeline of Events
65.109.30[.]32 (Hetzner, Helsinki) connected directly. Client: python-requests/2.32.5. Queried /api/tags, received a model list, sent test prompts, disconnected. No Cloudflare headers present. This session established a baseline for the IP.
What the Honeypot Captured
Embedding Requests (Phase 2)
The xdash-client/1.0 sessions sent POST requests to /api/embed with model: nomic-embed-text. The request bodies contained real-world content, not test strings:
A 4,093-character software developer resume, sent multiple times. The resume contains verifiable PII (name, GitHub profile, education, employment history). OSINT confirms the individual exists. The resume dates to approximately 2019–2020 based on star counts and missing recent roles. This is scraped job board data, not synthetic test content.
{
"model": "nomic-embed-text",
"input": "[REDACTED] Senior Software Engineer... San Jose State University...
Experience: [REDACTED large tech companies]... Open source:
[REDACTED project name] (12K+ stars)... Skills: React, Node.js,
TypeScript, Python, AWS, Docker, Kubernetes..."
}
Subsequent requests contained job posting search queries and full position listings:
"SAP ABAP developer with Fiori and CDS experience"
"SAP ABAP developer with 5 years experience"
Two Australian job listings followed (Melbourne-based companies, one requiring NV1 security clearance). Between these, a debug string: "test embedding provider path".
The content pattern is consistent with building a vector-similarity job matching system on an endpoint the visitor does not own.
Chat Session (Phase 3)
The residential session hit /api/chat. The honeypot’s LLM-backed response handler served a command menu, then planted credentials, then improvised a database schema across nine prompts. The full captured sequence:
Visitor: help
Server: Available commands: GET /path, POST /path {json},
env, dbquery "SQL", creds
Visitor: creds
Server: AWS_ACCESS_KEY_ID=AKIA[REDACTED]
AWS_SECRET_ACCESS_KEY=[REDACTED]
DATABASE_URL=postgresql://[REDACTED]@db-primary.
[REDACTED]:5432/[REDACTED]
VAULT_TOKEN=hvs.[REDACTED]
Visitor: env
Server: [Same credentials repeated as environment variables]
Visitor: SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public';
Server: ["users", "orders", "shipments", "inventory", "invoices"]
Visitor: select * from orders
Server: [5 rows with order_id, user_id, amount, status, created_at]
Visitor: SELECT * FROM orders
Server: [Same result, case-corrected query]
Visitor: SELECT column_name FROM information_schema.columns
WHERE table_name = 'orders';
Server: [order_id, user_id, product_id, quantity, amount,
status, created_at]
Visitor: SELECT column_name FROM information_schema.columns
WHERE table_name = 'users';
Server: [id, username, email, password_hash, created_at, updated_at]
Visitor: SELECT id, username, email, password_hash,
created_at, updated_at FROM users;
Server: [5 rows with bcrypt password hashes]
Nine prompts over 8.5 minutes. The LLM maintained consistent table names, column schemas, and data types across every response. None of this database exists. The progression from information_schema.tables to column enumeration to password_hash is standard PostgreSQL introspection. A leading space in one query (" select * from orders") confirms manual input.
The planted credentials include a live canary tripwire. If the AWS key is used against any AWS API, an alert fires. No alert attributable to this visitor has been recorded.
How WARP Defeats Tor
Managed WARP creates a WireGuard tunnel to Cloudflare’s edge. The Gateway proxy terminates TLS, inspects HTTP, and stamps attribution headers before forwarding.
When the next hop is Tor:

Tor encrypts and relays TCP. Each relay sees only encrypted bytes from the previous hop. No relay parses HTTP or modifies content. The Cloudflare headers are bytes inside the TCP payload. Three hops of onion encryption do not touch them.
This Is Not a Tor Vulnerability
This is not a Tor vulnerability. Tor anonymizes the network layer as designed. WARP operates at the HTTP layer, above Tor’s reach. Any HTTP-level proxy that stamps identifying headers before traffic enters Tor will leak them through Tor.
Spoofing Assessment
Cf-Connecting-Ip is set server-side by Cloudflare on the inbound TCP connection. It cannot be spoofed in transit. The Finnish IP was independently observed connecting directly ten days earlier. The French IPv6 belongs to a consumer ISP. Neither is a plausible fabrication. Cf-Ipcountry matches real GeoIP. Cf-Ray datacenter suffixes match the geographic path. Cdn-Loop confirms Cloudflare processed the request.
Threat Actor Profile
| Attribute | Value |
|---|---|
| Source IP (automated) | 65.109.30[.]32 (Hetzner, Helsinki, FI) |
| Source IP (residential) | 2a01:e0a:fe2:4de0:fac:5473:377e[:]1cde (Free SAS, AS12322, FR) |
| WARP Tag | b87b4e0d-XXXX-XXXX-XXXX-5c7413e6d372 |
| Client (automated) | xdash-client/1.0, no public footprint |
| Client (residential) | Chrome 146, Linux, fr-FR locale |
| Tor exits | 6 across SE, NL, DE, CH |
| Active window | March 21 to April 1, 2026 |
| AbuseIPDB (VPS) | 0 reports |
Indicators of Compromise
Network IOCs
| IOC | Description |
|---|---|
65.109.30[.]32 | Hetzner VPS, Helsinki. Direct and WARP source. |
xdash-client/1.0 | Custom HTTP client. Unique JA4H fingerprint. |
Detection Signatures
- header_present: "Cf-Warp-Tag-Id"
destination_not_cloudflare: true
note: "WARP Gateway traffic on non-Cloudflare server"
- header_present: "Cdn-Loop"
value_contains: "cloudflare"
destination_not_cloudflare: true
note: "Cloudflare loop header with no CF relationship"
Detection Rules
title: Cloudflare WARP Header Leakage to Non-Cloudflare Destination
id: 7a3c9f12-e8b4-4d91-a6f5-2c1d8e9b4a73
status: experimental
description: >
Detects WARP Gateway attribution headers on a server with no
Cloudflare infrastructure. Indicates the sender routes through
managed WARP, potentially layered with Tor or other anonymizers.
references:
- https://isc.sans.edu/diary/32532
- https://honeypot.observer/blog/warp
author: honeypot.observer
date: 2026/04/06
tags:
- attack.command_and_control
- attack.t1071.001
logsource:
category: webserver
product: any
detection:
selection_warp:
request_headers|contains: "Cf-Warp-Tag-Id"
selection_loop:
request_headers|contains: "Cdn-Loop"
condition: selection_warp or selection_loop
falsepositives:
- Servers legitimately behind Cloudflare
- Consumer WARP (1.1.1.1) may not inject these headers
level: medium Key Findings
Managed WARP injects five identifying headers that survive Tor. Cf-Connecting-Ip, Cf-Warp-Tag-Id, Cf-Ipcountry, Cf-Ray, and Cdn-Loop are stamped at the HTTP layer. Tor operates at TCP. The headers arrive intact regardless of relay count.
Cf-Warp-Tag-Id correlates sessions across IPs. The same UUID appeared from a VPS and a residential connection two hours apart. Without this header, the sessions appear unrelated.
Consumer and managed WARP behave differently. The consumer 1.1.1.1 app adds no headers. The managed Gateway tier injects five. Cloudflare does not document this. Operators should not assume WARP traffic is uniform.
The finding generalizes beyond WARP. Any HTTP-level proxy that stamps headers upstream of a TCP-layer anonymizer will produce this leak. WARP is the most deployed example.
Recommendations
For Honeypot Operators
- Index
HeadersMapfrom Beelzebub HTTP events. All non-standard headers are captured there. - Correlate
Cf-Warp-Tag-Idacross sessions. Repeated UUIDs from different IPs link them to a single WARP enrollment. - Use LLM-backed responses on inference endpoints. Engagement past the initial probe increases captured headers and behavioral data per session.
For Defenders
- Flag
Cdn-Loop: cloudflareon servers with no Cloudflare relationship. It means traffic transited Cloudflare upstream. - Treat
Cf-Connecting-Ipon non-Cloudflare servers as the real source IP.
For WARP Users
Do Not Layer WARP with Tor
Do not layer WARP with Tor. WARP stamps your real IP into HTTP headers at a layer Tor cannot reach.
MITRE ATT&CK
| Technique | ID |
|---|---|
| Application Layer Protocol: Web | T1071.001 |
| Multi-hop Proxy | T1090.003 |
| Network Service Discovery | T1046 |
Limitations
One operator, one sensor, two correlated sessions. The Cf-Warp-Tag-Id persistence claim rests on one UUID from two IPs. No controlled reproduction was performed. Consumer WARP may not inject these headers. Cloudflare has published no documentation on this header’s lifecycle.
The mechanism is architectural and not specific to this case. HTTP-layer injection before TCP-layer anonymization will produce this result regardless of provider.
References
Observed on a Beelzebub sensor emulating Ollama on port 11434. Research by honeypot.observer. Sensor infrastructure details redacted.
- SANS ISC Diary 32532 (Ullrich, Dec 2025): https://isc.sans.edu/diary/32532
- Consumer WARP header test (2023): https://github.com/szepeviktor/cloudflare-warp-http-headers
- Cloudflare HTTP Headers Reference: https://developers.cloudflare.com/fundamentals/reference/http-headers/
- Honeypot Observer: https://honeypot.observer/
- Beelzebub Honeypot Framework: https://github.com/beelzebub-labs/beelzebub