DVWA靶场通关笔记-SQL Injection Blind(SQL盲注 Impossible级别)

发布于:2025-09-03 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、源码分析

1、index.php

2、impossible.php

二、SQL注入防范分析

1、CSRF Token 验证

2、输入数字强制校验

3、PDO 预处理语句


本系列为通过《DVWA靶场通关笔记》的SQL Injection Blind关卡(low,medium,high,impossible共4关)渗透集合,通过对相应关卡源码的代码审计找到讲解渗透原理并进行渗透实践。本文为SQL Injection  Blind impossible关卡的原理分析部分,讲解相对于low、medium和high级别,为何对其进行渗透测试是Impossible的。

一、源码分析

1、index.php

进入DVWA靶场源目录,找到index.php源码。

这段 PHP 代码是 Damn Vulnerable Web Application (DVWA) 中 SQL盲注演示页面的主控制器,根据用户设置的安全级别(低 / 中 / 高 / 安全)加载不同级别安全成都程度的代码,通过表单让用户输入或选择用户 ID,动态生成页面并展示查询结果,同时检测 PHP 不安全配置并提供 SQL 注入学习资源链接,用于演示不同防护等级下的盲注攻击场景及防御方式。

经过注释后的详细代码如下所示。

<?php
// 定义网站根目录路径常量,用于后续文件引用
define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
// 引入DVWA页面基础功能库
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';

// 启动页面,验证用户是否已认证并初始化PHPIDS(入侵检测系统)
dvwaPageStartup( array( 'authenticated', 'phpids' ) );

// 创建新页面实例
$page = dvwaPageNewGrab();
// 设置页面标题
$page[ 'title' ]   = 'Vulnerability: SQL Injection (Blind)' . $page[ 'title_separator' ].$page[ 'title' ];
// 设置页面ID,用于导航和标识
$page[ 'page_id' ] = 'sqli_blind';
// 添加帮助按钮和源代码按钮
$page[ 'help_button' ]   = 'sqli_blind';
$page[ 'source_button' ] = 'sqli_blind';

// 连接数据库
dvwaDatabaseConnect();

// 设置HTTP请求方法(默认为GET)
$method            = 'GET';
// 初始化不同安全级别对应的源文件
$vulnerabilityFile = '';

// 根据安全级别Cookie值选择不同的实现文件
switch( $_COOKIE[ 'security' ] ) {
	case 'low':
		// 低安全级别:存在明显SQL注入安全风险
		$vulnerabilityFile = 'low.php';
		break;
	case 'medium':
		// 中安全级别:部分防御措施,使用POST方法和下拉菜单
		$vulnerabilityFile = 'medium.php';
		$method = 'POST';
		break;
	case 'high':
		// 高安全级别:增强防御,但仍可能存在安全风险
		$vulnerabilityFile = 'high.php';
		break;
	default:
		// 安全模式:使用预处理语句,理论上无SQL注入风险
		$vulnerabilityFile = 'impossible.php';
		break;
}

// 引入选定的实现文件
require_once DVWA_WEB_PAGE_TO_ROOT . "vulnerabilities/sqli_blind/source/{$vulnerabilityFile}";

// 检查PHP配置中的安全风险
$WarningHtml = '';
// 检测Magic Quotes是否启用(已弃用的安全机制)
if( ini_get( 'magic_quotes_gpc' ) == true ) {
	$WarningHtml .= "<div class=\"warning\">The PHP function \"<em>Magic Quotes</em>\" is enabled.</div>";
}
// 检测Safe Mode是否启用(已弃用的安全机制)
if( ini_get( 'safe_mode' ) == true ) {
	$WarningHtml .= "<div class=\"warning\">The PHP function \"<em>Safe mode</em>\" is enabled.</div>";
}

// 构建页面主体内容
$page[ 'body' ] .= "
<div class=\"body_padded\">
	<h1>Vulnerability: SQL Injection (Blind)</h1>

	{$WarningHtml}

	<div class=\"vulnerable_code_area\">";

