内容简介:【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC)
作者: Balis0ng
预估稿费:400RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
传送门
【漏洞预警】Joomla!3.7.0 Core SQL注入漏洞(更新漏洞环境)
前言
Joomla!是世界上最受欢迎的内容管理系统(CMS)解决方案之一。 它可以让用户自定义构建网站实现强大的在线应用程序。据不完全统计互联网上超过3%的网站运行Joomla!,同时它占有全球9%以上的CMS市场份额。
截止至2016年11月,Joomla!的总下载量超过7800万次。目前Joomla!官方还提供了超过7800个扩展插件(含免费、收费插件)及其他的可用资源可供下载。
漏洞概述
漏洞等级 : 严重
漏洞类型 :SQL 注入
利用难度 :简单
利用方式 :远程
影响版本 :Joomla! 3.7.0 Core
漏洞简述 :这个漏洞出现在3.7.0新引入的一个组件“com_fields”, 这个组件任何人都可以访问,无需登陆验证 。由于对请求数据过滤不严导致 sql 注入,sql注入对导致数据库中的敏感信息泄漏,例如用户的密码hash以及登陆后的用户的session(如果是获取到登陆后管理员的session,那么整个网站的后台系统可能被控制)。
漏洞分析
看了一下漏洞概要,发现写的比较简单,自己复现的时候发现调用关系比较复杂,特别是在类函数的调用中,稍不注意就钻错函数了。
首先,在E:/www/joomla370/components/com_fields/controller.php中有这样一段代码:
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和一个layout,然后满足一定条件就进入到了if中,这里有一个关键点,设置了一个$config['base_path']为administrator的components目录。
然后调用父类的构造方法:
parent::__construct($config);
我们跟一下这个方法:
在E:/www/joomla370/libraries/legacy/controller/legacy.php中的__construct方法,函数体太长,就不贴了,主要在395行,加入我们的model:
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); }
这里是进入到了else这个分支,然后这个地方的$this->basePath就是我们最开始的注意点,是administators/components
然后设置完了后就调用该类中的display方法进行一个实际操作,包括创建model之类的操作,在第613行:
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); }
这里的$viewName是取自于view,也就是fields,然后这里先调用getView函数取得视图,然后再调用了getModel进行一个操作:
public function getModel($name = '', $prefix = '', $config = array()) { if (empty($name)) { $name = $this->getName(); } if (empty($prefix)) { $prefix = $this->model_prefix; } if ($model = $this->createModel($name, $prefix, $config)) { // Task is a reserved state $model->setState('task', $this->task); // Let's get the application object and set menu information if it's available $menu = JFactory::getApplication()->getMenu(); if (is_object($menu)) { if ($item = $menu->getActive()) { $params = $menu->getParams($item->id); // Set default state data $model->setState('parameters.menu', $params); } } } return $model; }
这里调用createModel方法进行类的实例化。
并返回$model。
接着调用setModel函数将$model push到$view中。
到665行调用视图的display函数:
else { $view->display(); } return $this;
然后我们跳转到视图的display函数中来,在E:/www/joomla370/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,我们跟进这个get函数,在E:/www/joomla370/libraries/legacy/view/legacy.php中第417行:
$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',那么拼接起来后的方法名就是getState,然后调用这个方法,由于这个方法在filedsmodel类中没有,那么会往父类里面找,根据一层层的继承关系,我们找到了getState方法,在E:/www/joomla370/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; }
然后这里会调用populateState方法,这个populateState只会调用一次。这个方法到底是哪个类中的呢?毋庸置疑,是filedsModel中的populateState方法:
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);
可以看到这里有调用了父类populateState方法,我们跟进到父类,在E:/www/joomla370/libraries/legacy/model/list.php
中,也就是漏洞原作者开始写的地方:
在第496行:
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)
这里取了个list的值进来赋值给了$list
然后将$list遍历出来,接着switch 键值。switch代码过多,我们只需要看switch完后的一个操作,也就是在567行:
$this->setState('list.' . $name, $value);
通过这个我们可以设置list.fullfullordering
那么这里是设置了,总得取出来才行啊,那我们开始看如何将这个值取出来的。
上面说道在视图文件中的display方法中,利用get('State')来调用了getState方法。紧跟着这个操作的下一行,就有一个get('Item')。
public function display($tpl = null) { $this->state = $this->get('State'); $this->items = $this->get('Items');
通过上面的分析,我们可以轻松的找到正确调用的方法,没错,就是E:/www/joomla370/libraries/legacy/model/list.php中的getItems方法:
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')); }
这里调用了一个_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(); }
然后这里又调用了一个getListQuery方法,这里调用的getListQuery不是此类的getListQuery,而是子类,也就是filedsModel类里的getListQuery了,我们看一下该方法,在该方法的最后呢,也就是304左右:
// 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; }
这里就调用getState将我们前面设置的list.fullordering的值给取了出来,然后带入到了order函数中去了,就造成了一个order by的注入。
PoC
http://localhost/joomla370/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml(1,concat(0x3e,user()),0)
PoC验证截图
漏洞环境
感谢开源社区力量
漏洞靶场环境 由phithon维护
Vulhub是一个面向大众的开源漏洞靶场,无需 docker 知识,简单执行两条命令即可编译、运行一个完整的漏洞靶场镜像。
https://github.com/phith0n/vulhub/tree/master/joomla/CVE-2017-8917
总结
总的来说,注入触发的流程也不新鲜了,早在之前的joomla爆的一些组件注入中,比如之前的history组件注入,也多数是因为populate函数中的对一些参数过滤不严格,然后导致后来取出来的时候就产生了注入。
参考链接
https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html
传送门
【漏洞预警】Joomla!3.7.0 Core SQL注入漏洞(更新漏洞环境)
本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/3870.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析
- 【漏洞分析】CouchDB漏洞(CVE–2017–12635, CVE–2017–12636)分析
- 【漏洞分析】lighttpd域处理拒绝服务漏洞环境从复现到分析
- 漏洞分析:对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- 路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析
- Weblogic IIOP反序列化漏洞(CVE-2020-2551) 漏洞分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
最优化导论
Edwin K. P. Chong、Stanislaw H. Zak / 孙志强、白圣建、郑永斌、刘伟 / 电子工业出版社 / 2015-10 / 89.00
本书是一本关于最优化技术的入门教材,全书共分为四部分。第一部分是预备知识。第二部分主要介绍无约束的优化问题,并介绍线性方程的求解方法、神经网络方法和全局搜索方法。第三部分介绍线性优化问题,包括线性优化问题的模型、单纯形法、对偶理论以及一些非单纯形法,简单介绍了整数线性优化问题。第四部分介绍有约束非线性优化问题,包括纯等式约束下和不等式约束下的优化问题的最优性条件、凸优化问题、有约束非线性优化问题的......一起来看看 《最优化导论》 这本书的介绍吧!