### 简要描述:
继续绕啊绕啊
### 详细说明:
首先还是老地方:archive_act.php(611行)
```
function respond_action() {
include_once ROOT . '/lib/plugins/pay/' . front::$get['code'] . '.php';
$payclassname = front::$get['code'];
$payobj = new $payclassname();
$uri = $_SERVER["REQUEST_URI"];
$__uriget = strstr($uri, '?');
$__uriget = str_replace('?', '', $__uriget);
$__uriget = explode('&', $__uriget);
$_GET = array();
foreach ($__uriget as $key => $val) {
$tmp = explode('=', $val);
$_GET[$tmp[0]] = $tmp[1];
if(preg_match('/\'|select|union|"/i', $tmp1)){
exit('非法参数');
}
}
file_put_contents('logs11.txt', var_export($_GET,true));
$status = $payobj->respond();
if ($status) {
echo '<script type="text/javascript">alert("' . lang('已经付款,跳转到订单查询') . '")</script>';
front::refresh(url('archive/orders/oid/' . front::get('subject'), true));
} else {
echo '<script type="text/javascript">alert("' . lang('跳转到订单查询') . '")</script>';
front::refresh(url('archive/orders/oid/' . front::get('subject'), true));
}
}
```
$tmp1与$tmp[1]开发人员一时犯糊涂,没看清,导致无效过滤。而且这里get参数进行了重组,从$_SERVER["REQUEST_URI"]分割获取,多此一举,还导致之前的过滤全部无效,这边过滤又失效。
通过front::$get['code'] 可以控制需要加载的pay文件,
再看文件: alipay.php:
```
function respond() {
if (!empty($_POST)) {
foreach($_POST as $key =>$data) {
if(preg_match('/(=|<|>|\')/', $data)){
return false;
}
$_GET[$key] = $data;
}
}
$payment = pay::get_payment($_GET['code']);
$seller_email = rawurldecode($_GET['seller_email']);
$order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
$order_sn = trim($order_sn);
if (!pay::check_money($order_sn,$_GET['total_fee'])) {
return false;
}
if($_GET['trade_status'] == "WAIT_SELLER_SEND_GOODS"||$_GET['trade_status'] == "TRADE_FINISHED" || $_GET['trade_status'] == "TRADE_SUCCESS") {
pay::changeorders($order_sn,$_GET);
return true;
}else {
return false;
}
}
```
控制参数trade_status=WAIT_SELLER_SEND_GOODS, 进入pay::changeorders($order_sn,$_GET);
```
public static function changeorders($id,$orderlog) {
//file_put_contents('logs.txt', $id);
$where=array();
$where['id']=$id;
$where['status']=4;
//$where['orderlog']=serialize($orderlog);
$update=orders::getInstance()->rec_update($where,$id);
if($update<1) {
exit('改变订单状态出错,请联系管理员');
}
}
```
在这里 $id 就是之前$order_sn,可以直接由get参数控制。
进入这个方法:$update=orders::getInstance()->rec_update($where,$id);
```
function rec_update($row,$where) {
$tbname=$this->name;
$sql=$this->sql_update($tbname,$row,$where);
//echo $sql."
";
return $this->query_unbuffered($sql);
}
```
这里程序员又犯糊涂了, rec_update的方法 where变量明显是第二个参数,传入的时候居然$where放到了第一个参数(这个程序员开了吧!),这样$id值就被当做sql语句的条件了。
好吧 开始绕waf:
首先是360的waf: 检测了好多危险函数,更可恶的全局过滤单引号,看到就杀。但是$order_sn 直接被带入到了where后面 根本不需要单引号,不起作用, 在之前的方法中有一个:
$order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
这样利用替换功能,在危险函数中间都插入^, 再把subject设置成^,就可以成功绕过360waf。
接下来在sql语句执行的时候又有一个过滤器:
```
if(preg_match('/(if|select|ascii|from|sleep)/i', $condition)){
//echo $condition;
exit('sql inject');
}
```
由于是update注入,又不能显示错误,sleep被过滤,只能用BENCHMARK。
又过滤了if,只能用or。
get参数又是从querystring中直接过去,空格会被替换成%20,所有只能用/**/替换:
最终的POC:(延时盲注法,稍微改动下)
### 漏洞证明:
暂无评论