网站地图    收藏   

主页 > 后端 > 网站安全 >

Finecms1.73代码审计总结缺陷打包和修复 - 网站安全

来源:自学PHP网    时间:2015-04-17 12:00 作者: 阅读:

[导读] FineCMS是一款基于PHP+MySql开发的内容管理系统,采用MVC设计模式实现业务逻辑与表现层的适当分离,使网页设计师能够轻松设计出理想的模板,插件化方式开发功能易用便于扩展,支持自...

FineCMS是一款基于PHP+MySql开发的内容管理系统,采用MVC设计模式实现业务逻辑与表现层的适当分离,使网页设计师能够轻松设计出理想的模板,插件化方式开发功能易用便于扩展,支持自定义内容模型和会员模型,并且可以自定义字段,系统内置文章、图片、下载、房产、商品内容模型,系统表单功能可轻松扩展出留言、报名、书籍等功能,实现与内容模型、会员模型相关联,FineCMS可面向中小型站点提供重量级网站建设解决方案

\

本人看程序代码,只是为了学习,不断学习中…

这是最新1.73版,比之前1.72版在安全方面改进很大,加了不少过滤,也修复了几个公布了的漏洞,不过貌似看更新记录上没说,哈哈。我也是第一次看这套程序,只是最近看到好几个文章爆的它的漏洞,俺也来插一脚。

 

作者:Seay

用到的工具:Seay PHP代码审计工具

下载地址:http://www.2cto.com/soft/201211/35390.html

目录:

一、前台(两处)+后台任意目录浏览+任意文件删除漏洞

二、后台任意文件读取漏洞

三、前台任意文件删除漏洞

四、后台遍地鸡肋注入

五、对找回密码功能代码的看法

载入工具扫描一遍

 先看看过滤函数:

1Core\\Model.php文件

/**

* 字符串转义函数

*

* SQL语句指令安全过滤,用于字符转义

* @access public

* @param mixed $value 所要转义的字符或字符串,注:参数支持数组

* @return string|string

*/

public static function quote_into($value) {

if (is_array($value)) {

foreach ($value as $key=>$string) {

$value[$key] = self::quote_into($string);

}

} else {

//当参数为字符串或字符时

if (is_string($value)) {

$value = ’\” . addslashes($value) . ’\”;

}

}

return $value;

}

 

 

2、 Extensions\\function.php文件

/**

* 安全过滤函数

* @param $string

* @return string

*/

