通过DVWA学习SQL盲注

时间:2021-01-21 21:47:45

本文是本人学习过程中做的笔记,不足之处很多,望各位大牛指导一下~

SQL盲注中,并不会像之前的SQL注入一样显示数据库内建的报错信息,而是显示通用的错误提示,也就是说SQL注入将不能依靠错误信息来判断注入语句的执行结果。但是,我们可以利用其中基于逻辑真假的不同结果来进行判断从而确定是否存在SQL注入漏洞。

 

判断盲注的常见用法:

1’ and 1=1 #
1’ and 1=2 #

判断这两种不同的输入是否有不一样的显示,如果一个正常一个通用的错误提示或者啥也不显示,则几乎可以确定是含有SQL注入漏洞的。

 

Low级:

源代码:

<?php     

if (isset($_GET['Submit'])) {

// Retrieve data

$id = $_GET['id'];

$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid); // Removed 'or die' to suppres mysql errors

$num = @mysql_numrows($result); // The '@' character suppresses errors making the injection 'blind'

$i = 0;

while ($i < $num) {

$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
?>

先常规地进行判断,输入单引号等不返回任何信息,输入上述说的两个语句只有第一个返回信息,第二个没有返回任何信息:

 通过DVWA学习SQL盲注

可以推测存在SQL注入漏洞。

 

竟然如此,我们就直接输入注入语句就成功利用了该漏洞:

 通过DVWA学习SQL盲注

 

然而,还想进一步挖掘更多的信息,需要进行逐层深入查询的操作。先是输入order by来确定有多少个字段,输入为2时还有返回结果但到3时并没有任何返回,因而可确定含有两个字段:

 通过DVWA学习SQL盲注

需要注意的一点是,在输入的内容中,id必须要有一个值来保证其是正确的从而才能确保后面的order by语句能正常执行并进行相应的显示,否则前面语句都不正确的话后面就不会显示任何信息。

 

接着就是union语句:

 通过DVWA学习SQL盲注

 

没啥问题,进一步深入,使用组合的查询语句:

 通过DVWA学习SQL盲注

 

此外,盲注猜测数据库信息有另一种方法,使用按位与的逻辑运算来进行判断,以猜测数据库名为例,将与号&后面的数字从1286432168421来逐个进行与运算,返回有结果的都表明该位为1,没有返回的都表明该位为0,语句为:

1' and ORD(MID(database(),1,1))&1>0 #

其中ORD函数负责将相应的字符转换成ASCII码,MID函数实现在第一个参数的字符串中从第二个参数指定的位置开始(1为起始位置)截取数量为第三个参数的数值的字符串。后面的&1>0表明在该字符的ASCII码的二进制表示中最低位是否为1,若为1>0为真,否则为假。如果&的是2或者4,则相应的就是比较二进制从低到高的第二位或第三位,128对应的就是最高位也就是第八位。

返回结果的都列在如下:

 通过DVWA学习SQL盲注通过DVWA学习SQL盲注通过DVWA学习SQL盲注

 

则根据返回的结果可推断数据库名第一个字符的二进制表示为:01100100,换算成十进制为:100,则ASCII码为100对应的字符为:d

同理,修改ORD函数中的第二个参数的值使其递增,从而遍历整个数据库名的各个位置再进行与运算来判断,最后可以得出数据库名为:dvwa。除此之外,可将database函数改为查询版本信息的VERSION函数、查询当前用户的CURRENT_USER函数实现查询。

 

知道数据库名为dvwa之后,继续深入查询:

 通过DVWA学习SQL盲注

 

列举出所有的表名之后,对users表进行进一步的查询:

 通过DVWA学习SQL盲注

 

知道users表的所有列之后,用以下组合语句进行一次性地列举所有用户信息:

 通过DVWA学习SQL盲注

当然显示的位置不足,可以采用limit来逐个遍历的方法更为清晰。

 

最后,查看一下源代码发现,有两个地方和SQL注入low级别的源代码不同:第一个是在调用mysql_query函数后移除了显示错误信息的die函数;第二个是在调用mysql_numrows函数的前面加一个@号,目的是为了将错误信息给强制去掉不让其显示出来。然而缺点还是和SQL注入的一样没有对输入进行任何过滤。

 

Low级别的利用大致如此,其实发现盲注和平时的注入并没有太大的区别,主要是看两种不同的返回结果(一个是正常查询的结果,另一个是通用的错误提示)来判断当前的条件是否成立进而确定相应的信息,当然前提是前面的语句必须让其为真。

 

 

Medium级:

 源代码:

<?php 

if (isset($_GET['Submit'])) {

// Retrieve data

$id = $_GET['id'];
$id = mysql_real_escape_string($id);

$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
$result = mysql_query($getid); // Removed 'or die' to suppres mysql errors

$num = @mysql_numrows($result); // The '@' character suppresses errors making the injection 'blind'

$i=0;

while ($i < $num) {

$first=mysql_result($result,$i,"first_name");
$last=mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
?>

SQL注入一样,加入mysql_real_escape_string()函数对输入的字符进行过滤,但是其利用点还是一样,没有在SELECT语句中对id变量用符号括起来而是直接引用,这样连闭合的字符都不用输入就可以直接去利用该漏洞了。

 

High级:

 源代码:

<?php     

if(isset($_GET['Submit'])){

// Retrieve data

$id = $_GET['id'];
$id = stripslashes($id);
$id = mysql_real_escape_string($id);

if (is_numeric($id)) {

$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid); // Removed 'or die' to suppres mysql errors

$num = @mysql_numrows($result); // The '@' character suppresses errors making the injection 'blind'

$i=0;

while ($i < $num) {

$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");

echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';

$i++;
}
}
}
?>

SQL注入一样,加了is_numeric()函数判断输入的变量是否为数值类型,有效地防范了SQL注入以及盲注漏洞。

 

无权访问information_schema和拒绝执行unionorder by语句:

在这里先和SQL注入无权访问的情况进行比较,在SQL注入中使用的是is null而不是is not null,判断是基于存在时什么都没有返回而不存在时则会出现报错信息,即使基于报错信息判断的;而在SQL盲注中使用的是is not null,因为盲注中不会返回任何报错信息,只能这样通过猜测该项不为空,若存在则整条语句为真则返回正常的信息进而来判断。当然在SQL注入中也可以使用is null来判断,原理一样,当存在时返回正常内容,当不存在时则返回错误信息。

猜测列名:

1' and column is not null #

其中column为猜测的列名

猜测当前表的表名:

1' and table.user is not null #

其中table为猜测的当前表表名

猜测其他表的表名:

1' and (select count(*) from table)>0 #

其中table为猜测的其他表表名

猜测对应关系:

1' and users.user is not null #

其中users和user为猜测的两者的对应关系

猜测用户名:

1' and user='admin
1' and user like '%a%

其中admin和包含字符a的字符串为猜测的用户名

猜测密码:

2' or user='admin' and password='286755fad04869ca523320acce0dc6a4

其中password中的MD5值为猜测的密码


同样为了方便可用Burpsuite进行猜解,和SQL注入的使用一样就不再多说了。