Vulnerability: A Remote Code Execution vulnerability has been found in the Horde_Image library when using the "Im" backend that utilizes ImageMagick's "convert" utility. It's not exploitable through any Horde application, because the code path to the vulnerability is not used by any Horde code. Custom applications using the Horde_Image library might be affected. This vulnerability affects all versions of Horde_Image from 2.0.0 to 2.5.1, and is fixed in 2.5.2. The problem is missing input validation of the index field in _raw() during construction of an ImageMagick command line.
Vulnerability ID: CVE-2017-14650
CWE: CWE-20
Severity score: 6.4
Project: https://github.com/horde/horde
Published: 2017-09-21
File: framework/Image/lib/Horde/Image/Im.php
Commit message: Fix RCE in _raw() via $index parameter.

$index is passed down from getImageAtIndex($index).
Luckily the only official horde code calling it
is the PDF preview code with a fixed index of 0.

Still code from third party vendors using the Horde framework libs
might expose getImageAtIndex() to the web.

PoC:

----------------
<?php
$options = array('index' => "5'$(ls -al )'");
$max_pages = 10;

// php fun: implicit conversion to integer
if ($options['index'] < $max_pages)
{
    // _raw() call in Im.php might look like
    //        /usr/bin/convert  "/tmp/img1URPsC"'[0]'  -strip png:"/tmp/img0NgHfI"
    echo exec('echo \'[' . $options['index'] . ']\' ');
}
?>
----------------
Signed-off-by: Thomas Jarosch <thomas.jarosch@intra2net.com>
Signed-off-by: Jan Schneider <jan@horde.org>
Commit author: Thomas Jarosch

Vulnerable version (before fix):
 private function _raw($convert = false, $options = array())         $tmpin = $this->toFile($this->_data);         $tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);         $command = $this->_convert . ' ' . implode(' ', $this->_operations)             . ' "' . $tmpin . '"\'[' . $options['index'] . ']\' '             . implode(' ', $this->_postSrcOperations)             . ' -strip ' . $this->_type . ':"' . $tmpout . '" 2>&1';         $this->_logDebug(sprintf("convert command executed by Horde_Image_im::raw(): %s", $command));

Fixed version (after fix):
 private function _raw($convert = false, $options = array())         $tmpin = $this->toFile($this->_data);         $tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);         $command = $this->_convert . ' ' . implode(' ', $this->_operations)             . ' "' . $tmpin . '"\'[' . (integer)$options['index'] . ']\' '             . implode(' ', $this->_postSrcOperations)             . ' -strip ' . $this->_type . ':"' . $tmpout . '" 2>&1';         $this->_logDebug(sprintf("convert command executed by Horde_Image_im::raw(): %s", $command));

Patch:
patch: @@ -198,7 +198,7 @@ private function _raw($convert = false, $options = array())
         $tmpin = $this->toFile($this->_data);
         $tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
         $command = $this->_convert . ' ' . implode(' ', $this->_operations)
-            . ' "' . $tmpin . '"\'[' . $options['index'] . ']\' '
+            . ' "' . $tmpin . '"\'[' . (integer)$options['index'] . ']\' '
             . implode(' ', $this->_postSrcOperations)
             . ' -strip ' . $this->_type . ':"' . $tmpout . '" 2>&1';
         $this->_logDebug(sprintf("convert command executed by Horde_Image_im::raw(): %s", $command));
path: framework/Image/lib/Horde/Image/Im.php