Joomla! 官方12月21号发布了3.4.7 新版程序,其中修复了Session序列化和一处SQL注入。
前一阵子 Joomla 的对象注入很火,而官方3.4.6的修复仅仅是严格过滤了X_FORWARDED_FOR、注释了USER_AGENT存入SESSION那一句,见:https://github.com/joomla/joomla-cms/commit/995db72ff4eaa544e38b4da3630b7a1ac0146264#diff-aba80b5850bf0435954b29dece250cbfL1021这样只是指哪补哪,治标不治本。看来官方上次的修复只是临时解决方案,这次的更新(3.4.7)算是彻底解决了此问题。
上次的对象注入,需要满足三个条件:
\\xF0\\x9D\\x8C\\x86
来截断。Joomla 官方也只能解决第一个,也就是改进session的处理方式。这次更新,在 libraries/cms/version/version.php 中,将SESSION存储在内部的Registry类对象中,弃用了以前使用 $_SESSION[$namespace][$name] 的方式:
$this->data = new \\Joomla\\Registry\\Registry;
并且,在写SESSION的时候会先做base64_encode:
public function close(){
if ($this->_state !== 'active'){
// @TODO :: generated error here
return false;
}
$session = JFactory::getSession();
$data = $session->getData();
// Before storing it, let's serialize and encode the JRegistry object
$_SESSION['joomla'] = base64_encode(serialize($data));
session_write_close();
return true;
}
这样,$_SESSION 就只剩下了$_SESSION[‘joomla’],而且$_SESSION[‘joomla’] 只存储了Registry的对象$data,在执行read()和write()时候,SESSION是经过base64_encode后的数据,就不会存在read()之后自动反序列化而导致对象注入了。
在反序列化的时候也不存在unserialize参数可控的情况。(可控的只是$data的成员变量)
if (isset($_SESSION['joomla']) && !empty($_SESSION['joomla'])){
$data = $_SESSION['joomla'];
$data = base64_decode($data);
$this->data = unserialize($data);
}
Joomla官方这次的解决方案比较好,不像上次那样治标不治本,这样的态度值得称赞。反观Apache对struts2 漏洞的修复…就不说了。
代码位于,administrator/components/com_categories/models/category.php,save()函数内:
$assoc = $this->getAssoc();
if ($assoc)
{
// Adding self to the association
$associations = $data['associations'];
foreach ($associations as $tag => $id)
{
if (empty($id))
{
unset($associations[$tag]);
}
}
// Detecting all item menus
$all_language = $table->language == '*';
if ($all_language && !empty($associations))
{
JError::raiseNotice(403, JText::_('COM_CATEGORIES_ERROR_ALL_LANGUAGE_ASSOCIATED'));
}
$associations[$table->language] = $table->id;
// Deleting old association for these items
$db = $this->getDbo();
$query = $db->getQuery(true)
->delete('#__associations')
->where($db->quoteName('context') . ' = ' . $db->quote($this->associationsContext))
->where($db->quoteName('id') . ' IN (' . implode(',', $associations) . ')');
$db->setQuery($query);
$db->execute();
if ($error = $db->getErrorMsg())
{
$this->setError($error);
return false;
}
if (!$all_language && count($associations))
{
// Adding new association for these items
$key = md5(json_encode($associations));
$query->clear()
->insert('#__associations');
foreach ($associations as $id)
{
$query->values($id . ',' . $db->quote($this->associationsContext) . ',' . $db->quote($key));
}
$db->setQuery($query);
$db->execute();
if ($error = $db->getErrorMsg())
{
$this->setError($error);
return false;
}
}
}