当前位置: 首页 > news >正文

DVWA靶场保姆级通关教程--06不安全验证机制

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 目录

    文章目录

    前言

    原理详解

    1. 前后端验证逻辑不一致

    2. 验证码值保存在客户端

    3. 验证码可预测或重复

    4. 验证码验证与逻辑解耦

    一、处理关卡报错

    二、low级别源码分析

    三、medium级别源码分析

    三、high级别源码分析

    安全点说明:

    四、impossible级别源码分析

    总结:Impossible级别的安全设计


前言

提示:这里可以添加本文要记录的大概内容:

Insecure CAPTCHA 模块的含义是:CAPTCHA 的验证机制存在安全漏洞,可以被攻击者轻松绕过,从而进行自动化攻击(例如暴力破解登录密码)。

原理详解

在 DVWA 的这个模块中,存在以下常见的实现漏洞:

1. 前后端验证逻辑不一致
  • 前端页面展示了验证码图片(如一个 5 位数字),用户输入后提交表单。

  • 但后端未正确校验或校验方式过于简单,甚至根本未检查验证码输入是否正确。

  • 攻击者可以直接跳过验证码输入字段,构造 POST 请求攻击。

2. 验证码值保存在客户端
  • 有的实现会把验证码值直接保存在 cookie、hidden field 或 session 中,攻击者可以通过抓包或调试 JS 获取验证码答案。

  • 甚至可以通过查看源码或 response 来获取验证码值。

3. 验证码可预测或重复
  • 如果验证码使用的是伪随机函数(如rand())生成,而种子固定,攻击者可预测其值。

  • 或者验证码值在每次访问时不更新(比如 10 分钟内都是同一个),那么攻击者只需识别一次,就可反复使用。

4. 验证码验证与逻辑解耦
  • 某些实现中,验证码验证逻辑在前端处理,而没有在服务端做真正校验,攻击者可直接跳过验证逻辑。


提示:以下是本篇文章正文内容,下面案例可供参考

一、处理关卡报错

$_DVWA[ 'recaptcha_public_key' ]  = '';

$_DVWA[ 'recaptcha_private_key' ] = '';


改为:

$_DVWA[ 'recaptcha_public_key' ] = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg';

$_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ';

重启或者退出dvwa重新登录,修改成功

二、low级别源码分析

<?php// 如果提交了表单,并且 step 是 1(第一步:验证码验证)
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) {$hide_form = true; // 隐藏第一步的 CAPTCHA 表单// 获取用户输入的新密码和确认密码$pass_new  = $_POST[ 'password_new' ]; // 新密码$pass_conf = $_POST[ 'password_conf' ]; // 确认密码// 验证 CAPTCHA,使用第三方(Google reCAPTCHA)$resp = recaptcha_check_answer($_DVWA[ 'recaptcha_private_key'], // 使用私钥$_POST['g-recaptcha-response']    // 用户填写的 CAPTCHA 响应);// 如果 CAPTCHA 验证失败if( !$resp ) {$html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; // 提示 CAPTCHA 错误$hide_form = false; // 显示 CAPTCHA 表单return; // 停止执行后续逻辑}else {// CAPTCHA 验证通过,检查两个密码是否一致if( $pass_new == $pass_conf ) {// 如果一致,显示第二步确认提交表单echo "<pre><br />You passed the CAPTCHA! Click the button to confirm your changes.<br /></pre><form action=\"#\" method=\"POST\"><input type=\"hidden\" name=\"step\" value=\"2\" /> <!-- 隐藏字段,表示进入第二步 --><input type=\"hidden\" name=\"password_new\" value=\"{$pass_new}\" /> <!-- 隐藏字段,带着密码 --><input type=\"hidden\" name=\"password_conf\" value=\"{$pass_conf}\" /> <!-- 隐藏字段,带着密码确认 --><input type=\"submit\" name=\"Change\" value=\"Change\" /> <!-- 提交按钮 --></form>";}else {$html     .= "<pre>Both passwords must match.</pre>"; // 密码不一致,提示错误$hide_form = false; // 显示 CAPTCHA 表单}}
}// 如果提交了表单,并且 step 是 2(第二步:真正修改密码)
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {$hide_form = true; // 隐藏 CAPTCHA 表单// 获取用户输入的新密码和确认密码$pass_new  = $_POST[ 'password_new' ];$pass_conf = $_POST[ 'password_conf' ];// 再次检查密码是否一致if( $pass_new == $pass_conf ) {// 对密码进行 SQL 注入防护$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new ); // 对密码进行 md5 哈希// 构造 SQL 更新语句,更新当前用户的密码$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );echo "<pre>Password Changed.</pre>"; // 提示密码修改成功}else {echo "<pre>Passwords did not match.</pre>"; // 提示密码不一致$hide_form = false; // 显示表单}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); // 关闭数据库连接
}?>