function safe_replace($string) {

$string = str_replace(‘%20′,”,$string);

$string = str_replace(‘%27′,”,$string);

$string = str_replace(‘%2527′,”,$string);

$string = str_replace(‘*’,”,$string);

$string = str_replace(‘”‘,’”‘,$string);

$string = str_replace(“‘”,”,$string);

$string = str_replace(‘”‘,”,$string);

$string = str_replace(‘;’,”,$string);

$string = str_replace(‘<’,'<’,$string);

$string = str_replace(‘>’,'>’,$string);

$string = str_replace(“{“,”,$string);

$string = str_replace(‘}’,”,$string);

return $string;

}

 

 

 

 

3config\attackcode.ini.php文件

 

/**

* GETPOST非法字符过滤配置(防非法字符攻击)

*/

 

return array(

/*

* GET参数非法字符过滤

*/

 

’get’ => array(

‘select ’,

‘insert ’,

‘\”,

‘/*’,

‘*’,

‘../’,

‘union ’,

‘into ’,

‘load_file(‘,

‘outfile ’,

‘script’,

),

 

/*

* POST值非法字符过滤

*/

 

‘post’ => array(

‘<script’,

‘<frame’,

‘<iframe’,

‘<style’,

),

 

 

4core\\Controller.php文件 可以看到 $_COOKIE没过滤

/**

* 用于初始化本类的运行环境,或对基本变量进行赋值

*/

public function __construct() {

if (get_magic_quotes_runtime()) set_magic_quotes_runtime(0);

if (get_magic_quotes_gpc()) {

!isset($_COOKIE) or $_COOKIE = $this->strip_slashes($_COOKIE);

} else {

!isset($_POST) or $_POST = $this->add_slashes($_POST);

!isset($_GET) or $_GET = $this->add_slashes($_GET);

!isset($_SESSION) or $_SESSION = $this->add_slashes($_SESSION);

}

$this->view = View::getInstance();

}

 

过滤函数挺多的,就是过滤的不严谨,

 

一、前台(两处)+后台任意目录浏览+任意文件删除漏洞

我们看到config\attackcode.ini.php文件的GETPOST的过滤函数,很明显的,过滤了../没过滤\\这是返回上一级目录的,不过作者忘记了在windows下有时候斜杠\\和反斜杠/是一样的,

 

第一处:在前台有个会员图片附件和文件附件浏览,我们利用这里就可以任意目录浏览了。

 

Controllers\\member\\\ContentController.php 文件

 

/**

* 附件管理

*/

public function attachmentAction() {

$type = $this->get(‘type’);

$mdir = ’uploadfiles/member/’ . $this->memberinfo['id'] . ’/'; //会员附件目录

if (!file_exists($mdir)) mkdir($mdir);

$mdir = $type == 1 ? $mdir . ’file/’ : $mdir . ’image/’;

if (!file_exists($mdir)) mkdir($mdir);

$dir = urldecode($this->get(‘dir’));

if (strpos($dir, ’../’) !== false) $this->memberMsg(lang(‘m-con-20′), url(‘member/content/attachment’, array(‘type’=>$type)));

$dir = substr($dir, 0, 1) == ’/' ? substr($dir, 1) : $dir;

$data = file_list::get_file_list($mdir . $dir . ’/');

$list = array();

if ($data) {

foreach ($data as $t) {

 

$dir可控

 

http://www.cnseay.com/index.php?s=member&c=content&a=attachment&dir=..\\..\\..\\..\\&type=0

 

\

 

第二处:

我们在前台发布的时候,点击附件,浏览,抓下包就可以看到GET地址

http://www.2cto.com /index.php?c=attachment&a=album&dir=Li5cLi5cLi5cLw==&iframe=0&admin=0

Li5cLi5cLi5cLw== Base64解密后为..\..\..\

\

3、后台任意目录浏览同理。

 

http://www.cnseay.com/index.php?s=admin&c=attachment&dir=XC4u&iframe=0

 

XC4u base64解密后是\..

\

4、后台任意文件删除

同道理,不说了

 

/**

* 删除文件夹

*

* @param string $file_dir 所要删除文件的路径

* @return boolean

*/

public static function delete_dir($file_dir) {

if (!$file_dir) return false;

$parse_dir = self::parse_dir($file_dir);

$file_list = self::get_file_list($parse_dir);

foreach ($file_list as $file) {

if (is_dir($parse_dir . ’/' . $file)) {

self::delete_dir($parse_dir . ’/' . $file);

rmdir($parse_dir . ’/' . $file);

} else {

unlink($parse_dir . ’/' . $file);

}

}

return true;

}

 

修复:过滤完整

二、后台任意文件读取漏洞:

同理,还是斜杠和反斜杠的问题,

 

Controllers\\admin\\ThemeController.php 文件

 

public function editAction() {

$dir = base64_decode($this->get(‘dir’));

$dir = substr($dir, -1) == DIRECTORY_SEPARATOR ? substr($dir, 0, -1) : $dir;

if (strpos($dir, ’../’) !== false) $this->adminMsg(lang(‘m-con-20′));

$name = $this->dir . $dir;

if (!is_file($name)) $this->adminMsg(lang(‘a-con-123′, array(’1′=>$name)));

if ($this->isPostForm()) {

$Pdir = $this->dir == dirname($name) . DIRECTORY_SEPARATOR ? ” : str_replace($this->dir, ”, dirname($name));

file_put_contents($name, stripslashes($_POST['file_content']), LOCK_EX);

$this->adminMsg(lang(‘success’), url(‘admin/theme/index’, array(‘dir’=>base64_encode($Pdir . DIRECTORY_SEPARATOR))), 3, 1, 1);

}

$file = file_get_contents($name);

$this->view->assign(array(

’name’ => str_replace($this->dir, ”, $name),

’file’ => $file,

‘syntax’ => strtolower(trim(substr(strrchr($name, ’.'), 1, 10))),

‘action’ => ’edit’,

‘iswrite’=> is_writable($this->dir),

));

$this->view->display(‘admin/theme_add’);

}

 

$dir可控

 

http://www.cnseay.com/index.php?s=admin&c=theme&a=edit&dir=XC4uXC4uXGNvbmZpZ1xkYXRhYmFzZS5pbmkucGhw

 

XC4uXDEudHh0 base64解密后是\..\..\config\database.ini.php

\

 

修复:过滤完整

三、前台任意文件删除漏洞

看工具的提示:

\

265 /**

266 * 删除附件

267 */

268 public function delattachmentAction() {

269 $type = $this->get(‘type’);

270 $mdir = ’uploadfiles/member/’ . $this->memberinfo['id'] . ’/'; //会员附件目录

271 if (!file_exists($mdir)) mkdir($mdir); //可能存在畸形目录创建漏洞

272 $mdir = $type == 1 ? $mdir . ’file/’ : $mdir . ’image/’;

273 if (!file_exists($mdir)) mkdir($mdir); //可能存在畸形目录创建漏洞

274 $dir = urldecode($this->get(‘dir’));

275 $dir = substr($dir, 0, 1) == ’/' ? substr($dir, 1) : $dir;

276 if (realpath($mdir . $dir) == false || strpos($dir, ’../’) !== false) $this->memberMsg(lang(‘m-con-21′));

277 if (file_exists($mdir . $dir)) {

278 if (is_dir($mdir . $dir)) {

279 $this->delDir($mdir . $dir);

280 $this->memberMsg(lang(‘success’), url(‘member/content/attachment’, array(‘type’=>$type)), 1);

281 } else {

282 unlink($mdir . $dir); //可能存在任意文件删除漏洞

283 $this->memberMsg(lang(‘success’), url(‘member/content/attachment’, array(‘type’=>$type, ’dir’=>urlencode(dirname($dir)))), 1);

284 }

285 } else {

286 $this->memberMsg(lang(‘m-con-22′, array(’1′=>$dir)));

287 }

288 }

 

 

很明显的,$dir我们可控,就是任意文件删除了,其实也是会员的一个删除附件的功能

http://www.cnseay.com/finecms/index.php?s=member&c=content&a=delattachment&dir=..%5C%5C..%5C%5C..%5C%5C..%5C%5Ccache..%5C%5C%2Finstall.lock&type=

 

删除安装锁文件,可重装程序,后台拿shell就很简单了

修复:过滤完整

 

四、后台遍地鸡肋注入

后台注入很多很多,不过要在后台关闭 “禁止非法字符提交”的时候才能用,

可能作者做这么一个开关,是考虑到用户体验吧,只是说明有这么一个缺陷,没有什么价值。

\

 

修复:intval()一下,或者其他

 

五、对找回密码功能代码的看法

controllers\\member\\Common.php文件中有一个找回密码的函数。

 

86 /**

87 * 密码找回邮件通知

88 */

89 protected function passEmail($username, $email) {

90 if (empty($username) || empty($email)) return false;

91 $rand = md5(rand(1000, 9999)); //随机数

92 $link = $this->get_server_name() . url(‘member/repass/find’, array(‘id’=>base64_encode(time() . ’|' . $rand . ’|' . md5($username))), 1);

93 $this->member->update(array(‘randcode’=>$rand), ”username=’” . $username . ”‘”); //可能存在SQL语句,请注意是否过滤

94 mail::set($this->site);

95 $content = $this->memberconfig['pass_tpl'] ? $this->memberconfig['pass_tpl'] : lang(‘m-com-6′, array(’1′=>$username, ’2′=>$link));

96 $content = str_replace(array(‘{username}’, ’{link}’), array($username, $link), $content);

97 return mail::sendmail($email, lang(‘m-com-7′, array(’1′=>$this->site['SITE_NAME'])), htmlspecialchars_decode($content));

98 }

 

 

它的功能就是给生成一个找回密码的链接发送的用户邮箱,同时修改用户randcode字段为生成的随机数的md5。

 

我们分析下找回密码链接:

$link = $this->get_server_name() . url(‘member/repass/find’, array(‘id’=>base64_encode(time() . ’|' . $rand . ’|' . md5($username))), 1);

 

就是这句代码拼接的链接,

get_server_name() 获得网站域名,后面加上member/repass/find,然后再加上时间戳time()和1000到9999之间的一个随机数和用户名的md5 的Base64编码,可以看出,时间戳可碰撞,随机数可碰撞,用户名md5可知,那么我们写一个程序,两个线程同时提交不同用户找回密码的请求,那时间戳相差就很小很小了,现在我们能大概确定时间戳的范围,用户名MD5知道,现在我们写一个自动提交的程序,当然要能判断链接是否正确,记得前两天我写了一个,可以到我博客去下载,我们改下程序,加一个自动生成1000-9999之间的数,不断递增并MD5加密,拼接链接,然后自动访问生成的链接,当出现成功特征字符,就说明成功了。如此看来,这个找回密码的方法还是有一定的危险性,当然这里只是对这个方法来讨论下。

 

修复:找回密码链接最好类似 md5(username+password+other) 这种形式,个人感觉会好一点。

 

 

之前说的cooki没过滤,我们来看下设置cookie的地方,

cookie::set(‘member_id’, $member['id'], $time);

cookie::set(‘member_code’, substr(md5(SITE_MEMBER_COOKIE . $member['id']), 5, 20), $time);

后面用到cookie的地方,比如

/**

* 获取会员信息

*/

protected function getMember() {

if (cookie::is_set(‘member_id’) && cookie::is_set(‘member_code’)) {

$uid = cookie::get(‘member_id’);

$code = cookie::get(‘member_code’);

if (!empty($uid) && $code == substr(md5(SITE_MEMBER_COOKIE . $uid), 5, 20)) {

$_memberinfo = $this->member->find($uid);

$member_table = $this->membermodel[$_memberinfo['modelid']]['tablename'];

if ($_memberinfo && $member_table) {

$_member = $this->model($member_table);

$memberdata = $_member->find($uid);

if ($memberdata) {

$_memberinfo = array_merge($_memberinfo, $memberdata);

$this->memberedit = 1; //不需要完善会员资料

}

if ($this->memberconfig['uc_use'] == 1 && function_exists(‘uc_api_mysql’)) {

$uc = uc_api_mysql(‘user’, ’get_user’, array(‘username’=> $_memberinfo['username']));

if ($uc != 0) $_memberinfo['uid'] = $uc[0];

}

return $_memberinfo;

}

}

}

return false;

}

 

这个验证用的比较好。

 

不看了,大半夜的,先看到这,以后有时间再把这套程序看完,这个文章写的比较急,将就着看吧,这几天天天忙着找工作。蛋疼啊


自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论