通过DedeCMS学习php代码审计

作者: 黑客网 分类: 正规黑客联系方式 发布时间: 2022-05-26 14:37

织梦(DedeCms)也是一个国产内容管理系统,曾经爆出过众多漏洞,甚至还有人开发了dedecms漏洞一键扫描器

DedeCms和PHPCMS活跃的年代差不多,大概是2015年前,目前也都少部分人在使用

DedeCMS 目前最新版是dedecms v5.7sp2,最后更新时间大概为2018年

这里就不记录安装过程了,通过安装过程获知默认后台密码是admin/admin

0x01 全局分析

个人习惯是对程序做一个比较明晰的全局分析,至少要知道程序的入口文件是什么流程,程序有多少入口文件,对外部数据有什么全局处理方式等等

对dedecms对全局分析时,首先选择了根目录下的index.php,慢慢分析会发现,dedecms是一个多入口文件的形式,不过每个入口文件的流程都大致相同。

通过全局分析得知dedecms大致有3个主要功能,也通过不同的入口文件进入

1)网站前台首页,没有什么功能点

2)会员中心,默认是关闭该功能的,需要后台打开

3)管理员后台

跟踪前台index.php的流程

首先跟一遍index.php的流程,index.php首先会加载common.inc.php,就先看看这个文件会做什么

include/common.inc.php

// 定义常量 // 检查GPC数据是否为cfg等系统定义变量 CheckRequest() // 使用addslashes()过滤GPC数据,并注册GPC数据到程序变量 _RunMagicQuotes() // 如果存在文件上传的变量,加载文件上传的安全函数 if($_FILES){require_once(DEDEINC.'/uploadsafe.inc.php');} // 数据库配置文件,里面是数据库账号密码相关变量信息 require_once(DEDEDATA.'/common.inc.php'); // 自动加载类库处理 require_once(DEDEINC.'/autoload.inc.php'); // 引入数据库类,这步会直接连接数据库,并返回一个数据库对象$db=$dsql require_once(DEDEINC.'/dedesqli.class.php'); // 全局常用函数 require_once(DEDEINC.'/common.func.php'); // 模块MVC框架需要的控制器和模型基类 require_once(DEDEINC.'/control.class.php'); require_once(DEDEINC.'/model.class.php');

common.inc.php 做了很多程序的初始化工作,代码审计时需要重点关注程序处理GPC这些外部数据的方式

common.inc.php 全局处理数据的代码:

foreach(Array('_GET','_POST','_COOKIE') as $_request) { foreach($$_request as $_k => $_v) { if($_k == 'nvarname') ${$_k} = $_v; else ${$_k} = _RunMagicQuotes($_v); } }

可以看到这个时期的DEDECMS也是使用了$$直接注册了GPC的变量,有可能存在变量覆盖的问题

uploadsafe.inc.php

这里再关心下文件上传的安全函数

include/uploadsafe.inc.php

$cfg_not_allowall为上传文件名后缀的黑名单,后面具体分析限制的逻辑

$imtypes为一些图片的MIME类型

$cfg_not_allowall = "php|pl|cgi|asp|aspx|jsp|php3|shtm|shtml"; $keyarr = array('name', 'type', 'tmp_name', 'size'); foreach($_FILES as $_key=>$_value) { $$_key = $_FILES[$_key]['tmp_name']; ${$_key.'_name'} = $_FILES[$_key]['name']; ${$_key.'_type'} = $_FILES[$_key]['type'] = preg_rep1ace('#[^0-9a-z\./]#i', '', $_FILES[$_key]['type']); ${$_key.'_size'} = $_FILES[$_key]['size'] = preg_rep1ace('#[^0-9]#','',$_FILES[$_key]['size']); if(!empty(${$_key.'_name'}) && (preg_match("#\.(".$cfg_not_allowall.")$#i",${$_key.'_name'}) || !preg_match("#\.#", ${$_key.'_name'})) ) { if(!defined('DEDEADMIN')) { exit('Not Admin Upload filetype not allow !'); } } $imtypes = array ( "image/pjpeg", "image/jpeg", "image/gif", "image/png", "image/xpng", "image/wbmp", "image/bmp" ); if(in_array(strtolower(trim(${$_key.'_type'})), $imtypes)) { $image_dd = @getimagesize($$_key); if (!is_array($image_dd)) { exit('Upload filetype not allow !'); } } }

这里结合实际例子来分析上诉代码,上传一个名为2.jpg的正常文件,$_FILES数组如下图

图片.png

uploadsafe.inc.php会注册$_FILES中数据到全局变量,在实例中便会注册以下变量

$litpic = "/Applications/MAMP/tmp/php/php8bhoXG" $litpic_name = "2.jpg" $litpic_type = "image/jepg" $litpic_stze = "4"

然后先看第一个文件上传的限制:

if( !empty(${$_key.'_name'}) && ( preg_match("#\.(".$cfg_not_allowall.")$#i",${$_key.'_name'}) || !preg_match("#\.#", ${$_key.'_name'}) ) ){ if(!defined('DEDEADMIN')){ exit('Not Admin Upload filetype not allow !'); } }

第一个if语句块:

条件1:$_FILES[$_key]['name']即2.jpg,若上传的文件名不为空,该条件为真

条件2是个或条件,满足其一即可

条件2.1:$_FILES[$_key]['name']即2.jpg,若后缀名在黑名单中该条件为真

条件2.2:$_FILES[$_key]['name']即2.jpg,若没有符号.,即无后缀文件,该条件为真

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

标签云