// 根据不同安全级别显示不同的用户交互界面
if( $vulnerabilityFile == 'high.php' ) {
	// 高级别安全:通过JavaScript弹窗设置用户ID
	$page[ 'body' ] .= "Click <a href=\"#\" onclick=\"javascript:popUp('cookie-input.php');return false;\">here to change your ID</a>.";
}
else {
	// 低、中级别安全:显示表单让用户输入ID
	$page[ 'body' ] .= "
		<form action=\"#\" method=\"{$method}\">
			<p>
				User ID:";
				
	// 中级别安全:使用下拉菜单限制用户选择范围
	if( $vulnerabilityFile == 'medium.php' ) {
		$page[ 'body' ] .= "\n				<select name=\"id\">";
		// 查询用户数量用于生成下拉选项
		$query  = "SELECT COUNT(*) FROM users;";
		$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
		$num    = mysqli_fetch_row( $result )[0];
		$i      = 0;
		// 动态生成下拉选项(1到用户总数)
		while( $i < $num ) { $i++; $page[ 'body' ] .= "<option value=\"{$i}\">{$i}</option>"; }
		$page[ 'body' ] .= "</select>";
	}
	else
		// 低级别安全:允许用户自由输入
		$page[ 'body' ] .= "\n				<input type=\"text\" size=\"15\" name=\"id\">";

	$page[ 'body' ] .= "\n				<input type=\"submit\" name=\"Submit\" value=\"Submit\">
			</p>\n";

	// 安全模式:添加CSRF防护令牌
	if( $vulnerabilityFile == 'impossible.php' )
		$page[ 'body' ] .= "			" . tokenField();

	$page[ 'body' ] .= "
		</form>";
}

// 显示查询结果(由引入include的指定文件生成)
$page[ 'body' ] .= "
		{$html}
	</div>

	<h2>More Information</h2>
	<ul>
		// 提供SQL注入相关的参考链接
		<li>" . dvwaExternalLinkUrlGet( 'http://www.securiteam.com/securityreviews/5DP0N1P76E.html' ) . "</li>
		<li>" . dvwaExternalLinkUrlGet( 'https://en.wikipedia.org/wiki/SQL_injection' ) . "</li>
		<li>" . dvwaExternalLinkUrlGet( 'http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/' ) . "</li>
		<li>" . dvwaExternalLinkUrlGet( 'http://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet' ) . "</li>
		<li>" . dvwaExternalLinkUrlGet( 'https://www.owasp.org/index.php/Blind_SQL_Injection' ) . "</li>
		<li>" . dvwaExternalLinkUrlGet( 'http://bobby-tables.com/' ) . "</li>
	</ul>
</div>\n";

// 输出最终HTML页面
dvwaHtmlEcho( $page );

?>

2、impossible.php

进入DVWA靶场SQL Injection Blind的source源码目录,找到impossible.php源码,分析其为何能让这一关卡名为不可能实现SQL盲注渗透。

打开源码impossible.php,分析可知这段代码实现了用户 ID 查询功能,通过登录校验、CSRF Token、数字输入验证及 PDO 预处理,彻底防范注入与 CSRF 攻击,保障安全,如下所示。

详细注释后的impossible.php源码如下所示。

<?php

// 检查是否通过GET方式提交了表单
if( isset( $_GET[ 'Submit' ] ) ) {
    // 验证CSRF令牌,防止跨站请求伪造攻击
    // 比较用户请求中的令牌与会话存储的令牌是否一致
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // 从GET参数中获取用户输入的ID
    $id = $_GET[ 'id' ];

    // 验证输入是否为数字(防止非数字类型的SQL注入)
    if(is_numeric( $id )) {
        // 准备SQL查询:从users表中查询指定ID的用户信息
        // 使用预处理语句防止SQL注入
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        
        // 将用户输入的ID绑定为整数类型参数
        // 这一步确保即使输入包含非数字字符也会被转换为整数
        $data->bindParam( ':id', $id, PDO::PARAM_INT );
        
        // 执行SQL查询
        $data->execute();

        // 检查查询结果是否为1条记录
        if( $data->rowCount() == 1 ) {
            // 用户存在时的反馈信息
            $html .= '<pre>User ID exists in the database.</pre>';
        }
        else {
            // 用户不存在时返回404状态码
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
            
            // 用户不存在时的反馈信息
            $html .= '<pre>User ID is MISSING from the database.</pre>';
        }
    }
}

// 生成新的CSRF令牌并存储到会话中
generateSessionToken();

?>

二、SQL注入防范分析

impossible.php 是 DVWA 中 SQL 盲注模块的最高防护级别页面,核心功能是在实现 “根据用户 ID 查询姓名” 业务的同时,通过多重机制杜绝安全风险。它先校验用户登录状态与 CSRF Token,确保请求合法;再对输入的 id 进行数字强制校验,过滤非预期输入;最后用 PDO 预处理语句执行数据库查询,彻底隔离用户输入与 SQL 逻辑。此外,页面还限制查询结果仅一条,避免数据泄露,通过 “请求校验 - 输入过滤 - 安全查询” 的闭环,既实现了用户数据查询的基础功能,又完全抵御 SQL 注入、CSRF 等攻击,是 Web 安全中数据库操作的标准安全实现。相对于Low级别、Medium级别、High级别,四个级别的防范措施对比如下所示。

