SQL盲注与⼀般注⼊的区别在于⼀般的注⼊攻击者可以直接从⻚⾯上看到注⼊语句的执⾏结果,⽽盲注
时攻击者通常是⽆法从显示 ⻚⾯上获取执⾏的结果,甚⾄连注⼊语句是否执⾏都⽆法得知。
难度(low)
审计代码:
看代码可以发现返回的结果是两种,⼀个是User ID exists in the database,⼀种是User ID is
MISSING from the database
开始操作:
输⼊1显示存在
输⼊1 and 1 =1 和1 and 1 = 2均显示存在
输⼊1’ and 1 =1#显示存在(这三种都显示存在)
存在字符型的盲注
然后猜解数据库名:输⼊1’ and length(database())=4 #,显示存在:说明⻓度为4
难度(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;";
try {
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
} catch (Exception $e) {
print "There was an error.";
exit;
}
$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>';
}
}
?>
可以看到,Medium级别的代码利⽤mysql_real_escape_string函数对特殊符号
\x00,\n,\r,,’
,\x1a进⾏转义,同时前端⻚⾯设置了下拉选择表单,希望以此来控制⽤户的输⼊。
可以通过过抓包来实现sql盲注
难度(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;";
try {
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors
} catch (Exception $e) {
$result = false;
}
$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,希望以此控
制只输出⼀个结果。
漏洞利⽤:
虽然添加了LIMIT 1,但是我们可以通过#将其注释掉。但由于服务器端执⾏sleep函数,会使得基于时间
盲注的准确性受到影响,这⾥我们只演示基于布尔的盲注:
抓包将参数id改为1’ and length(database())=4 #,显示存在,说明数据库名的⻓度为4个字
符;