Altm4nz's blog

WEB划水选手

CVE-2019-6707 CVE-2019-6708分析

前言

phpshe最新1.7版本的后台sql注入分析。

官网:http://www.phpshe.com/

实验环境

CVE-2019-6707

在后台批量上下架操作中存在注入。

我们先根据路由查看源码,抓包拿到操作的路径

1
POST /phpshe/admin.php?mod=product&act=state&state=1&token=c04c2e7a55d84759c0e09eb54b16fccf

定位admin.php:86

1
2
3
4
5
if (in_array("{$mod}.php", pe_dirlist("{$pe['path_root']}module/{$module}/*.php"))) {
include("{$pe['path_root']}module/{$module}/{$mod}.php");
}
pe_result();
?>

此时引入了admin模型中的product.php

在switch中找到state分支

1
2
3
4
5
6
7
8
9
10
case 'state':
pe_token_match();
$product_id = is_array($_p_product_id) ? $_p_product_id : $_g_id;
if ($db->pe_update('product', array('product_id'=>$product_id), array('product_state'=>$_g_state))) {
pe_success("操作成功!");
}
else {
pe_error("操作失败...");
}
break;

如果提交的pproduct_id是一个数组的话会直接进入db->pe_update操作中。没有任何过滤防护措施。

继续跟进db->pe_update,在db.class.php:226

1
2
3
4
5
6
7
8
public function pe_update($table, $where, $set)
{
//处理设置语句
$sqlset = $this->_doset($set);
//处理条件语句
$sqlwhere = $this->_dowhere($where);
return $this->sql_update("update `".dbpre."{$table}` {$sqlset} {$sqlwhere}");
}

pe_update会将参数抛向sql_update,继续跟进一下,

1
2
3
4
5
6
7
8
public function sql_update($sql)
{
if ($this->query($sql) == true) {
$result = $this->affected_rows();//affected_rows()是获取insert/update/delete结果集条数。
return $result == 0 ? true : $result;
}
return 0;
}

此时我们从product_id传上的数据会被直接拼接在语句中执行。

构造一个POST包进行测试,由于没有回显,我们进行延时

成功延时,网站对传入的product_id没有过滤处理造成了时间盲注。

之后继续审计发现

1
2
3
4
5
6
7
8
9
10
11
case 'tuijian':
pe_token_match();
foreach ($_p_product_id as $v) {
$result = $db->pe_update('product', array('product_id'=>$v), array('product_istuijian'=>$_g_tuijian));
}
if ($result) {
pe_success("操作成功!");
}
else {
pe_error("操作失败...");
}

不仅 state 分支,tuijian order move分支均没有对$product_id进行处理。

均存在注入点

CVE-2019-6708

在管理页面订单处

定位到order.php

这里依旧采取了一个语句的拼接

然后sql_where进入pe_selectall方法

继续跟进pe_selectall方法

1
2
3
4
5
6
public function pe_selectall($table, $where = '', $field = '*', $limit_page = array())
{
//处理条件语句
$sqlwhere = $this->_dowhere($where);
return $this->sql_selectall("select {$field} from `".dbpre."{$table}` {$sqlwhere}", $limit_page);
}

看到此方法依然没有对语句的过滤处理就直接执行。

回到后台页面,进入订单页面。

之后进入wsend分支

1
http://127.0.0.1/phpshe/admin.php?mod=order&state=wsend

在state处进行注入

成功延时。可进行时间盲注。

如果wsend分支中有订单存在,同样可以进行bool盲注。

见下面两图

我们对比user.php和admin.php中对订单查询的处理

user.php多了一层对gstate的过滤,admin.php则没有这个操作。

继续审计发现在order_pay页面中,同样没有对gstate进行过滤就直接拼接。

同样存在注入,payload如下

1
http://127.0.0.1/phpshe/admin.php?mod=order_pay&state=wpay%27and%201=1%20--%201