起因文件include\plugin\payway\alipay\return_url_db.php:这是一个付款的调用文件$alipayNotify = new AlipayNotify($alipay_config);
$verify_result = $alipayNotify->verifyReturn();
//验证成功
if ($verify_result) {
//商户订单号
$out_trade_no = $_GET['out_trade_no'];
echo $out_trade_no;
//支付宝交易号
$trade_no = $_GET['trade_no'];
$info = $db->pe_select('order', array('order_id'=>$out_trade_no));
if ($_GET['trade_status'] == 'WAIT_SELLER_SEND_GOODS') {
if ($info['order_state'] == 'notpay') {
$order['order_outid'] = $trade_no;
$order['order_payway'] = 'alipay_db';
$order['order_state'] = 'paid';
$order['order_ptime'] = time();
$db->pe_update('order', array('order_id'=>$out_trade_no), $order);
}
一般我会先去证明漏洞是否存在然后再看上面的验证条件(首先这里肯定不是必须管理员情况下才能够利用),这里用了最简单的GET获取直接带入pe_select(跟人)public function pe_select($table, $where = '', $field = '*')
{
//处理条件语句
$sqlwhere = $this->_dowhere($where);
return $this->sql_select("select {$field} from `".dbpre."{$table}` {$sqlwhere} limit 1");
}
继续跟进_dowhereprotected function _dowhere($where)
{
if (is_array($where)) {
foreach ($where as $k => $v) {
$k = str_ireplace('`', '', $k);
if (is_array($v)) {
$where_arr[] = "`{$k}` in('".implode("','", $v)."')";
}
else {
in_array($k, array('order by', 'group by')) ? ($sqlby = " {$k} {$v}") : ($where_arr[] = "`{$k}` = '{$v}'");
}
}
$sqlwhere = is_array($where_arr) ? 'where '.implode($where_arr, ' and ').$sqlby : $sqlby;
}
else {
$where && $sqlwhere = (stripos(trim($where), 'order by') === 0 or stripos(trim($where), 'group by') === 0) ? "{$where}" : "where 1 {$where}";
}
return $sqlwhere;
}
可以看到这里面并没有应用到安全函数,只是对内容进行一些简单的链接,分割等操作public function sql_select($sql)
{
$row = array();
return $row = $this->fetch_assoc($this->query($sql));
}
而在sql_select函数中就直接执行查询了,所以可以肯定漏洞存在,那么现在就是绕过上面的判断
跟进verifyReturn看看
判断文件:/include/plugin/payway/alipay/lib/alipay_notify.class.phpfunction verifyReturn(){
if(empty($_GET)) {//判断POST来的数组是否为空
return false;
}
else {
//生成签名结果
$isSign = $this->getSignVeryfy($_GET, $_GET["sign"]);
//获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
$responseTxt = 'true';
if (! empty($_GET["notify_id"])) {$responseTxt = $this->getResponse($_GET["notify_id"]);}
//写日志记录
//if ($isSign) {
// $isSignStr = 'true';
//}
//else {
// $isSignStr = 'false';
//}
//$log_text = "responseTxt=".$responseTxt."\n return_url_log:isSign=".$isSignStr.",";
//$log_text = $log_text.createLinkString($_GET);
//logResult($log_text);
//验证
//$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
if (preg_match("/true$/i",$responseTxt) && $isSign) {
return true;
} else {
return false;
}
}
}
函数开始先判断GET是否为空,我们跟入看看function getSignVeryfy($para_temp, $sign) {
//除去待签名参数数组中的空值和签名参数
$para_filter = paraFilter($para_temp);
//对待签名参数数组排序
$para_sort = argSort($para_filter);
//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
$prestr = createLinkstring($para_sort);
$isSgin = false;
switch (strtoupper(trim($this->alipay_config['sign_type']))) {
case "MD5" :
$isSgin = md5Verify($prestr, $sign, $this->alipay_config['key']);
break;
default :
$isSgin = false;
}
return $isSgin;
}
前面三个函数都是对我们GET进来的数组进行整理与链接的操作
这里的三个变量,有两个变量是我们可控的,而$this->alipay_config['key'] 是我们在支付宝中的接口key值是无法控的
而我们跟进md5Verifyfunction md5Verify($prestr, $sign, $key) {
$prestr = $prestr . $key;
$mysgin = md5($prestr);
if($mysgin == $sign) {
return true;
}
else {
return false;
}
}
果然与书上写的一样,o(︶︿︶)o 唉 这也同时造成这个漏洞鸡肋的唯一点
漏洞复现:
第二枚就简单实在好利用:
漏洞文件:include\plugin\payway\ebank\Receive.php
include('../../../../common.php');
$cache_payway = cache::get('payway');
$payway = unserialize($cache_payway['ebank']['payway_config']);
$key = $payway['ebank_md5'];
$v_oid =trim($_POST['v_oid']);
$v_pmode =trim($_POST['v_pmode']);
$v_pstatus =trim($_POST['v_pstatus']);
$v_pstring =trim($_POST['v_pstring']);
$v_amount =trim($_POST['v_amount']);
$v_moneytype =trim($_POST['v_moneytype']);
$remark1 =trim($_POST['remark1']);
$remark2 =trim($_POST['remark2']);
$v_md5str =trim($_POST['v_md5str']);
/**
* 重新计算md5的值
*/
$md5string=strtoupper(md5($v_oid.$v_pstatus.$v_amount.$v_moneytype.$key));
echo $key;
/**
* 判断返回信息,如果支付成功,并且支付结果可信,则做进一步的处理
*/
if ($v_md5str==$md5string) {
if($v_pstatus=="20") {
$info = $db->pe_select('order', array('order_id'=>$v_oid));
if ($info['order_state'] == 'notpay') {
$order['order_outid'] = $v_pmode;
$order['order_payway'] = 'ebank';
$order['order_state'] = 'paid';
$order['order_ptime'] = time();
$db->pe_update('order', array('order_id'=>$v_oid), $order);
pe_success('订单支付成功...');
}
}
else {
echo "支付失败";
}
}
else{
echo "
校验失败,数据可疑";
}
?>