Joomla!3.7.0 Core SQL注入漏洞.
POC
这次干脆先放出poc吧。
http://localhost:2500/Joomla370/index.php?
option=com_fields
&view=fields
&layout=modal
&list[fullordering]=updatexml(1,concat(0x3e,database()),0)
这次根据参数的传入流程来进行分析。
漏洞
危害组件
3.7.0版本中出现了com_field
组件,无需授权即可访问。查看...\components\com_fields\controller.php
,在第27行左右,其相关代码如下:
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
,而这两个参数都是我们可控的。若满足则将会加载JPATH_ADMINISTRATOR
中的com_fields
组件,并且将base_path
设置为 JPATH_COMPONENT_ADMINISTRATOR
,之后调用父类的构造方法。
传入sql语句
在调用父类构造方法后,一路运行到...\Joomla370\libraries\legacy\controller\legacy.php
中,约莫707行,这时会通过$this->$doTask
调用display()
函数。
跟进display()
函数,它位于 ...\Joomla370\libraries\legacy\controller\legacy.php
,接着运行至legacy.php的约莫671行左右,调用了视图(view)的display()
函数。我们跟进一下,跳转进入...\Joomla370\administrator\components\com_fields\views\fields\view.html.php
,
此时运行到,下面这条语句,给get()
传入的参数为State
$this->state = $this->get('State');
我们跟进这个get()
函数,一直运行到422行,
之后将会调用 getState()
,跟进,进入...\Joomla370\libraries\legacy\model\legacy.php
之后会调用filedsModel类中的populateState()
,跟进后会发现调用其父类的populateState()
函数,其定义在 ...\Joomla370\libraries\legacy\model\list.php
中,约莫在第495行,相关代码如下:
..省略..
if ($list = $app->getUserStateFromRequest($this->context . '.list', 'list', array(), 'array'))
..省略..
这里我们先跟进一下getUserStateFromRequest()
,它的定义在...\Joomla370\libraries\cms\application\cms.php
中,在该函数结束后,它获取了我们通过get方法传入的参数,也就是说,我们成功的控制了fullordering
的值。
在该函数运行完后,流程将会回到前面的那个定义在...\Joomla370\libraries\cms\application\cms.php
中的populateState()
函数。此时运行的代码如下:
foreach ($list as $name => $value)
{
// Exclude if blacklisted
if (!in_array($name, $this->listBlacklist))
{
// Extra validations
switch ($name){...}
$this->setState('list.' . $name, $value);
}
}
如果数组的key不在黑名单(blacklisted)中,将会为$list
变量根据相应的State
进行注册,在这部分函数运行到结束部分,可以看见成功的控制了list
数组的fullordering
的值。
查看变量,如下:
注入过程
接下来继续运行,一直运行回到Joomla370\administrator\components\com_fields\views\fields\view.html.php
中的display()
函数中。
跟进这一行 $this->get('Items');
,进入...\Joomla370\libraries\legacy\view\legacy.php
,约莫在422行,这里的行为跟前面分析类似,此后将会调用getitem()
:
继续跟进,进入...\Joomla370\libraries\legacy\model\list.php
,约莫在186行:
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'));
}
通过_getList
调用了_getListQuery
,继续跟进,进入...\Joomla370\libraries\legacy\model\list.php
,约莫在 132行,
if ($lastStoreId != $currentStoreId || empty($this->query))
{
$lastStoreId = $currentStoreId;
$this->query = $this->getListQuery();
}
调用了 getListQuery()
,继续跟进,进入 ...\Joomla370\administrator\components\com_fields\models\fields.php
,一直运行到约莫在 305 行,调用getState
方法,传入list.fullordering
参数。相关代码如下:
查看变量表:
之后在第314行,将$listOrdering
带入查询,相关代码如下:
$query->order($db->escape($listOrdering) . ' ' . $db->escape($orderDirn));
在进行$query->order
之前,会先进行一次过滤,跟进$db->escape
,进入...\Joomla370\libraries\joomla\database\driver\mysqli.php
,约莫242行,相关代码如下:
public function escape($text, $extra = false)
{
$this->connect();
$result = mysqli_real_escape_string($this->getConnection(), $text);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
对于传入的$text
通过mysqli_real_escape_string()
进行过滤,只转义了一些字符。因此可以通过构造进行成功的注入。