CVE-2025-25194
Server-Side Request Forgery (SSRF) in activitypub_federation
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. This vulnerability, which is present in versions 0.6.2 and prior of activitypub_federation and versions 0.19.8 and prior of Lemmy, allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request. As of time of publication, a fix has not been made available.
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
- None
Exploit likelihood
What each source reports
CVE List Score used
4.0
Server-Side Request Forgery (SSRF) in activitypub_federation
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. This vulnerability, which is present in versions 0.6.2 and prior of activitypub_federation and versions 0.19.8 and prior of Lemmy, allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request. As of time of publication, a fix has not been made available.
- Record
CVE-2025-25194- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2025-4082
4.0
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. This vulnerability, which is present in versions 0.6.2 and prior of activitypub_federation and versions 0.19.8 and prior of Lemmy, allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request. As of time of publication, a fix has not been made available.
- Record
EUVD-2025-4082- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Updated
- 2026-11-03
GitHub AdvisoriesGHSA-7723-35v7-qcxw
4.0
Server-Side Request Forgery (SSRF) in activitypub_federation
Summary
This vulnerability allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request.
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 library attempts to prevent Localhost access using the following mechanism (/src/config.rs):
pub(crate) async fn verify_url_valid(&self, url: &Url) -> Result<(), Error> {
match url.scheme() {
"https" => {}
"http" => {
if !self.allow_http_urls {
return Err(Error::UrlVerificationError(
"Http urls are only allowed in debug mode",
));
}
}
_ => return Err(Error::UrlVerificationError("Invalid url scheme")),
};
// Urls which use our local domain are not a security risk, no further verification needed
if self.is_local_url(url) {
return Ok(());
}
if url.domain().is_none() {
return Err(Error::UrlVerificationError("Url must have a domain"));
}
if url.domain() == Some("localhost") && !self.debug {
return Err(Error::UrlVerificationError(
"Localhost is only allowed in debug mode",
));
}
self.url_verifier.verify(url).await?;
Ok(())
}
There are multiple issues with the current anti-Localhost implementation:
- It does not resolve the domain address supplied by the user.
- The Localhost check is using only a simple comparison method while ignoring more complex malicious tampering attempts.
- It filters only localhost domains, without any regard for alternative local IP domains or other sensitive domains, such internal network or cloud metadata domains.
We can reach the verify_url_valid function while sending a Webfinger request to lookup a user’s account (/src/fetch/webfinger.rs):
pub async fn webfinger_resolve_actor<T: Clone, Kind>(
identifier: &str,
data: &Data<T>,
) -> Result<Kind, <Kind as Object>::Error>
where
Kind: Object + Actor + Send + 'static + Object<DataType = T>,
for<'de2> <Kind as Object>::Kind: serde::Deserialize<'de2>,
<Kind as Object>::Error: From<crate::error::Error> + Send + Sync + Display,
{
let (_, domain) = identifier
.splitn(2, '@')
.collect_tuple()
.ok_or(WebFingerError::WrongFormat.into_crate_error())?;
let protocol = if data.config.debug { "http" } else { "https" };
let fetch_url =
format!("{protocol}://{domain}/.well-known/webfinger?resource=acct:{identifier}");
debug!("Fetching webfinger url: {}", &fetch_url);
let res: Webfinger = fetch_object_http_with_accept(
&Url::parse(&fetch_url).map_err(Error::UrlParse)?,
data,
&WEBFINGER_CONTENT_TYPE,
)
.await?
.object;
debug_assert_eq!(res.subject, format!("acct:{identifier}"));
let links: Vec<Url> = res
.links
.iter()
.filter(|link| {
if let Some(type_) = &link.kind {
type_.starts_with("application/")
} else {
false
}
})
.filter_map(|l| l.href.clone())
.collect();
for l in links {
let object = ObjectId::<Kind>::from(l).dereference(data).await;
match object {
Ok(obj) => return Ok(obj),
Err(error) => debug!(%error, "Failed to dereference link"),
}
}
Err(WebFingerError::NoValidLink.into_crate_error().into())
}
The Webfinger logic takes the user account from the GET parameter “resource” and sinks the domain directly into the…
- Record
GHSA-7723-35v7-qcxw- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
NVD
4.0
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. This vulnerability, which is present in versions 0.6.2 and prior of activitypub_federation and versions 0.19.8 and prior of Lemmy, allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request. As of time of publication, a fix has not been made available.
- Record
CVE-2025-25194- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSV
4.0
Server-Side Request Forgery (SSRF) in activitypub_federation
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. This vulnerability, which is present in versions 0.6.2 and prior of activitypub_federation and versions 0.19.8 and prior of Lemmy, allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request. As of time of publication, a fix has not been made available.
- Record
CVE-2025-25194- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSVGHSA-7723-35v7-qcxw
4.0
Server-Side Request Forgery (SSRF) in activitypub_federation
Summary
This vulnerability allows a user to bypass any predefined hardcoded URL path or security anti-Localhost mechanism and perform an arbitrary GET request to any Host, Port and URL using a Webfinger Request.
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 library attempts to prevent Localhost access using the following mechanism (/src/config.rs):
pub(crate) async fn verify_url_valid(&self, url: &Url) -> Result<(), Error> {
match url.scheme() {
"https" => {}
"http" => {
if !self.allow_http_urls {
return Err(Error::UrlVerificationError(
"Http urls are only allowed in debug mode",
));
}
}
_ => return Err(Error::UrlVerificationError("Invalid url scheme")),
};
// Urls which use our local domain are not a security risk, no further verification needed
if self.is_local_url(url) {
return Ok(());
}
if url.domain().is_none() {
return Err(Error::UrlVerificationError("Url must have a domain"));
}
if url.domain() == Some("localhost") && !self.debug {
return Err(Error::UrlVerificationError(
"Localhost is only allowed in debug mode",
));
}
self.url_verifier.verify(url).await?;
Ok(())
}
There are multiple issues with the current anti-Localhost implementation:
- It does not resolve the domain address supplied by the user.
- The Localhost check is using only a simple comparison method while ignoring more complex malicious tampering attempts.
- It filters only localhost domains, without any regard for alternative local IP domains or other sensitive domains, such internal network or cloud metadata domains.
We can reach the verify_url_valid function while sending a Webfinger request to lookup a user’s account (/src/fetch/webfinger.rs):
pub async fn webfinger_resolve_actor<T: Clone, Kind>(
identifier: &str,
data: &Data<T>,
) -> Result<Kind, <Kind as Object>::Error>
where
Kind: Object + Actor + Send + 'static + Object<DataType = T>,
for<'de2> <Kind as Object>::Kind: serde::Deserialize<'de2>,
<Kind as Object>::Error: From<crate::error::Error> + Send + Sync + Display,
{
let (_, domain) = identifier
.splitn(2, '@')
.collect_tuple()
.ok_or(WebFingerError::WrongFormat.into_crate_error())?;
let protocol = if data.config.debug { "http" } else { "https" };
let fetch_url =
format!("{protocol}://{domain}/.well-known/webfinger?resource=acct:{identifier}");
debug!("Fetching webfinger url: {}", &fetch_url);
let res: Webfinger = fetch_object_http_with_accept(
&Url::parse(&fetch_url).map_err(Error::UrlParse)?,
data,
&WEBFINGER_CONTENT_TYPE,
)
.await?
.object;
debug_assert_eq!(res.subject, format!("acct:{identifier}"));
let links: Vec<Url> = res
.links
.iter()
.filter(|link| {
if let Some(type_) = &link.kind {
type_.starts_with("application/")
} else {
false
}
})
.filter_map(|l| l.href.clone())
.collect();
for l in links {
let object = ObjectId::<Kind>::from(l).dereference(data).await;
match object {
Ok(obj) => return Ok(obj),
Err(error) => debug!(%error, "Failed to dereference link"),
}
}
Err(WebFingerError::NoValidLink.into_crate_error().into())
}
The Webfinger logic takes the user account from the GET parameter “resource” and sinks the domain directly into the…
- Record
GHSA-7723-35v7-qcxw- CVSS 3.1
- 4.0 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
activitypub_federationcrates.io |
through 0.6.2 | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| aliases | 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-25194 is a medium vulnerability (CVSS 4.0) in Lemmynet Lemmy, published February 10, 2025. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.42%, the 36th percentile. No source names a fixed version.
References
Left out here: the rest of 2 source descriptions. The JSON record has them.