| Reporter | Title | Published | Views | Family All 6 |
|---|---|---|---|---|
| CVE-2014-1860 | 8 Jan 202015:37 | – | cve | |
| CVE-2014-1860 | 8 Jan 202015:37 | – | cvelist | |
| EUVD-2014-1923 | 7 Oct 202500:30 | – | euvd | |
| CVE-2014-1860 | 8 Jan 202016:15 | – | nvd | |
| Design/Logic Flaw | 8 Jan 202016:15 | – | prion | |
| CVE-2014-1860 | 22 May 202505:41 | – | redhatcve |
Hi,
I have discovered a vulnerability that might lead to code execution in
Contao CMS <= 3.2.4
Contao CMS <= 3.2.4 does not properly validate user input in several
locations which is then passed directly into PHP's unserialize.
This has been fixed in Contao 3.2.5 as per commit:
https://github.com/contao/core/commit/8c9cb044bdc887a8202bb65a64545c025664f957
and
https://github.com/contao/core/commit/1717336598fdcf1ed3f4ad488e140147cb31516d
Announcements can be found at
https://contao.org/en/news/contao-3_2_5.html
https://contao.org/en/news/contao-2_11_14.html
Thanks to the Contao developers for being so responsive.
The full report can be found at my repo in
https://github.com/pedrib/PoC/blob/master/contao-3.2.4.txt
Regards,
Pedro Ribeiro
Agile Information Security
------
Proof of concept:
> Vulnerabilities in Contao 3.2.4
> Discovered by Pedro Ribeiro ([email protected]) of Agile Information Security
====================================================================
Vulnerability: PHP object injection leading to all kinds of badness
File(line): contao-core-3.2.4/system/modules/core/drivers/DC_Table.php(149)
File(line): contao-core-3.2.4/system/modules/core/drivers/DC_Folder.php(124)
File(line): contao-core-3.2.4/system/modules/core/widgets/KeyValueWizard.php(73)
File(line): Potentially many others that call deserialize with user input
Code snippet:
contao-core-3.2.4/system/modules/core/drivers/DC_Table.php(149):
if (\Input::post('FORM_SUBMIT') == 'tl_select')
{
$ids = deserialize(\Input::post('IDS'));
if (!is_array($ids) || empty($ids))
{
$this->reload();
}
$session = $this->Session->getData();
$session['CURRENT']['IDS'] = deserialize(\Input::post('IDS'));
contao-core-3.2.4/system/modules/core/drivers/DC_Folder.php(124):
if (\Input::post('FORM_SUBMIT') == 'tl_select')
{
$ids = deserialize(\Input::post('IDS'));
contao-core-3.2.4/system/modules/core/widgets/KeyValueWizard.php(73)
public function validate()
{
$mandatory = $this->mandatory;
$options = deserialize($this->getPost($this->strName));
deserialize from functions.php (starting at line 280):
/**
* Return an unserialized array or the argument
* @param mixed
* @param boolean
* @return mixed
*/
function deserialize($varValue, $blnForceArray=false)
{
if (is_array($varValue))
{
return $varValue;
}
if (!is_string($varValue))
{
return $blnForceArray ? (($varValue === null) ? array() : array($varValue)) : $varValue;
}
elseif (trim($varValue) == '')
{
return $blnForceArray ? array() : '';
}
$varUnserialized = @unserialize($varValue);
if (is_array($varUnserialized))
{
$varValue = $varUnserialized;
}
elseif ($blnForceArray)
{
$varValue = array($varValue);
}
return $varValue;
}
The Input class does good validation on POST and GET variables for XSS, but in this case it provides no protection because the malicious data is a well formed PHP object.
post from Input class:
public static function post($strKey, $blnDecodeEntities=false)
{
$strCacheKey = $blnDecodeEntities ? 'postDecoded' : 'postEncoded';
if (!isset(static::$arrCache[$strCacheKey][$strKey]))
{
$varValue = static::findPost($strKey);
if ($varValue === null)
{
return $varValue;
}
$varValue = static::stripSlashes($varValue);
$varValue = static::decodeEntities($varValue);
$varValue = static::xssClean($varValue, true);
$varValue = static::stripTags($varValue);
if (!$blnDecodeEntities)
{
$varValue = static::encodeSpecialChars($varValue);
}
static::$arrCache[$strCacheKey][$strKey] = $varValue;
}
return static::$arrCache[$strCacheKey][$strKey];
}
getPost from Widget class (starting at line 722):
/**
* Find and return a $_POST variable
*
* @param string $strKey The variable name
*
* @return mixed The variable value
*/
protected function getPost($strKey)
{
$strMethod = $this->allowHtml ? 'postHtml' : 'post';
if ($this->preserveTags)
{
$strMethod = 'postRaw';
}
// Support arrays (thanks to Andreas Schempp)
$arrParts = explode('[', str_replace(']', '', $strKey));
if (!empty($arrParts))
{
$varValue = \Input::$strMethod(array_shift($arrParts), $this->decodeEntities);
foreach($arrParts as $part)
{
if (!is_array($varValue))
{
break;
}
$varValue = $varValue[$part];
}
return $varValue;
}
return \Input::$strMethod($strKey, $this->decodeEntities);
}
There are a few magic methods that seem exploitable.
For example on contao-core-3.2.4/system/modules/core/classes/FrontendUser.php, it might be possible to overwrite the admin session with another session by manipulating the intId parameter (which is the userID):
public function __destruct()
{
$session = $this->Session->getData();
if (!isset($_GET['pdf']) && !isset($_GET['file']) && !isset($_GET['id']) && $session['referer']['current'] != \Environment::get('requestUri'))
{
$session['referer']['last'] = $session['referer']['current'];
$session['referer']['current'] = substr(\Environment::get('requestUri'), strlen(TL_PATH) + 1);
}
$this->Session->setData($session);
if ($this->intId)
{
$this->Database->prepare("UPDATE " . $this->strTable . " SET session=? WHERE id=?")
->execute(serialize($session), $this->intId);
}
}
Overwriting configuration files in contao-core-3.2.4/system/modules/core/library/Contao/Config.php
/**
* Automatically save the local configuration
*/
public function __destruct()
{
if ($this->blnIsModified)
{
$this->save();
}
}
public function save()
{
if ($this->strTop == '')
{
$this->strTop = '<?php';
}
$strFile = trim($this->strTop) . "\n\n";
$strFile .= "### INSTALL SCRIPT START ###\n";
foreach ($this->arrData as $k=>$v)
{
$strFile .= "$k = $v\n";
}
$strFile .= "### INSTALL SCRIPT STOP ###\n";
$this->strBottom = trim($this->strBottom);
if ($this->strBottom != '')
{
$strFile .= "\n" . $this->strBottom . "\n";
}
$strTemp = md5(uniqid(mt_rand(), true));
// Write to a temp file first
$objFile = fopen(TL_ROOT . '/system/tmp/' . $strTemp, 'wb');
fputs($objFile, $strFile);
fclose($objFile);
// Make sure the file has been written (see #4483)
if (!filesize(TL_ROOT . '/system/tmp/' . $strTemp))
{
\System::log('The local configuration file could not be written. Have your reached your quota limit?', __METHOD__, TL_ERROR);
return;
}
// Then move the file to its final destination
$this->Files->rename('system/tmp/' . $strTemp, 'system/config/localconfig.php');
Arbitrary file deletion in contao-core-3.2.4/system/modules/core/library/Contao/ZipWriter.php:
public function __destruct()
{
if (is_resource($this->resFile))
{
@fclose($this->resFile);
}
if (file_exists($this->strTemp))
{
@unlink($this->strTemp);
}
Again arbitrary file deletion in contao-core-3.2.4/system/modules/core/vendor/swiftmailer/classes/Swift/ByteStream/TemporaryFileByteStream.php:
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
Comment:
User input is passed directly into unserialize(), possibly leading to code execution.
References:
https://www.owasp.org/index.php/PHP_Object_Injection
http://www.alertlogic.com/writing-exploits-for-exotic-bug-classes/
http://www.suspekt.org/downloads/POC2009-ShockingNewsInPHPExploitation.pdf
http://vagosec.org/2013/12/wordpress-rce-exploit/
# 0day.today [2018-02-06] #Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation