CVE-2026-11526
Description
A 2-arg open() in GD::Image::_make_filehandle lets attackers inject commands or overwrite files by passing a crafted filename to any GD image constructor.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A 2-arg open() in GD::Image::_make_filehandle lets attackers inject commands or overwrite files by passing a crafted filename to any GD image constructor.
Vulnerability
GD versions before 2.86 for Perl contain an OS command injection and file overwrite vulnerability in the _make_filehandle method of GD::Image. This method uses Perl's two-argument open() (open($fh, $thing)) to open a filename argument. Because two-argument open() interprets shell metacharacters, a filename that begins or ends with a pipe (| cmd or cmd |) or begins with a redirect (> path or >> path) is executed as a command or redirect instead of being opened as a file [1][2]. The vulnerable code path is reached by every filename-accepting constructor (new, newFromPng, newFromJpeg, etc.); the in-memory *Data variants are not affected [2].
Exploitation
An attacker needs the ability to supply a crafted filename string to any GD::Image constructor that accepts a file path. No authentication or special privileges are required beyond the application's normal input surface (e.g., HTTP file-upload handlers, configuration loaders). The attacker provides a filename such as "| malicious_command" so that the two-argument open() interprets the pipe as a command to execute. Similarly, a filename like "> /tmp/overwritten" causes the file to be opened for writing, truncating or overwriting it [1][2].
Impact
Successful exploitation allows an attacker to execute arbitrary operating system commands under the process UID or to overwrite arbitrary files writable by the process. This can lead to complete compromise of the application's host (via command execution) or destruction of critical data (via file overwrite). Both CWE-78 (OS Command Injection) and CWE-73 (External Control of File Name or Path) are addressed by the fix [1][2].
Mitigation
Upgrade to GD version 2.86 or later, which was released on 2026-06-14 [1]. The fix replaces the two-argument open() with three-argument open() (open($fh, '<', $thing)), ensuring that the filename is always treated as a literal pathname [2]. No workaround is available for earlier versions other than filtering or sanitizing all filename inputs to prevent shell metacharacters. This CVE is not currently listed on the CISA Known Exploited Vulnerabilities (KEV) catalog.
AI Insight generated on Jun 14, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
167b163713c6cFix CVE-2026-11526: command injection via 2-arg open() in _make_filehandle
4 files changed · +68 −4
lib/GD/Image.pm+3 −2 modified@@ -101,9 +101,10 @@ sub _make_filehandle { } return $fh if defined(fileno $fh); - # otherwise treat it as a file to open + # otherwise treat it as a file to open; 3-arg open so the filename is + # not interpreted as a command or redirect $fh = gensym; - if (!open($fh,$thing)) { + if (!open($fh,'<',$thing)) { die "$thing not found: $!"; return undef; }
lib/GD/Image_pm.PL+3 −2 modified@@ -129,9 +129,10 @@ sub _make_filehandle { } return $fh if defined(fileno $fh); - # otherwise treat it as a file to open + # otherwise treat it as a file to open; 3-arg open so the filename is + # not interpreted as a command or redirect $fh = gensym; - if (!open($fh,$thing)) { + if (!open($fh,'<',$thing)) { die "$thing not found: $!"; return undef; }
MANIFEST+1 −0 modified@@ -40,6 +40,7 @@ t/Polyline.t t/autodetect.t t/caller.t t/fork.t +t/security_open.t t/test_data/Generic.ttf t/test_data/frog.jpg t/test_data/frog.xbm
t/security_open.t+61 −0 added@@ -0,0 +1,61 @@ +# _make_filehandle is the single open path for every filename constructor +# (new, newFrom*). A 2-arg open() there interprets shell-magic prefixes, so a +# "cmd |" filename runs a command and a "> file" filename truncates a file. +# These must be treated as plain pathnames. +use strict; +use warnings; +use GD; +use File::Temp qw( tempdir ); +use File::Spec; +use Test::More tests => 5; + +my $dir = tempdir(CLEANUP => 1); + +# A trailing-pipe payload must not run a command. +{ + my $marker = File::Spec->catfile($dir, "pwned_read"); + my $fh = eval { GD::Image->_make_filehandle("touch $marker |") }; + close $fh if $fh; + ok !-e $marker, "trailing-pipe payload does not execute a command"; +} + +# A leading-pipe payload must not run a command. +{ + my $marker = File::Spec->catfile($dir, "pwned_write"); + my $fh = eval { GD::Image->_make_filehandle("| touch $marker") }; + close $fh if $fh; + ok !-e $marker, "leading-pipe payload does not execute a command"; +} + +# A redirect payload must not truncate a file. +{ + my $victim = File::Spec->catfile($dir, "victim"); + open my $fh, ">", $victim or die "$victim: $!"; + print $fh "important data\n"; + close $fh; + my $made = eval { GD::Image->_make_filehandle("> $victim") }; + close $made if $made; + is -s $victim, 15, "redirect payload does not truncate a file"; +} + +# A plain filename still opens as a file. +{ + my $real = File::Spec->catfile($dir, "real.txt"); + open my $fh, ">", $real or die "$real: $!"; + print $fh "x\n"; + close $fh; + my $opened = eval { GD::Image->_make_filehandle($real) }; + ok $opened, "plain filename still opens as a file"; +} + +# 2-arg open() silently trimmed surrounding whitespace (including a trailing +# newline); 3-arg open treats the argument literally, so an un-chomped name no +# longer opens the trimmed file. +{ + my $real = File::Spec->catfile($dir, "plain.txt"); + open my $fh, ">", $real or die "$real: $!"; + print $fh "x\n"; + close $fh; + my $padded = eval { GD::Image->_make_filehandle("$real\n") }; + ok !$padded, "trailing whitespace is significant (filename not trimmed)"; +}
Vulnerability mechanics
Root cause
"GD::Image::_make_filehandle uses Perl's 2-arg open(), which interprets shell-magic prefixes in the filename argument as commands or redirects instead of literal pathnames."
Attack vector
An attacker who can control a filename argument passed to any GD constructor that calls `_make_filehandle` can inject shell-magic prefixes. A filename beginning with `|` (e.g. `| cmd`) or ending with `|` (e.g. `cmd |`) causes Perl's 2-arg `open()` to execute the string as a command rather than open a file. A filename beginning with `>` or `>>` causes the open to truncate or append to an arbitrary file under the process UID. No authentication is required beyond the ability to supply the filename argument.
Affected code
The vulnerability resides in `GD::Image::_make_filehandle` (in `lib/GD/Image.pm` and `lib/GD/Image_pm.PL`). This subroutine is the single open path behind every filename-accepting constructor (`new`, `newFromPng`, `newFromJpeg`, etc.). The in-memory `*Data` variants do not open a path and are unaffected.
What the fix does
The patch replaces the two-argument `open($fh,$thing)` call with the three-argument form `open($fh,'<',$thing)` in both `lib/GD/Image.pm` and `lib/GD/Image_pm.PL`. In Perl, the two-argument form interprets special characters in the filename (such as leading/trailing `|` or leading `>`) as shell metacharacters, enabling command injection or file overwrite. The three-argument form treats the third argument strictly as a literal pathname, eliminating the shell interpretation. The accompanying test file `t/security_open.t` verifies that pipe and redirect payloads no longer execute commands or truncate files.
Preconditions
- inputThe attacker must be able to supply a filename argument to a GD constructor that calls _make_filehandle (e.g., new, newFromPng, newFromJpeg).
- authNo authentication is required beyond the ability to provide the filename input.
Generated on Jun 14, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.