CVE-2025-23221
Fedify has an Infinite loop and Blind SSRF found inside the Webfinger mechanism
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
5.4
Fedify has an Infinite loop and Blind SSRF found inside the Webfinger mechanism
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
- Record
CVE-2025-23221- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Weakness
- CWE-835, CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2025-0153
5.4
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
- Record
EUVD-2025-0153- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Updated
- 2026-11-03
GitHub AdvisoriesGHSA-c59p-wq67-24wx
5.4
Infinite loop and Blind SSRF found inside the Webfinger mechanism in @fedify/fedify
Summary
This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack.
Details
The Webfinger endpoint takes a remote domain for checking accounts as a feature, however, as per the ActivityPub spec (https://www.w3.org/TR/activitypub/#security-considerations), on the security considerations section at B.3, access to Localhost services should be prevented while running in production.
The lookupWebFinger function, responsible for returning an actor handler for received actor objects from a remote server, can be abused to perform a Denial of Service (DoS) and Blind SSRF attacks while attempting to resolve a malicious actor’s object. On Fedify, two client-facing functions implement the lookupWebFinger function- getActorHandle, and lookupObject, which are both used as a wrapper for the vulnerable lookup function. As the lookupObject function is implemented only for CLI usage, we won’t focus our PoC and explanation on it, but it is still vulnerable in the same way getActorHandle is.
The getActorHandle function is a wrapper function for the getActorHandleInternal function (both present at /src/vocab/actor.ts):
async function getActorHandleInternal(
actor: Actor | URL,
options: GetActorHandleOptions = {},
): Promise<`@${string}@${string}` | `${string}@${string}`> {
const actorId = actor instanceof URL ? actor : actor.id;
if (actorId != null) {
const result = await lookupWebFinger(actorId, {
userAgent: options.userAgent,
tracerProvider: options.tracerProvider,
});
if (result != null) {
const aliases = [...(result.aliases ?? [])];
if (result.subject != null) aliases.unshift(result.subject);
for (const alias of aliases) {
const match = alias.match(/^acct:([^@]+)@([^@]+)$/);
if (match != null) {
const hostname = new URL(`https://${match[2]}/`).hostname;
if (
hostname !== actorId.hostname &&
!await verifyCrossOriginActorHandle(
actorId.href,
alias,
options.userAgent,
options.tracerProvider,
)
) {
continue;
}
return normalizeActorHandle(`@${match[1]}@${match[2]}`, options);
}
}
}
}
if (
!(actor instanceof URL) && actor.preferredUsername != null &&
actor.id != null
) {
return normalizeActorHandle(
`@${actor.preferredUsername}@${actor.id.host}`,
options,
);
}
throw new TypeError(
"Actor does not have enough information to get the handle.",
);
}
The actorId parameter containing a URL of the actor ID sinks into the lookupWebFinger function which is a wrapper for the lookupWebFingerInternal:
async function lookupWebFingerInternal(
resource: URL | string,
options: LookupWebFingerOptions = {},
): Promise<ResourceDescriptor | null> {
if (typeof resource === "string") resource = new URL(resource);
let protocol = "https:";
let server: string;
if (resource.protocol === "acct:") {
const atPos = resource.pathname.lastIndexOf("@");
if (atPos < 0) return null;
server = resource.pathname.substring(atPos + 1);
if (server === "") return null;
} else {
protocol = resource.protocol;
server = resource.host;
}
let url = new URL(`${protocol}//${server}/.well-known/webfinger`);
url.searchParams.set("resource", resource.href);
while (true) {
logger.debug(
"Fetching WebFinger resource descriptor from {url}...",
{ url: url.href },
);
let response: Response;
try {
response = await fetch(url, {
headers: {
Accept:…
- Record
GHSA-c59p-wq67-24wx- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Weakness
- CWE-835, CWE-918
- Updated
- 2026-11-02
NVD
5.4
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
- Record
CVE-2025-23221- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Weakness
- CWE-835, CWE-918
- Updated
- 2026-11-02
OSV
5.4
Fedify has an Infinite loop and Blind SSRF found inside the Webfinger mechanism
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
- Record
CVE-2025-23221- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Weakness
- CWE-835, CWE-918
- Updated
- 2026-11-02
OSVGHSA-c59p-wq67-24wx
5.4
Infinite loop and Blind SSRF found inside the Webfinger mechanism in @fedify/fedify
Summary
This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack.
Details
The Webfinger endpoint takes a remote domain for checking accounts as a feature, however, as per the ActivityPub spec (https://www.w3.org/TR/activitypub/#security-considerations), on the security considerations section at B.3, access to Localhost services should be prevented while running in production.
The lookupWebFinger function, responsible for returning an actor handler for received actor objects from a remote server, can be abused to perform a Denial of Service (DoS) and Blind SSRF attacks while attempting to resolve a malicious actor’s object. On Fedify, two client-facing functions implement the lookupWebFinger function- getActorHandle, and lookupObject, which are both used as a wrapper for the vulnerable lookup function. As the lookupObject function is implemented only for CLI usage, we won’t focus our PoC and explanation on it, but it is still vulnerable in the same way getActorHandle is.
The getActorHandle function is a wrapper function for the getActorHandleInternal function (both present at /src/vocab/actor.ts):
async function getActorHandleInternal(
actor: Actor | URL,
options: GetActorHandleOptions = {},
): Promise<`@${string}@${string}` | `${string}@${string}`> {
const actorId = actor instanceof URL ? actor : actor.id;
if (actorId != null) {
const result = await lookupWebFinger(actorId, {
userAgent: options.userAgent,
tracerProvider: options.tracerProvider,
});
if (result != null) {
const aliases = [...(result.aliases ?? [])];
if (result.subject != null) aliases.unshift(result.subject);
for (const alias of aliases) {
const match = alias.match(/^acct:([^@]+)@([^@]+)$/);
if (match != null) {
const hostname = new URL(`https://${match[2]}/`).hostname;
if (
hostname !== actorId.hostname &&
!await verifyCrossOriginActorHandle(
actorId.href,
alias,
options.userAgent,
options.tracerProvider,
)
) {
continue;
}
return normalizeActorHandle(`@${match[1]}@${match[2]}`, options);
}
}
}
}
if (
!(actor instanceof URL) && actor.preferredUsername != null &&
actor.id != null
) {
return normalizeActorHandle(
`@${actor.preferredUsername}@${actor.id.host}`,
options,
);
}
throw new TypeError(
"Actor does not have enough information to get the handle.",
);
}
The actorId parameter containing a URL of the actor ID sinks into the lookupWebFinger function which is a wrapper for the lookupWebFingerInternal:
async function lookupWebFingerInternal(
resource: URL | string,
options: LookupWebFingerOptions = {},
): Promise<ResourceDescriptor | null> {
if (typeof resource === "string") resource = new URL(resource);
let protocol = "https:";
let server: string;
if (resource.protocol === "acct:") {
const atPos = resource.pathname.lastIndexOf("@");
if (atPos < 0) return null;
server = resource.pathname.substring(atPos + 1);
if (server === "") return null;
} else {
protocol = resource.protocol;
server = resource.host;
}
let url = new URL(`${protocol}//${server}/.well-known/webfinger`);
url.searchParams.set("resource", resource.href);
while (true) {
logger.debug(
"Fetching WebFinger resource descriptor from {url}...",
{ url: url.href },
);
let response: Response;
try {
response = await fetch(url, {
headers: {
Accept:…
- Record
GHSA-c59p-wq67-24wx- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L - Weakness
- CWE-835, CWE-918
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
@fedify/fedifynpm |
1.0.13 to 1.0.13 | 1.0.14 |
@fedify/fedifynpm |
1.1.10 to 1.1.10 | 1.1.11 |
@fedify/fedifynpm |
1.2.10 to 1.2.10 | 1.2.11 |
@fedify/fedifynpm |
1.3.3 to 1.3.3 | 1.3.4 |
@fedify/fedifynpm |
1.0.13 up to 1.0.14 | 1.0.14 |
@fedify/fedifynpm |
1.1.10 up to 1.1.11 | 1.1.11 |
@fedify/fedifynpm |
1.2.10 up to 1.2.11 | 1.2.11 |
@fedify/fedifynpm |
1.3.3 up to 1.3.4 | 1.3.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| aliases, products, published | EUVD | |
| first indexed | CVE List, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2025-23221 is a medium vulnerability (CVSS 5.4) in @fedify Fedify, published January 20, 2025. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.62%, the 49th percentile. Fixed in @fedify/fedify 1.0.14.
References
github.com/dahlia/fedify/security/advisories/GHSA-c59p-wq67-24wx
github.com/dahlia/fedify/commit/8be3c2038eebf4ae12481683a1e809b314be3151
github.com/dahlia/fedify/commit/c505eb82fcd6b5b17174c6659c29721bc801ab9a
github.com/dahlia/fedify/commit/e921134dd5097586e4563ea80b9e8d1b5460a645
nvd.nist.gov/vuln/detail/CVE-2025-23221
github.com/advisories/GHSA-c59p-wq67-24wx
github.com/CVEProject/cvelistV5/tree/main/cves/2025/23xxx/CVE-2025-23221.json
github.com/dahlia/fedify
Left out here: the rest of 2 source descriptions. The JSON record has them.