级别 输入方式 防护措施 注入难度 核心特点
Low GET 参数 无任何防护 极低 直接拼接 SQL 语句,可通过页面反馈判断注入结果
Medium POST 参数(下拉) 字符串转义(数字型查询无引号) 转义无法防御数字型注入,可直接构造逻辑表达式
High Cookie 参数 无过滤但有随机延迟 中等 依赖时间盲注,输入位置隐蔽,需通过延迟判断结果
Impossible GET 参数 + Token 数字验证 + PDO 预处理 + CSRF 防护 彻底隔离用户输入与 SQL 逻辑,无注入风险,完全防御各类攻击

impossible.php 并非仅实现 “查询用户数据” 的简单功能,而是通过 三重防护机制 构建了完整的安全闭环,彻底杜绝 SQL 注入及相关攻击:

防护机制 作用场景 核心目标
CSRF Token 验证 请求合法性校验 防止第三方伪造用户请求
输入数字强制校验 输入源过滤 阻断非预期格式的恶意输入
PDO 预处理语句 数据库查询安全 彻底隔离用户输入与 SQL 逻辑

1、CSRF Token 验证

CSRF 攻击利用用户已登录的会话伪造恶意请求,impossible.php 通过 Token 验证 确保请求由用户主动发起,而非第三方伪造。

// 5. 若用户点击「Submit」提交请求,先验证 CSRF Token
if (isset($_GET[ 'Submit' ])) {
    // 核心:验证请求中的 Token 与会话中存储的 Token 是否一致
    checkToken($_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php');

    // 后续业务逻辑...
}

// 6. 在表单中嵌入 CSRF Token 隐藏字段(generateSessionToken() 已生成会话 Token)
$page[ 'body' ] .= '
            ' . tokenField() . '  <!-- 生成 <input type="hidden" name="user_token" value="随机字符串"> -->
        </form>
';
  • generateSessionToken():在用户会话($_SESSION['session_token'])中生成一个随机、唯一的 Token(如 a3f2d4e5b1),每次页面刷新或请求后可重新生成(短期有效);
  • tokenField():将会话中的 Token 嵌入表单隐藏字段,随用户请求一起提交;
  • checkToken(...):对比请求参数 user_token 与会话 session_token,若不一致则判定为恶意请求,跳转至首页并阻断后续操作。

2、输入数字强制校验

即使通过 CSRF 验证,代码仍对用户输入的 id 参数进行 强制数字校验,从源头阻断非预期格式的输入(如 SQL 注入片段)。

if (isset($_GET[ 'Submit' ])) {
    checkToken(...); // 先过 Token 验证

    // 7. 获取用户输入的 id 参数,并进行数字类型强制校验
    $id = $_GET[ 'id' ];

    // 核心:仅允许数字输入(整数/浮点数),非数字直接返回错误提示
    if (is_numeric($id)) {
        // 合法输入:进入数据库查询流程
    } else {
        // 非法输入:渲染错误提示,不执行任何数据库操作
        $page[ 'body' ] .= '<pre>ERROR: Invalid ID. Please enter a numeric value.</pre>';
    }
}
  • 若用户输入恶意字符串(如 1' OR '1'='1UNION SELECT...),is_numeric($id) 会判定为 false,直接阻断后续查询,避免非法输入进入数据库层;
  • 仅允许 123.5 等数字格式,符合「用户 ID 为数字」的业务逻辑,实现 “输入格式与业务逻辑匹配” 的安全原则。

3、PDO 预处理语句

对于合法的数字输入,代码使用 PDO 预处理语句(参数化查询) 执行数据库查询,这是杜绝 SQL 注入的核心机制。

if (is_numeric($id)) {
    // 8. 初始化 PDO 数据库连接(DVWA 核心配置中已确保 PDO 连接安全)
    $pdo = dvwaDbConnect();

    // 9. 准备预处理 SQL:使用占位符 (:id) 代替直接拼接用户输入
    $data = $pdo->prepare('SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;');

    // 10. 绑定参数:明确指定参数类型为整数(PDO::PARAM_INT),强制类型匹配
    $data->bindParam(':id', $id, PDO::PARAM_INT);

    // 11. 执行查询(用户输入仅作为参数传递,不参与 SQL 语法解析)
    $data->execute();

    // 12. 获取查询结果(仅返回一条数据,避免批量泄露)
    $row = $data->fetch();

    // 13. 渲染查询结果:有数据则显示用户名,无数据则提示“未找到”
    if ($row) {
        $page[ 'body' ] .= "<pre>ID: {$id}<br>First name: {$row[ 'first_name' ]}<br>Surname: {$row[ 'last_name' ]}</pre>";
    } else {
        $page[ 'body' ] .= '<pre>User ID not found in the database.</pre>';
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到