About
CAPTCHA (验证码)是一个程序,它可以判断访问网页的用户是人还是机器人,我们在生活中经常需要输入,例如在网页注册表格底部有扭曲文字的彩色图片。许多网站都使用验证码来防止“机器人程序”或通常用来生成垃圾邮件的自动程序的滥用。没有一个计算机程序能够像人类一样读取失真的文本,因此机器人程序无法导航受验证码保护的站点。
验证码通常用于保护敏感功能免受自动机器人的攻击,该功能通常在用户注册和更改、密码更改和发布内容中使用。在本例中,验证码保护用户帐户的更改密码功能,这对 CSRF 攻击和暴力穷举等攻击提供了有限的保护。
注意 DVWA 验证码使用的是 Google 提供 reCAPTCHA 服务,我们暂时使用不了该服务,这个对实验没有任何影响,因为我们根本不会去动验证码而是直接绕过。
Object
你的目标是更改当前用户的密码,因为该站点使用的验证码的校验流程很差劲。
001 Low
这个验证码的问题是它很容易被绕过。开发人员假设所有用户都将通过屏幕1,完成验证码,然后进入下一个屏幕,在那里密码将实际更新。通过将新密码直接提交到更改页面,用户可以绕过验证码系统。
根据源码的逻辑判断修改密码被分成了 2 个步骤。
- 首先后端会调用
recaptcha_check_answer()
检查验证码,如果验证码校验通过,会把step
参数设置为2
; - 当
step
的值为2
时,比较输入的新密码是否相同,如果相同就直接更改数据库;
<?php
if(isset($_POST['Change']) && ($_POST['step'] == '1')){
// Hide the CAPTCHA form
$hide_form = true;
// Get input
$pass_new = $_POST['password_new'];
$pass_conf = $_POST['password_conf'];
// Check CAPTCHA from 3rd party
$resp = recaptcha_check_answer(
$_DVWA['recaptcha_private_key'],
$_POST['g-recaptcha-response']
);
// Did the CAPTCHA fail?
if(!$resp) {
// 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 user
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 {
// Both new passwords do not match.
$html .= "
<pre>Both passwords must match.</pre>";
$hide_form = false;
}
}
}
if(isset($_POST['Change']) && ($_POST['step'] == '2')){
// Hide the CAPTCHA form
$hide_form = true;
// Get input
$pass_new = $_POST['password_new'];
$pass_conf = $_POST['password_conf'];
// Check to see if both password match
if($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)) ? "" : ""));
$pass_new = md5($pass_new);
// Update database
$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>' );
// Feedback for the end user
echo "
<pre>Password Changed.</pre>";
}
else {
// Issue with the passwords matching
echo "
<pre>Passwords did not match.</pre>";
$hide_form = false;
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
我们可以通过控制step
参数直接绕过验证码:
进入数据库检查一下:
002 Medium
开发人员试图在会话中设置状态变量,并跟踪用户在提交数据之前是否成功地完成了验证码。因为状态变量位于客户端,因此攻击者也可以对其进行绕过操作。
if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) {
// Hide the CAPTCHA form
$hide_form = true;
// Get input
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];
// Check to see if they did stage 1
if( !$_POST[ 'passed_captcha' ] ) {
$html .= "
<pre><br />You have not passed the CAPTCHA.</pre>";
$hide_form = false;
return;
}
// Check to see if both password match
if( $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)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update database
$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>' );
// Feedback for the end user
echo "
<pre>Password Changed.</pre>";
}
else {
// Issue with the passwords matching
echo "
<pre>Passwords did not match.</pre>";
$hide_form = false;
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
从源码来看,后端逻辑仅仅多了一个passed_captcha
变量的验证:
// Check to see if they did stage 1
if( !$_POST[ 'passed_captcha' ] ) {
$html .= "
<pre><br />You have not passed the CAPTCHA.</pre>";
$hide_form = false;
return;
}
我们在 Brupsuite 加上即可:
到数据库验证一下:
003 High
有些开发时的废弃代码遗留在页面中,在网页上线后从未删除过。因此可以模拟这些代码中的变量,允许将无效值注入验证码字段。
您需要欺骗您的用户代理以及使用 CAPTCHA 值来绕过检查。
<?php
if( isset( $_POST[ 'Change' ] ) ) {
// Hide the CAPTCHA form
$hide_form = true;
// Get input
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];
// Check CAPTCHA from 3rd party
$resp = recaptcha_check_answer(
$_DVWA[ 'recaptcha_private_key' ],
$_POST['g-recaptcha-response']
);
if (
$resp ||
(
$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
)
){
// CAPTCHA was correct. Do both new passwords match?
if ($pass_new == $pass_conf) {
$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 );
// Update database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";
$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>' );
// Feedback for user
echo "
<pre>Password Changed.</pre>";
} else {
// Ops. Password mismatch
$html .= "
<pre>Both passwords must match.</pre>";
$hide_form = false;
}
} else {
// What happens when the CAPTCHA was entered incorrectly
$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);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
我们在 Firefox 浏览器中按右键,选择 View Page Source 查看网页源代码:
我们根据注释和后端源码,在 Brupsuite 的 Repeater 中添加相关参数:
- 由后端 PHP 源码可知,我们要在 POST data 中加一个参数
g-recaptcha-response
,它的值为hidd3n_valu3
- 我们要把 Request Header 中的 User-Agent 的值改为:
reCAPTCHA
;
去数据库检查一下:
References
- 6 - Insecure Captcha (low/med/high) - Damn Vulnerable Web Application (DVWA) - YouTube
- DVWA 通关指南:Insecure CAPTCHA (不安全的验证流程) - 乌漆WhiteMoon - 博客园 (cnblogs.com)
- 【差评君】验证码不光可以白嫖人们的智力,没准还侵犯你的隐私_哔哩哔哩_bilibili