CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2025-68950
CVSS 4.0|EPSS 0.01%

CVE-2025-68950: The Ouroboros of Graphics - ImageMagick Recursive DoS

Alon Barad
Alon Barad
Software Engineer•December 30, 2025•5 min read
PoC AvailableNot in KEV

Executive Summary (TL;DR)

ImageMagick forgot to stop MVG files from loading other MVG files. By creating two tiny text files that reference each other, an attacker can trigger infinite recursion, exhausting the stack and crashing the process. Fix: Upgrade to 7.1.2-12 or disable the MVG coder in policy.xml.

A classic recursion vulnerability in ImageMagick's MVG (Magick Vector Graphics) parser allows for infinite loops via self-referential image primitives. While officially rated as 'Local' access, this creates a trivial Denial of Service vector for any web application processing user-uploaded images.

The Hook: When Pictures Start Talking to Themselves

ImageMagick is the ubiquitous Swiss Army Knife of image processing. It runs on practically every server that handles user uploads, thumbnail generation, or format conversion. It supports over 200 formats, from the mundane (PNG, JPEG) to the esoteric. One of those esoteric formats is MVG (Magick Vector Graphics).

MVG isn't really an image format in the traditional sense; it's a script. It's a text-based vector language that tells ImageMagick how to draw lines, circles, and—crucially—how to composite other images onto the canvas. It's a powerful feature designed for complex compositions.

But here's the kicker: What happens when an MVG script tells ImageMagick to load another MVG script? And what if that second script tells it to load the first one back? You get an infinite loop, a stack overflow, and a dead worker process. It's the software equivalent of two mirrors facing each other, screaming into the void until the universe (or the kernel) decides it has had enough.

The Flaw: Unchecked Recursion in DrawPrimitive

The vulnerability lives in MagickCore/draw.c, specifically within the DrawPrimitive function. This function is responsible for parsing the drawing commands inside an MVG file. One of these commands is image, which allows the script to overlay an external image file.

When DrawPrimitive encounters an image command, it calls ReadImage to load the referenced file. ReadImage looks at the file header (or extension), figures out it's an MVG, and passes it right back to the MVG parser... which calls DrawPrimitive again.

Prior to version 7.1.2-12, there was no guard rail here. The developers had blocked protocols like ftp and http to prevent Server-Side Request Forgery (SSRF) in this context, but they forgot to check if the file being loaded was another MVG. This omission allowed for uncontrolled recursion (CWE-674). The stack grows with every call until it hits the operating system's limit (usually 8MB on Linux), resulting in an immediate Segmentation fault.

The Code: The Smoking Gun

Let's look at the fix in commit 204718c. It’s embarrassingly simple, which usually implies the bug was embarrassingly obvious in hindsight. The patch adds a single check to the exclusion list inside MagickCore/draw.c.

Before the fix, the code prevented ftp, http, and https (and vid) to avoid network-based shenanigans, but left the door wide open for the file format itself:

/* Vulnerable Logic */
if ((LocaleCompare(clone_info->magick,"ftp") != 0) &&
    (LocaleCompare(clone_info->magick,"http") != 0) &&
    (LocaleCompare(clone_info->magick,"https") != 0) &&
    (LocaleCompare(clone_info->magick,"vid") != 0))
  composite_images=ReadImage(clone_info,exception);

The patch simply adds mvg to the "Do Not Fly" list:

              if ((LocaleCompare(clone_info->magick,"ftp") != 0) &&
                  (LocaleCompare(clone_info->magick,"http") != 0) &&
                  (LocaleCompare(clone_info->magick,"https") != 0) &&
+                 (LocaleCompare(clone_info->magick,"mvg") != 0) &&
                  (LocaleCompare(clone_info->magick,"vid") != 0))
                composite_images=ReadImage(clone_info,exception);

This LocaleCompare check ensures that if the format detected (clone_info->magick) is MVG, the ReadImage call is skipped entirely. The recursion chain is broken before it can even start the second iteration.

The Exploit: Building the Infinite Loop

Exploiting this doesn't require complex memory corruption techniques or ROP chains. It just requires two text files. We rely on the image primitive in MVG. The syntax is generally image <composite_op> <x>,<y> <width>,<height> 'filename'.

First, we create trigger1.mvg:

push graphic-context
  image over 0,0 0,0 'trigger2.mvg'
pop graphic-context

Next, we create its evil twin, trigger2.mvg:

push graphic-context
  image over 0,0 0,0 'trigger1.mvg'
pop graphic-context

[!NOTE] You can actually do this with a single file referencing itself, but the dual-file method is often more reliable against simple deduplication logic.

Now, run it against a vulnerable version:

$ magick identify trigger1.mvg
Segmentation fault (core dumped)

While the CVSS score marks this as "Local", consider a web application that allows profile picture uploads. If you upload trigger1.mvg (renaming it to trigger1.png might bypass basic extension filters, but ImageMagick's magic-byte detection will likely still sniff it out as MVG or text), the server tries to process it. It follows the link to trigger2.mvg (if you can upload that too), and pop goes the worker process.

If the server isn't using strict process isolation or resource limits, you can effectively TKO the image processing microservice.

The Mitigation: Policy.xml is Your Best Friend

The official fix is to upgrade to ImageMagick 7.1.2-12. However, history has taught us that ImageMagick has a vast attack surface. Today it's MVG recursion; tomorrow it's a Ghostscript delegate issue.

The most robust defense for any infrastructure using ImageMagick is to aggressively restrict the allowed "coders" (formats) via policy.xml. Do you really need your web server to process MVG, MSL, or ephemeral vector formats? Probably not.

Add this to your /etc/ImageMagick-7/policy.xml inside the <policymap> section:

<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="HTTPS" />

This disables the MVG coder entirely. Even if the vulnerability exists in the binary, the policy enforcement layer stops the parser from even attempting to read the file. It's the difference between wearing a bulletproof vest and just not standing in front of the gun.

Official Patches

ImageMagickOfficial patch commit on GitHub

Fix Analysis (1)

Technical Appendix

CVSS Score
4.0/ 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Probability
0.01%
Top 100% most exploited

Affected Systems

ImageMagick < 7.1.2-12Web applications utilizing ImageMagick for image processingContent Management Systems (CMS) with image upload features

Affected Versions Detail

ProductAffected VersionsFixed Version
ImageMagick
ImageMagick Studio LLC
< 7.1.2-127.1.2-12
AttributeDetail
CWE IDCWE-674 (Uncontrolled Recursion)
CVSS v3.14.0 (Medium)
Attack VectorLocal (Remote via Upload)
ImpactDenial of Service (Stack Exhaustion)
EPSS Score0.00013
Patch Commit204718c

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1203Exploitation for Client Execution
Execution
CWE-674
Uncontrolled Recursion

Exploit Resources

Known Exploits & Detection

Manual AnalysisTwo-file circular dependency MVG PoC demonstrated in report.

Vulnerability Timeline

Vulnerability Timeline

Patch Committed to GitHub
2024-02-12
CVE Published
2025-02-18

References & Sources

  • [1]ImageMagick GitHub Repository
  • [2]NVD Entry for CVE-2025-68950

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.