DVWA Medium Blind SQL Injection安全测试与防护分析

注意:有关Blind SQL Injection的基础知识与原理已在上篇文章指出,此处不再解释。
可参考该文章https://www.cnblogs.com/lky-sec/articles/21412117
本文仅用于网络安全学习与防御研究。所有测试均在本人本地部署、合法授权的 DVWA 靶场中进行,禁止将相关方法用于任何未授权系统

中等难度SQL盲注

一 测试范围与环境

本次测试目标为本地DVWA靶场中的Blind SQL Injection模块

项目 配置
测试环境 本地 DVWA 靶场
安全等级 Medium
测试系统 Kali Linux
测试工具 Firefox、Burp Suite、sqlmap
测试方式 黑盒测试、源码审计、自动化辅助验证
授权情况 本人本地靶场,合法授权

二 页面与请求分析

1.设置难度等级

进入 DVWA Security,将安全等级设置为 Medium,随后进入 SQL Injection (Blind) 模块。
image

2.观察模块正常功能

可从页面看到,和SQL Injection相同,都是通过下拉框的形式进行选择User ID进行提交
image
但是返回信息与SQL Injection不同,SQL盲注仅返回是否存在的信息,而具体内容并不会进行返回。
image

3.使用Burp Suite进行抓包分析

开启Burp Suite Proxy模块进行拦截,提交一次正常请求,观察HTTP请求方式以及参数。
image
由抓包结果可知,该模块使用 POST 请求向:/vulnerabilities/sqli_blind/提交数据,请求体为:id=1&Submit=Submit。

三 手工黑盒测试

1.使用Repeater模块

右键将正常请求发送到Burp Suite Repeater。
image

2.判断注入点与类型

修改拦截请求中的id参数,输入1' 返回用户不存在。
image
输入1 and 1=1# 返回用户存在。
image
输入1 and 1=2# 返回用户不存在。
image
结合三者输入与对应输出结果,可以判断存在注入点且注入类型为数字型布尔盲注

3.判断数据库名长度

利用length()函数逐步判断数据库名长度,输入1 and length(database())=数字# 发现数字为4时,返回用户存在,可判断出数据库名长度为4。
image

4.逐字符判断数据库名

使用ASCII(SUBSTRING(字符串,起始位置,截取长度))判断字符,此处我们使用Intruder模块。ASCII范围从32到126,模式为Sniper attack,类型为Numbers。
image
通过返回长度以及Response里的返回结果可知,第一个字符对应ASCII码为100,即该字符为d。
image
不断更改起始位置,最后重复上述过程,可知数据库名为dvwa。

5.判断当前数据库表数量

输入1 and (select count(table_name) from information_schema.tables where table_schema=database())=数字# 使用Intruder模块,最后判断出表数量为2。
image

6.判断第一张表名字长度

输入1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=数字# 得出表长度为9。
image

7.逐字符判断表名

输入1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),起始位置,1))=数字# 得出表名为guestbook。
后续判断表字段数量,字段名省略,详细步骤可见引言链接文章。

四 sqlmap自动化验证

1.保存完整的HTTP请求

准备正常请求,页面重新提交用户,将拦截的请求制作成文件,打开终端,输入nano request.txt将刚才复制内容粘贴进去,保存。
image

2.使用sqlmap探测

输入sqlmap -r request.txt -p id 存在布尔盲注和时间盲注。
image

3.获取当前数据库

输入sqlmap -r request.txt -p id --current-db 验证数据库名确为dvwa。
image

4.获取表名

输入sqlmap -r request.txt -p id -D dvwa --tables 验证第一个表名确为guestbook。
image

5.获取guestbook表字段名

输入sqlmap -r request.txt -p id -D dvwa -T guestbook --columns 得出字段名comment、name和comment_id。
image

五 代码审计

1.点击view source查看源码

<?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();
}

?> 

2.源码分析

Medium等级首先使用mysqli_real_escape_string()对用户提交的id参数进行特殊字符转义,相比Low增加了一定防护。
但程序随后仍将$id直接拼接进SQL语句:

$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

由于id处于未加引号的数字型SQL上下文,因此仍可通过AND等逻辑条件改变原SQL语句结构。
同时,程序不会直接回显查询到的数据,而是通过mysqli_num_rows()判断是否存在结果,并返回User ID exists或User ID is MISSING。因此可以根据页面真假响应差异逐步推断数据库信息,形成布尔型盲注。
漏洞的根本原因仍然是用户输入直接参与SQL语句拼接,而没有使用参数化查询。

3.修复方案

1)对 id 参数进行严格的整数类型验证
2)使用参数化查询或预编译语句
3)对异常响应进行统一处理
4)使用数据库最小权限原则
5)增加异常请求日志与安全监控

六 Low与Medium差异

对比项 Low Medium
请求方式 GET POST
参数来源 $_GET['id'] $_POST['id']
页面输入 文本框,可直接输入 下拉框,但请求仍可被修改
SQL 上下文 单引号包裹的字符串型 未加引号的数字型
输入处理 无任何过滤 使用 mysqli_real_escape_string()
单引号影响 可破坏原有字符串边界 会被转义,但并非漏洞关键
SQL 拼接 直接拼接 仍然直接拼接
SQL 注入 存在 仍然存在
错误信息 使用 @ 抑制 使用 @ 抑制
条件为假时 返回 404 状态及“MISSING”提示 仅返回“MISSING”提示
盲注判断依据 响应内容和 HTTP 状态 响应内容
根本修复 参数化查询 参数化查询
posted @ 2026-07-25 16:08  网安新手  阅读(21)  评论(0)    收藏  举报