核心安全问题(即 “Insecure CAPTCHA” 原理) 

<input type="hidden" name="step" value="2" />

攻击者可以跳过第一个 CAPTCHA 表单,直接构造第二步请求(step=2)提交新密码,因为 第二步中没有再次验证 CAPTCHA 是否通过,也没有其他身份校验机制。

所以这里输入密码,用bp抓包,将step的值从1直接改为2,即跳过安全验证,直接改密

抓包结果如下:

在返回的数据包中发现修改成功

三、medium级别源码分析

<?phpif( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) { // 如果提交了表单并且当前步骤为第1步// Hide the CAPTCHA form$hide_form = true; // 隐藏 CAPTCHA 表单// Get input$pass_new  = $_POST[ 'password_new' ]; // 获取新密码$pass_conf = $_POST[ 'password_conf' ]; // 获取确认密码// Check CAPTCHA from 3rd party$resp = recaptcha_check_answer( // 调用 Google reCAPTCHA 接口进行验证$_DVWA[ 'recaptcha_private_key' ], // 使用 DVWA 中设置的私钥$_POST['g-recaptcha-response'] // 获取表单中用户填写的 CAPTCHA 响应);// Did the CAPTCHA fail?if( !$resp ) { // 如果 CAPTCHA 验证失败// What happens when the CAPTCHA was entered incorrectly$html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>"; // 显示错误信息$hide_form = false; // 显示表单return; // 停止执行}else {// CAPTCHA was correct. Do both new passwords match?if( $pass_new == $pass_conf ) { // 如果两个密码一致// Show next stage for the userecho "<pre><br />You passed the CAPTCHA! Click the button to confirm your changes.<br /></pre> // CAPTCHA 验证成功提示<form action=\"#\" method=\"POST\"> // 第二步的确认修改表单<input type=\"hidden\" name=\"step\" value=\"2\" /> // 标记为第2步<input type=\"hidden\" name=\"password_new\" value=\"{$pass_new}\" /> // 隐藏域保存新密码<input type=\"hidden\" name=\"password_conf\" value=\"{$pass_conf}\" /> // 隐藏域保存确认密码<input type=\"hidden\" name=\"passed_captcha\" value=\"true\" /> // 隐藏域标记 CAPTCHA 已通过<input type=\"submit\" name=\"Change\" value=\"Change\" /> // 提交按钮</form>";}else {// Both new passwords do not match.$html     .= "<pre>Both passwords must match.</pre>"; // 显示密码不一致的错误信息$hide_form = false; // 显示表单}}
}if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // 如果当前为第2步// Hide the CAPTCHA form$hide_form = true; // 隐藏 CAPTCHA 表单// Get input$pass_new  = $_POST[ 'password_new' ]; // 获取新密码$pass_conf = $_POST[ 'password_conf' ]; // 获取确认密码// Check to see if they did stage 1if( !$_POST[ 'passed_captcha' ] ) { // 如果未通过 CAPTCHA 验证(没有设置 passed_captcha 字段)$html     .= "<pre><br />You have not passed the CAPTCHA.</pre>"; // 提示用户未通过 CAPTCHA$hide_form = false; // 显示表单return; // 停止执行}// Check to see if both password matchif( $pass_new == $pass_conf ) { // 如果两个密码一致// They do!$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // 对密码进行转义防止 SQL 注入$pass_new = md5( $pass_new ); // 将密码使用 md5 加密// Update database$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; // 构造 SQL 更新语句$result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // 执行 SQL 并在出错时输出错误信息// Feedback for the end userecho "<pre>Password Changed.</pre>"; // 提示密码修改成功}else {// Issue with the passwords matchingecho "<pre>Passwords did not match.</pre>"; // 提示密码不一致$hide_form = false; // 显示表单}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); // 关闭数据库连接
}?>

