
# Sicherheitsforschungs-Labor: Reproduktion von CVE-2025-61584 (GHSA-9g7x-737f-5xpc) — Befehlsinjektion über github.head_ref im pull_request_target-Workflow (.github/workflows/pr.yml)
Automatisiertes Forschungsobjekt – nicht das Upstream-Projekt.
Dieses Repository ist ein Wegwerf-Labor, das von einer automatisierten Testumgebung für eine Masterarbeit an der Université Laval zur Reproduktion veröffentlichter GitHub-Actions-Workflow-Schwachstellen erstellt wurde. Es ist ein wörtlicher Schnappschuss von
serverless-dns/serverless-dnsbeim Commitb0b1a1538aeb1991b5bc13dfe4e18e686913b12e(2025-04-26), weiterverbreitet unter der eigenen Lizenz dieses Projekts, deren Datei unverändert in diesem Schnappschuss enthalten ist.Das Upstream-Projekt ist nicht beteiligt, wird niemals angegriffen, und die hier untersuchte Schwachstelle ist bereits öffentlich bekannt. Jedes Secret und jede Variable in diesem Repository ist ein zufällig generierter Dummy-Wert – es sind keine echten Anmeldedaten vorhanden. Aktionsreferenzen und Runner-Images sind auf den Stand vom 2025-04-26 fixiert; siehe
pinning.mdin der Testumgebungs-Ausgabe für jede am Schnappschuss vorgenommene Änderung.Fragen oder Einwände: [email protected]
serverless-dns ist ein Pi-Hole-artiger Inhaltsblocker, serverloser, Stub-DNS-over-HTTPS- (DoH) und DNS-over-TLS- (DoT) Resolver. Läuft sofort einsatzbereit auf , , und . Die kostenlosen Stufen all dieser Dienste sollten ausreichen, um den DNS-Verkehr von 10 bis 20 Geräten pro Monat abzudecken.
RethinkDNS betreibt serverless-dns in Produktion an diesen Endpunkten:
| Cloud-Plattform | Serverstandorte | Protokoll | Domain | Nutzung |
|---|---|---|---|---|
| ⛅ Cloudflare Workers | 280+ (ping) | DoH | sky.rethinkdns.com | konfigurieren |
| 🦕 Deno Deploy | 30+ (ping) | DoH | private Beta | |
| ⏱️ Fastly Compute@Edge | 80+ (ping) | DoH | private Beta | |
| 🪂 Fly.io | 30+ (ping) | DoH und DoT | max.rethinkdns.com | konfigurieren |
Die serverseitige Verarbeitung dauert zwischen 0 Millisekunden (ms) und 2 ms (Median), und die Ende-zu-Ende-Latenz (variiert je nach Region und Netzwerk) liegt zwischen 10 ms und 30 ms (Median).
Der Rethink DNS-Resolver auf Fly.io wird von FOSS United gesponsert.
Cloudflare Workers ist die einfachste Plattform, um serverless-dns einzurichten:
Für Schritt-für-Schritt-Anleitungen siehe:
| Plattform | Schwierigkeit | Laufzeit | Dokumentation |
|---|---|---|---|
| ⛅ Cloudflare | Einfach | v8 Isolates | Hosting auf Cloudflare Workers |
| 🦕 Deno.com | Mittel | Deno Isolates | Hosting auf Deno.com |
| ⏱️ Fastly Compute@Edge | Einfach | Fastly JS | Hosting auf Fastly Compute@Edge |
| 🪂 Fly.io | Schwer | Node MicroVM | Hosting auf Fly.io |
Um Blocklisten einzurichten, besuche https://<meine-domain>.tld/configure in deinem Browser (es sollte etwas Ähnliches wie die Konfigurationsseite von RethinkDNS geladen werden).
Für Hilfe oder Unterstützung kannst du gerne ein Issue eröffnen oder einen Patch einreichen.
Node:
# in das Arbeitsverzeichnis wechseln
cd /my/work/dir
# dieses Repository klonen
git clone https://github.com/serverless-dns/serverless-dns.git
# in serverless-dns wechseln
cd ./serverless-dns
Node:
# node v22+ über nvm installieren, falls erforderlich
# https://github.com/nvm-sh/nvm#installing-and-updating
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install --lts
# Abhängigkeiten herunterladen
npm i
# (optional) Abhängigkeiten aktualisieren
npm update
# serverless-dns auf node ausführen
./run n
# einen clinicjs.org-Profiler ausführen
./run n [cpu|fn|mem]
Deno:
# deno.land v2+ installieren
# https://github.com/denoland/deno/#install
curl -fsSL https://deno.land/install.sh | sh
# serverless-dns auf deno ausführen
./run d
Fastly:
# node v22+ über nvm installieren, falls erforderlich
# die Fastly-CLI installieren
# https://developer.fastly.com/learning/tools/cli
# serverless-dns auf Fastly Compute@Edge ausführen
./run f
Wrangler:
# Cloudflare Workers (cli) aka Wrangler installieren
# https://developers.cloudflare.com/workers/cli-wrangler/install-update
npm i wrangler --save-dev
# serverless-dns auf Cloudflare Workers (cli) ausführen
# Stelle sicher, dass Wrangler zuerst eingerichtet ist:
# https://developers.cloudflare.com/workers/cli-wrangler/authentication
./run w
# wrangler mit Chrome DevTools profilieren
# blog.cloudflare.com/profiling-your-workers-with-wrangler
Commits in diesem Repository erzwingen den Google-JavaScript-Styleguide (ref: .eslintrc.cjs).
Ein Git-pre-commit-Hook führt Linter (eslint) und Formatierer (prettier) auf .js-Dateien aus. Verwende git commit --no-verify,
um diesen Hook zu umgehen.
Pull-Requests werden ebenfalls auf Verstöße gegen den Codestil überprüft und wo möglich automatisch korrigiert.
Konfiguriere env.js, wenn du die Standardwerte anpassen musst.
Für Cloudflare Workers richte stattdessen Umgebungsvariablen in wrangler.toml ein.
Für Fastly Compute@Edge richte stattdessen Umgebungsvariablen in fastly.toml ein.
serverless-dns unterstützt die Authentifizierung mit einem alphanumerischen Bearer-Token sowohl für DoH als auch für DoT. Für ein Token, msg-key (Geheimnis), hänge die Ausgabe von hex(hmac-sha256(msg-key|domain.tld), msg) an die Umgebungsvariable ACCESS_KEYS im CSV-Format an. Hinweis: msg ist derzeit auf sdns-public-auth-info festgelegt.
msg-key am Ende des Blockstempels, wie folgt:
1:1:4AIggAABEGAgAA:<msg-key> (hier ist 1 die Version, 1:4AIggAABEGAgAA
der Blockstempel, <msg-key> das Auth-Geheimnis und : das Trennzeichen).msg-key am Ende des SNI (Domainnamens), der den Blockstempel enthält:
1-4abcbaaaaeigaiaa-<msg-key> (hier ist 1 die Version, 4abcbaaaaeigaiaa
der Blockstempel, <msg-key> das Auth-Geheimnis und - das Trennzeichen).Wenn die Absicht besteht, Auth auch mit DoT zu verwenden, halte msg-key kürzer (8 bis 24 Zeichen), da Subdomains insgesamt nur 63 Zeichen lang sein dürfen.
Du kannst die Zugriffsschlüssel für deinen Fork von max.rethinkdns.com generieren, wie folgt:
msgkey="ShortAlphanumericSecret"
domain="my-serverless-dns-domain.tld"
curl 'https://max.rethinkdns.com/genaccesskey?key='"$msgkey"'&dom='"$domain"
# Ausgabe
# {"accesskey":["my-serverless-dns-domain.tld|deadbeefd3adb33fa2bb33fd3eadf084beef3b152beefdead49bbb2b33fdead83d3adbeefdeadb33f"],"context":"sdns-public-auth-info"}
serverless-dns kann so eingerichtet werden, dass Protokolle über Cloudflare Logpush hochgeladen werden.
CF_ACCOUNT_ID=<hex-cloudflare-account-id>
CF_API_KEY=<api-key-with-logs-edit-permission-at-account-level>
R2_BUCKET=<r2-bucket-name>
R2_ACCESS_KEY=<r2-access-key-for-the-bucket>
R2_SECRET_KEY=<r2-secret-key-with-read-write-permissions>
# optional, richte einen Filter ein, sodass nur Protokolle dieses Workers übertragen werden; wenn du
# jedoch keinen Filter auf den Workernamen (script-name) benötigst, bearbeite das Feld "filter" unten entsprechend.
SCRIPT_NAME=<name-of-the-worker-as-in-wrangler-toml>
# für weitere Optionen, ref: developers.cloudflare.com/logs/get-started/api-configuration
# Logpush-API mit cURL: developers.cloudflare.com/logs/tutorials/examples/example-logpush-curl
# Verfügbare Logpull-Felder: developers.cloudflare.com/logs/reference/log-fields/account/workers_trace_events
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/logpush/jobs" \
-H "Authorization: Bearer ${CF_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"name": "dns-logpush",
"logpull_options": "fields=EventTimestampMs,Outcome,Logs,ScriptName×tamps=rfc3339",
"destination_conf": "r2://'"$R2_BUCKET"'/{DATE}?access-key-id='"${R2_ACCESS_KEY}"'&secret-access-key='"${R2_SECRET_KEY}"'&account-id='"{$CF_ACCOUNT_ID}"',
"dataset": "workers_trace_events",
"filter": "{\"where\":{\"and\":[{\"key\":\"ScriptName\",\"operator\":\"contains\",\"value\":\"'"${SCRIPT_NAME}"'\"},{\"key\":\"Outcome\",\"operator\":\"eq\",\"value\":\"ok\"}]}}",
"enabled": true,
"frequency": "low"
}'
logpush = true in wrangler.toml, was Logpush aktiviert.LOG_LEVEL = "logpush", die die Protokollebene so anhebt, dass nur Anfrage- und Fehlerprotokolle ausgegeben werden.LOGPUSH_SRC = "csv,of,subdomains", die log-pusher.js veranlasst, Anfrage-Protokolle nur dann auszugeben, wenn der Workers-hostname eine der Subdomains enthält.An R2 veröffentlichte Protokolle können entweder über R2 Workers, die R2-API oder die Logpush-API abgerufen werden.
Workers Analytics wird, falls aktiviert, gegen einen Protokollschlüssel, lid, übertragen, der, wenn nicht angegeben, auf den Hostnamen der serverlosen Bereitstellung gesetzt wird, wobei Punkte, ., durch Unterstriche, _, ersetzt werden. Auth muss eingerichtet sein, wenn du über die API nach Analytics fragst, die ein JSON zurückgibt; z. B.: https://max.rethinkdns.com/1:<optional-stamp>:<msg-key>/analytics?t=<time-interval-in-mins>&f=<field-name>. Mögliche fields sind ip (Client-IP), qname (DNS-Abfragename), region (Resolver-Region), qtype (DNS-Abfragetyp), dom (Top-Level-Domains), ansip (DNS-Antwort-IPs) und cc (Antwort-IP-Ländercodes).
Protokollerfassung und Analysen sind für Fly und Deno Deploy noch nicht implementiert.
Deno Deploy (Cloud) und Deno (die Laufzeit) legen nicht dieselbe API-Oberfläche offen (z. B. unterstützt Deno Deploy nur HTTP/S-Server-Listener; während Deno zusätzlich zu einfachem HTTP und HTTP/S auch rohes TCP/UDP/TLS unterstützt).
Außer auf Node verwendet serverless-dns DoH-Upstreams, die durch Umgebungsvariablen definiert sind, CF_DNS_RESOLVER_URL / CF_DNS_RESOLVER_URL_2.
Auf Node ist der Standard-DNS-Upstream 1.1.1.2 (ref) oder der rekursive DNS-Resolver unter fdaa::3, wenn auf Fly.io ausgeführt.
Die Einstiegspunkte für Node und Deno sind src/server-node.js bzw. src/server-deno.ts,
und beide lauschen auf TCP-over-TLS-, HTTP/S-Verbindungen; während der Einstiegspunkt für Cloudflare Workers, der nur über HTTP (cli) oder
über HTTP/S (prod) lauscht, src/server-workers.js ist; und für Fastly ist es src/server-fastly.js.
Lokale (Nicht-Prod-)Einrichtungen auf Node lesen key- (privat) und cert- (öffentliche Kette) Dateien standardmäßig aus
Pfaden, die in Umgebungsvariablen definiert sind, TLS_KEY_PATH und TLS_CRT_PATH.
Für die Produktionseinrichtung auf Node (auf Fly.io) muss entweder TLS_OFFLOAD auf true gesetzt werden oder key und cert müssen
base64 in der Umgebungsvariablen TLS_CERTKEY kodiert sein (ref), wie folgt:
# ENTWEDER: TLS an fly.io auslagern und tls_offload auf true setzen
TLS_OFFLOAD="true"
# ODER: base64-Darstellung sowohl von key (privat) als auch cert (öffentliche Kette)
TLS_CERTKEY="KEY=b64_key_content\nCRT=b64_cert_content"
Für Deno werden key- und cert-Dateien aus Pfaden gelesen, die in Umgebungsvariablen definiert sind, TLS_KEY_PATH und TLS_CRT_PATH (ref).
Der Prozess-Start ist für jede dieser Laufzeiten unterschiedlich: Für Node steuert src/core/node/config.js den Start;
während es für Deno src/core/deno/config.ts ist, und für Workers ist es src/core/workers/config.js.
src/system.js Pub-Sub koordiniert die Start-Phase zwischen verschiedenen Modulen.
Auf Node und Deno wird der prozessinterne DNS-Cache durch @serverless-dns/lfu-cache unterstützt; Cloudflare Workers wird sowohl durch die Cache-Web-API als auch durch
prozessinterne LFU-Caches unterstützt. Um das Caching auf allen drei Plattformen vollständig zu deaktivieren, setze die Umgebungsvariable PROFILE_DNS_RESOLVES=true.
Cloudflare Workers und Deno Deploy sind ephemer, d. h. der "Prozess", der Client-Anfragen bedient, ist nicht langlebig, und tatsächlich können zwei aufeinanderfolgende Anfragen von zwei verschiedenen Isolates ("Prozessen") bedient werden. Fastly Compute@Edge ist ebenfalls ephemer, verwendet jedoch keine Isolates; stattdessen erstellt und zerstört Fastly für jede Anfrage eine wasmtime-Sandbox. Der Resolver auf Fly.io, der Node ausführt, wird durch persistente VMs unterstützt und ist daher langlebiger, wie traditionelle "serverfull"-Umgebungen.
Für Deno Deploy wird die Codebasis mit deno bundle in einer einzigen JavaScript-Datei gebündelt und dann
an Deno.com übergeben.
Die Build- und Laufzeitkonfigurationen von Cloudflare Workers sind in wrangler.toml definiert.
Webpack5 bündelt die Dateien in einem ESM-Modul, das dann von Wrangler zu Cloudflare hochgeladen wird.
Die Build- und Laufzeitkonfigurationen von Fastly Compute@Edge sind in fastly.toml definiert.
Webpack5 bündelt die Dateien in einem ESM-Modul, das dann von npx js-compute-runtime zu WASM kompiliert
und anschließend mit der Fastly-CLI verpackt und auf Fastly Compute@Edge veröffentlicht wird.
Für Fly.io, das Node ausführt, sind die Laufzeitanweisungen in fly.toml definiert (verwendet für dev- und live-Bereitstellungstypen),
während die Bereitstellungsanweisungen in node.Dockerfile sind. flyctl richtet
serverless-dns entsprechend auf der Infrastruktur von Fly.io ein.
# für cloudflare workers.dev bauen und bereitstellen
npm run build
# normalerweise ist env-name prod
npx wrangler publish [-e <env-name>]
# für fastly compute@edge bündeln, bauen und bereitstellen
# developer.fastly.com/reference/cli/compute/publish
fastly compute publish
# für fly.io bauen und bereitstellen
npm run build:fly
flyctl deploy --dockerfile node.Dockerfile --config <fly.toml> [-a <app-name>] [--image-label <some-uniq-label>]
Für Bereitstellungen, die die TLS-Terminierung an Fly.io auslagern (B1-Bereitstellungstyp), sind die Laufzeitanweisungen stattdessen in
fly.tls.toml definiert, das HTTP2-Cleartext und HTTP/1.1 auf Port 443 sowie DNS over TCP auf Port 853 einrichtet.
Ref: github/workflows.
190+ Blocklisten werden in einem Succinct Radix Trie komprimiert (basiert auf Steve Hanovs Implementierung) mit Modifikationen,
um die Zeichenfolgensuche (lookup) auf Kosten der "Succinctness" zu beschleunigen. Die Blocklisten werden mit
Unix-Zeitstempel versioniert (definiert in src/basicconfig.json, heruntergeladen von pre.sh), der einmal pro Woche generiert wird, aber wir möchten sie täglich / stündlich generieren,
wenn möglich siehe), und auf Cloudflare R2 gehostet (Umgebungsvariable: CF_BLOCKLIST_URL).
serverless-dns lädt 3 Blocklistendateien
herunter, die zum Einrichten des Radix-Trie während des Laufzeit-Starts erforderlich sind, oder lädt sie lazy
herunter, wenn eine DNS-Anfrage bedient wird.
serverless-dns kompiliert rund ~13M Einträge (Stand Januar 2023) aus rund 190+ Blocklisten. Diese sind im Repository serverless-dns/blocklists definiert.