20-PHP代码审计——damicms5.3-5.4漏洞分析
Posted songly_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20-PHP代码审计——damicms5.3-5.4漏洞分析相关的知识,希望对你有一定的参考价值。
本次分析damicms的两个漏洞,一个是5.3版本的任意文件操作漏洞,另一个是5.4版本的逻辑支付漏洞。
漏洞一:支付逻辑漏洞
damicms5.4版本存在订单支付逻辑漏洞,提交订单后可将商品的数量修改成-1,然后选择站内支付,可以成功提交订单。
影响版本:
damicms5.4
漏洞环境:
damicms5.4
php5.6.27
在首页的产品展示中随便选择一个商品点击购买,然后填完地址信息后选择站内扣款,开启burpsuite抓包然后点击提交订单,如下所示
然后将qty字段的值修改成-1,然后点击Go放行数据包,可以看到响应消息返回提交订单成功,漏洞复现成功。
接下来我们分析后台订单的业务逻辑,找到lib/Action/MemberAction的dobuy方法
//订单处理
function dobuy(){
self::is_login();
if (!$_POST) {
exit();
}
if (!is_array($_POST['id'])) {
$this->error('您的购物为空!');
exit();
}
if ($_POST['realname'] == '' || $_POST['tel'] == '') {
$this->error('收货人信息为空!');
exit();
}
$trade_type = (int)$_POST['trade_type'];
$iscart = (int)$_POST['iscart'];
$group_trade_no = "GB" . time() . "-" . $_SESSION['dami_uid'];
if ($iscart == 1) {
import('@.ORG.Cart');
$cart = new Cart();
$cart->destroy();
}
//过滤可能出现的xss
$_POST = array_map('remove_xss', $_POST);
$trade = M('member_trade');
if (C('TOKEN_ON') && !$trade->autoCheckToken($_POST)) {
$this->error(L('_TOKEN_ERROR_'));
}//防止乱提交表单
//循环出购物车 写进数据库
if ($trade_type == 1) {
//......
} else if ($trade_type == 2) {
//......
//接着会执行这段逻辑
} else if ($trade_type == 3) {
$title = '';
$total_fee = 0;
$total_num = 0;
for ($i = 0; $i < count($_POST['id']); $i++) {
$price = (float)M('article')->where('aid=' . intval($_POST['id'][$i]))->getField('price');
//过滤非数字字符串
if (!is_numeric($_POST['id'][$i]) || !is_numeric($_POST['price'][$i]) || !is_numeric($_POST['qty'][$i])) {
continue;
}
//这行代码是漏洞的触发点
$total_fee += (intval($_POST['qty'][$i]) * $price) * 1;
}
$have_money = M('member')->where('id=' . $_SESSION['dami_uid'])->getField('money');
if ($have_money < $total_fee) {
$this->assign('jumpUrl', U('Member/chongzhi'));
$this->error('您的余额不足,请充值!');
exit();
}
for ($i = 0; $i < count($_POST['id']); $i++) {
//这里还过滤了一次非数字字符串
if (!is_numeric($_POST['id'][$i]) || !is_numeric($_POST['price'][$i]) || !is_numeric($_POST['qty'][$i])) {
continue;
}
$data['gid'] = $_POST['id'][$i];
$data['uid'] = $_SESSION['dami_uid'];
$data['price'] = (float)M('article')->where('aid=' . $data['gid'])->getField('price');//必须
$data['province'] = $_POST['province'];
$data['city'] = $_POST['city'];
$data['area'] = $_POST['area'];
$data['sh_name'] = $_POST['realname'];
$data['sh_tel'] = $_POST['tel'];
$data['address'] = $_POST['address'];
$data['group_trade_no'] = $group_trade_no;
$data['out_trade_no'] = "DB" . time() . "-" . $_SESSION['dami_uid'];
$data['servial'] = $_POST['gtype'][$i];
$data['status'] = 1;//已付款等待发货
$data['trade_type'] = 3;
$data['addtime'] = time();
$data['num'] = (int)$_POST['qty'][$i];
$total_num += $data['num'];
$trade->add($data);
if (strlen($title) < 400) {
$title .= $_POST['name'][$i] . " 数量:" . $data['num'] . ' 单价:' . $data['price'] . '<br>';
}
}
//扣款
M('member')->setDec('money', 'id=' . $_SESSION['dami_uid'], $total_fee);
if (intval(C('MAIL_TRADE')) == 1) {
$config = F('basic', '', './Web/Conf/');
$user_name = $config[sitetitle] . '管理员';
$subject = $config[sitetitle] . '订单提醒';
$bodyurl = '下单时间:' . date('Y-m-d H:i:s', time()) . '<br>会员编号:' . $_SESSION['dami_uid'] . '<br>姓名:' . $_POST['realname'] . '<br>订单号:' . $group_trade_no . '<br>付款方式:站内扣款<br>订购物件:<br>' . $title . '<br>总数量:' . $total_num . '<br>总金额:' . $total_fee . '元';
$sendto_email = C('MAIL_TOADMIN');
$email_port = C('MAIL_PORT');
send_mail($sendto_email, $user_name, $subject, $bodyurl, $email_port);
}
$this->assign('group_trade_no', $group_trade_no);
$this->display('buysuccess');
} else {
$this->error('交易方式不确定!');
exit();
}
}
dobuy函数内部对提交的数据其实过滤并不算严格,接着调用了这行代码:
$total_fee += (intval($_POST['qty'][$i]) * $price) * 1;
intval函数的作用是获取变量的整数值,这里的qty代表数量,qty的值为-1并且没有对负数进行过滤,最后计算得到的金额为负,导致产生漏洞。
在订单页面中可以看到显示已付款
支付漏洞分析思路可以从负数和整形溢出两方面入手,负数很好理解,因为像支付这种业务逻通常是不允许出现负数情况的,一般都要做好负数的处理。另一个就是整形溢出,首先php是一个弱类型语言,只有当程序在运行期间才会确定变量的数据类型(自动类型转换),因此在编写代码期间我们可以对变量赋予任何数据类型,因此php在进行类型转换时有可能出现整形溢出导致漏洞产生。例如int的数据类型的范围是-2147483648 ~ 2147483647,假设金额的数据类型是int,然后通过抓包把金额改成4294967297时,php在对输入的4294967297数据类型强制转换成int时就会导致整形溢出,最后ph会p把溢出的数据丢弃,此时金额的数目可能就变成了1。
漏洞二:任意文件操作漏洞
影响版本:
damicms5.5.3
漏洞环境:
damicms5.5.3
php5.6.27
任意文件删除漏洞主要出现在后台,在插件工具 ---> 模板管理
随便选择一个文件点击删除,开启Burpsuite工具抓包,http请求数据格式如下
|Web|Tpl|default|footer*html代表要删除的文件路径,Tpl/Del/id/是del函数的路由,找到Admin\\Lib\\Action\\TplAction.class.php文件,定位到del方法
// 删除模板
public function del(){
//提取要删除的文件路径
$id = dami_url_repalce(str_replace('*','.',trim($_GET['id'])));
//校验请求的文件路径是否具备删除的权限
if (!substr(sprintf("%o",fileperms($id)),-3)){
$this->error('无删除权限!');
}
//删除文件
@unlink($id);
if (!empty($_SESSION['tpl_jumpurl'])){
//assign函数中参数1为展示页面中需要的参数
$this->assign("jumpUrl",$_SESSION['tpl_jumpurl']);
}
else{
$this->assign("jumpUrl",'?s=Tpl/index');
}
$this->success('删除文件成功!');
}
del方法内部调用了dami_url_repalce函数提取请求中的文件路径,分析dami_url_repalce函数做了哪些处理
//替换采集等通过url参数传值
function dami_url_repalce($xmlurl,$order='asc'){
if($order=='asc'){
return str_replace(array('|','@','#'),array('/','=','&'),$xmlurl);
}else{
return str_replace(array('/','=','&'),array('|','@','#'),$xmlurl);
}
}
该函数内部会将xmlurl的文件路径中的竖线“|”符号会被替换成“/”符号,最终替换后的文件路径为./Web/Tpl/default/footer.html ,这里没有对“../或./”这些敏感,危险的文件路径进行任何实质性的安全过滤。接着del函数内部中的fileperms函数判断了请求的文件路径是否具备删除权限,然后调用unlink函数删除文件。
假设是在黑盒测试的环境下,我们不知道该站点的网站文件目录结构,可以通过工具遍历爆破网站的目录结构,如果读取到一些敏感的目录文件时,再构造恶意的文件路径,例如通过任意文件删除漏洞,删除install目录下的install.lck文件,然后再重装网站从而截取数据库的管理员权限。
在http请求中将参数s的值替换成Tpl/Del/id/.|install*lck ,如下所示
响应消息返回200,成功将install.lck文件删除,接着我们就可以直接执行install/index.php文件重装网站截取数据库的管理员权限。
add函数同样存在任意文件读取漏洞
// 编辑模板
public function add(){
$filename = dami_url_repalce(str_replace('*','.',trim($_GET['id'])));
if (empty($filename)) {
$this->error('模板名称不能为空!');
}
$content = read_file($filename);
$this->assign('filename',$filename);
$this->assign('content',htmlspecialchars($content));
$this->display('add');
}
add函数内部同样也没有过滤,只判断了传入的文件名是否为空,接着就读取了文件的内容。
读取Web/Conf/config.php文件的内容,如下所示
漏洞分析完毕。
以上是关于20-PHP代码审计——damicms5.3-5.4漏洞分析的主要内容,如果未能解决你的问题,请参考以下文章