DVWA靶场——Insecure CAPTCHA(不安全的验证码) Insecure CAPTCHA不安全的验证码CAPTCHA全称为Completely Automated Public Turing Test to Tell Computers and Humans Apart中文名字是全自动区分计算机和人类的图灵测试。关于这一项其实是在验证流程出现了逻辑漏洞。用户首先访问网页触发页面的验证码的js模块向谷歌服务器发起请求谷歌服务器将验证码发给用户。用户输入验证码发送数据回去这里发给的是访问网站的服务器网站的服务器拿到验证码后再去访问谷歌的服务器谷歌的服务器会判断验证码是否正确再将结果返回给网站服务器。low因为其验证会发送至谷歌服务器需要科学上网这里不太好做演示所以我们直接通过源代码审计找出其漏洞?php //第一阶段验证身份验证阶段为step 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 //使用第三方进行身份验证 //recaptcha_check_answer($privkey,$remoteip, $challenge,$response) 参数$privkey是服务器申请的private key$remoteip是用户的ip$challenge是recaptcha_challenge_field字段的值来自前端页面 $response是recaptcha_response_field字段的值。函数返回ReCaptchaResponse class的实例ReCaptchaResponse类有2个属性 $is_valid是布尔型的表示校验是否有效 $error是返回的错误代码。 $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 . prebr /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 prebr /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 . preBoth 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 prePassword Changed./pre; } else { // Issue with the passwords matching echo prePasswords did not match./pre; $hide_form false; } ((is_null($___mysqli_res mysqli_close($GLOBALS[___mysqli_ston]))) ? false : $___mysqli_res); } ?按照源码分析总共分为两个阶段第一阶段对用户的身份进行验证step为1验证成功后才能进行密码修改第二阶段step为2两次输入的密码一致可以进行修改所以我们可以将抓取数据包中的step直接改为2放行则可以修改成功Medium?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 . prebr /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 // 对参数passed_captcha进行验证如果通过身份验证该参数就为true prebr /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 . preBoth 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 . prebr /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 prePassword Changed./pre; } else { // Issue with the passwords matching echo prePasswords did not match./pre; $hide_form false; } ((is_null($___mysqli_res mysqli_close($GLOBALS[___mysqli_ston]))) ? false : $___mysqli_res); } ?与Low难度相比增加了一个passed_capt当passed_capt为true时就可以修改密码了所以解决方法依旧是抓包修改相关参数放行即可High?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] ); //通过身份验证条件或者 参数g-recaptcha-respon为hidd3n_valu3并且参数 HTTP_USER_AGE为 reCAPTC就算是验证通过了 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 prePassword Changed./pre; } else { // Ops. Password mismatch $html . preBoth passwords must match./pre; $hide_form false; } } else { // What happens when the CAPTCHA was entered incorrectly $html . prebr /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这里是指谷歌返回的验证结果是false并且参数recaptcha_response_field不等于hidd3n_valu3或者http包头的User-Agent参数不等于reCAPTCHA时就认为验证码输入错误反之则认为已经通过了验证码的检查。搞清楚了验证逻辑剩下就是伪造绕过了由于$resp参数我们无法控制所以重心放在参数recaptcha_response_field、User-Agent上。修改g-recaptcha-responsehidd3n_valu3User-Agent:reCAPTCHA重放请求密码修改成功。Impossible?php if( isset( $_POST[ Change ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ user_token ], $_SESSION[ session_token ], index.php ); // Hide the CAPTCHA form $hide_form true; // Get input $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([MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work., E_USER_ERROR)) ? : )); $pass_new md5( $pass_new ); $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([MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work., E_USER_ERROR)) ? : )); $pass_conf md5( $pass_conf ); $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([MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work., E_USER_ERROR)) ? : )); $pass_curr md5( $pass_curr ); // 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 echo prebr /The CAPTCHA was incorrect. Please try again./pre; $hide_form false; return; } else { // Check that the current password is correct $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(); // Do both new password match and was the current password correct? if( ( $pass_new $pass_conf) ( $data-rowCount() 1 ) ) { // Update the database $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(); // Feedback for the end user - success! echo prePassword Changed./pre; } else { // Feedback for the end user - failed! echo preEither your current password is incorrect or the new passwords did not match.br /Please try again./pre; $hide_form false; } } } // Generate Anti-CSRF token generateSessionToken(); ?Impossible级别的代码增加了Anti-CSRF token 机制防御CSRF攻击利用PDO技术防护sql注入验证过程不再分成两部分了同时要求用户输入之前的密码进一步加强了身份认证。并且也不存在逻辑漏洞了抓包改改包并不能绕过验证码的检测。