medium 级别与 low 级别的区别是:

  • 增加了对 CAPTCHA 验证通过的状态存储:通过隐藏字段 passed_captcha 传递。

  • 在第二步执行修改前,检查 passed_captcha 是否存在,以避免跳过 CAPTCHA 验证。

不过这仍然可以被伪造表单绕过(如构造 POST 请求带 passed_captcha=true

这里的话还是可以通过抓包添加这个字段来提交,显示修改成功,这里的修改也是在repeater模块中修改,然后点击send

三、high级别源码分析

 查看源码,服务器的验证逻辑是当$resptrue,并且参数recaptcha_response_field等于hidd3n_valu3(或者http包头的User-Agent参数等于reCAPTCHA)时,就认为验证码输入正确,反之错误。

<?php// 如果用户提交了 "Change" 表单
if( isset( $_POST[ 'Change' ] ) ) {// 隐藏 CAPTCHA 表单$hide_form = true;// 获取用户输入的新密码和确认密码$pass_new  = $_POST[ 'password_new' ]; // 新密码$pass_conf = $_POST[ 'password_conf' ]; // 确认密码// 调用 reCAPTCHA 第三方函数验证用户输入$resp = recaptcha_check_answer($_DVWA[ 'recaptcha_private_key' ], // 使用私钥$_POST['g-recaptcha-response']     // 用户输入的验证码响应);// 如果验证码验证成功,或使用了一个特定的隐藏值(后门验证码)并且UA是'reCAPTCHA'(模拟机器人)if ($resp || ($_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3' // 这是个后门值,意味着攻击者可以绕过验证码&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'     // 并且 User-Agent 被伪装成 reCAPTCHA)){// 如果两个密码相同if ($pass_new == $pass_conf) {// 使用 mysqli_real_escape_string 防止 SQL 注入(前提是数据库连接存在)$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new ); // 使用 MD5 对密码进行哈希(不安全)// 构造 SQL 更新语句,修改当前登录用户的密码$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";// 执行 SQL 语句,如果失败则输出错误$result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );// 提示用户密码修改成功echo "<pre>Password Changed.</pre>";} else {// 如果两个密码不一致,提示错误$html     .= "<pre>Both passwords must match.</pre>";$hide_form = false;}} else {// 如果验证码验证失败,提示错误$html     .= "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";$hide_form = false;return;}// 关闭数据库连接((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}// 生成 Anti-CSRF token,防止 CSRF 攻击
generateSessionToken();?>

安全点说明:

  • 高等级增加了限制:必须通过 reCAPTCHA 验证或匹配特定值且 User-Agent 正确(用于防御简单的自动脚本)。

  • 后门逻辑存在:验证码绕过后门 'hidd3n_valu3' + 'reCAPTCHA' 的组合,可以被攻击者利用。

  • 仍使用 MD5 加密密码:MD5 不安全,容易被破解,建议使用 bcrypt 或 Argon2。

  • 数据库使用了过时接口:代码基于 mysqli_* 和老旧错误处理方式,存在维护问题。

  • 由于$resp参数我们无法控制,所以重心放在参数recaptcha_response_field、User-Agent上。
  • 修改参数,点击Send:显示修改成功。

 以上绕过方式确实是看了 DVWA 的源码才找到的“后门绕过条件”,这在真实世界中属于**“白盒渗透”,而大多数实际攻击是“黑盒测试”**,不能看到源码,就要靠逻辑推理 + 测试来逐步发现漏洞。在真实的 Web 安全测试中,验证码绕过是一项难度较高的工作,需要你善于分析流程、测试请求逻辑、借助自动化工具,不能依赖看源码“取巧”,而是要推演漏洞逻辑 + 试错测试

四、impossible级别源码分析

<?php// 如果点击了“Change”按钮(提交了修改密码的表单)
if (isset($_POST['Change'])) {// 1️⃣ 校验 CSRF Token,防止跨站请求伪造checkToken($_REQUEST['user_token'], $_SESSION['session_token'], 'index.php');// 表示验证码验证通过后不再显示表单(控制显示)$hide_form = true;// 2️⃣ 获取用户输入的新密码,并进行处理(去转义 + SQL安全 + 哈希)$pass_new = $_POST['password_new'];$pass_new = stripslashes($pass_new); // 去除反斜杠$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new) : trigger_error(...)); // SQL转义,防止注入$pass_new = md5($pass_new); // 对新密码进行 MD5 加密// 3️⃣ 同样处理确认密码字段$pass_conf = $_POST['password_conf'];$pass_conf = stripslashes($pass_conf);$pass_conf = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_conf) : trigger_error(...));$pass_conf = md5($pass_conf);// 4️⃣ 当前密码校验处理(确保用户是本人)$pass_curr = $_POST['password_current'];$pass_curr = stripslashes($pass_curr);$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr) : trigger_error(...));$pass_curr = md5($pass_curr);// 5️⃣ 验证 Google reCAPTCHA,确保用户是真人操作$resp = recaptcha_check_answer($_DVWA['recaptcha_private_key'],        // 使用 DVWA 配置中的私钥$_POST['g-recaptcha-response']          // 获取用户提交的验证码响应 token);// 6️⃣ 判断验证码是否验证通过if (!$resp) {echo "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";$hide_form = false; // 显示表单重新输入} else {// 7️⃣ 验证当前密码是否正确(从数据库中查找该用户+密码是否存在)$data = $db->prepare('SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;');$data->bindParam(':user', dvwaCurrentUser(), PDO::PARAM_STR);$data->bindParam(':password', $pass_curr, PDO::PARAM_STR);$data->execute();// 8️⃣ 如果新密码一致,且当前密码正确,就更新数据库密码if (($pass_new == $pass_conf) && ($data->rowCount() == 1)) {$data = $db->prepare('UPDATE users SET password = (:password) WHERE user = (:user);');$data->bindParam(':password', $pass_new, PDO::PARAM_STR);$data->bindParam(':user', dvwaCurrentUser(), PDO::PARAM_STR);$data->execute();echo "<pre>Password Changed.</pre>"; // 成功提示} else {echo "<pre>Either your current password is incorrect or the new passwords did not match.<br />Please try again.</pre>";$hide_form = false;}}
}// 9️⃣ 页面底部重新生成 Anti-CSRF token,用于下次提交
generateSessionToken();?>

总结:Impossible级别的安全设计

安全点说明
✅ CSRF 防护使用 user_token 和 session 进行校验
✅ SQL 注入防护使用 PDO 预处理语句,避免 SQL 注入
✅ XSS 防护未涉及 DOM 输出,但数据未直接回显,隐患小
✅ CAPTCHA 验证使用 Google reCAPTCHA,服务端校验是否通过
✅ 身份验证验证当前密码是否正确,防止他人盗改密码
✅ 表单逻辑清晰分支判断详细,失败时不暴露具体原因(只有简单提示)

相关文章:

  • 安全核查基线-1.LPD服务
  • 构筑芯片行业的“安全硅甲”
  • 教育+AI:个性化学习能否颠覆传统课堂?
  • 游戏引擎学习第266天:添加顶部时钟概览视图。
  • CSS实现图片垂直居中方法
  • 利用GPT实现油猴脚本—网页滚动(优化版)
  • CSS flex:1
  • C23 与 MISRA C:2025:嵌入式 C 语言的进化之路
  • 计算机视觉与深度学习 | 视觉+激光雷达+惯惯性SLAM算法汇总(原理,公式,代码)
  • JDK8 HashMap红黑树退化为链表的机制解析
  • 为人类文明建一座“永不遗忘”的数字博物馆:Funes 技术解析
  • 【计算机视觉】Car-Plate-Detection-OpenCV-TesseractOCR:车牌检测与识别
  • 在 MyBatis 中实现控制台输出 SQL 参数
  • java学习笔记
  • AI客服问答自动生成文章(基于deepseek实现)
  • ABB电机保护单元通过Profibus DP主站转Modbus TCP网关实现上位机通讯
  • Vulnhub Lazysysadmin靶机攻击实战(一)
  • 硬链接与软连接
  • 如何从极狐GitLab 容器镜像库中删除容器镜像?
  • Bitcoin跨链协议Clementine的技术解析:重构DeFi生态的信任边界
  • 4月证券私募产品备案量创23个月新高,股票策略占比超六成
  • 春秋航空:如果供应链持续改善、油价回落到合理水平,公司补充运力的需求将会增长
  • 美联储如期按兵不动,强调“失业率和通胀上升的风险均已上升”(声明全文)
  • 上海明后天将迎强风大雨,陆地最大阵风7~9级
  • 印巴冲突升级,巴防长称已击落5架印度战机
  • A股三大股指集体高开大涨超1%,券商、房地产涨幅居前