新手指南:DVWA-1.9全级别教程之Insecure CAPTCHA

*本文原创作者:lonehand,转载须注明来自FreeBuf.COM

目前,最新的 DVWA 已经更新到 1.9 版本( http://www.dvwa.co.uk/ ),而网上的教程大多停留在旧版本,且没有针对 DVWA high 级别的教程,因此萌发了一个撰写新手教程的想法,错误的地方还请大家指正。

DVWA简介

DVWA Damn Vulnerable Web Application )是一个用来进行安全脆弱性鉴定的 PHP/MySQL Web 应用,旨在为安全专业人员测试自己的专业技能和工具提供合法的环境,帮助 web 开发者更好的理解 web 应用安全防范的过程。

DVWA共有十个模块,分别是

Brute Force (暴力(破解))

Command Injection (命令行注入)

CSRF (跨站请求伪造)

File Inclusion (文件包含)

File Upload (文件上传)

Insecure CAPTCHA (不安全的验证码)

SQL Injection SQL 注入)

SQL Injection Blind )( SQL 盲注)

XSS Reflected )(反射型跨站脚本)

XSS Stored )(存储型跨站脚本)

需要注意的是, DVWA 1.9 的代码分为四种安全级别: Low Medium High Impossible 。初学者可以通过比较四种级别的代码,接触到一些 PHP 代码审计的内容。

DVWA的搭建

Freebuf 上的这篇文章《新手指南:手把手教你如何搭建自己的渗透测试环境》( http://www.freebuf.com/sectool/102661.html )已经写得非常好了,在这里就不赘述了。

之前模块的相关内容

Brute Force

Command Injection

CSRF

File Inclusion

File Upload

本文介绍 Insecure CAPTCHA 模块的相关内容,后续教程会在之后的文章中给出。

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

服务器端核心代码:

is_valid ) {
// What happens when the CAPTCHA was entered incorrectly
$html     .= "

The CAPTCHA was incorrect. Please try again.

";
$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 "


You passed the CAPTCHA! Click the button to confirm your changes.




";
}
else {
// Both new passwords do not match.
$html .= "

Both passwords must match.

";
$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(

 . mysql_error() . 

);

// Feedback for the end user
echo "

Password Changed.

";
}
else {
// Issue with the passwords matching
echo "

Passwords did not match.

";
$hide_form = false;
}

mysql_close();
}

?>

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

漏洞利用

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

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

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

更改 step 参数绕过验证码:

修改密码成功:

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

http://192.168.153.130/dvwa/vulnerabilities/captcha/ ">

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

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

Medium

服务器端核心代码:

is_valid ) {
// What happens when the CAPTCHA was entered incorrectly
$html     .= "

The CAPTCHA was incorrect. Please try again.

";
$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 "


You passed the CAPTCHA! Click the button to confirm your changes.





";
}
else {
// Both new passwords do not match.
$html .= "

Both passwords must match.

";
$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 .= "


You have not passed the CAPTCHA.

";
$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(

 . mysql_error() . 

);

// Feedback for the end user
echo "

Password Changed.

";
}
else {
// Issue with the passwords matching
echo "

Passwords did not match.

";
$hide_form = false;
}

mysql_close();
}

?>

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

漏洞利用

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

抓到的包:

更改之后的包:

更改密码成功:

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

http://192.168.153.130/dvwa/vulnerabilities/captcha/ ">

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

不过依然会跳转到更改密码成功的界面。

High

服务器端核心代码:

is_valid && ( $_POST[ recaptcha_response_field ] != hidd3n_valu3 || $_SERVER[ HTTP_USER_AGENT ] != reCAPTCHA ) ) {
// What happens when the CAPTCHA was entered incorrectly
$html     .= "

The CAPTCHA was incorrect. Please try again.

";
$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(

 . mysql_error() . 

);

// Feedback for user
echo "

Password Changed.

";
}
else {
// Ops. Password mismatch
$html .= "

Both passwords must match.

";
$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 "

The CAPTCHA was incorrect. Please try again.

";
$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 "

Password Changed.

";
}
else {
// Feedback for the end user - failed!
echo "

Either your current password is incorrect or the new passwords did not match.
Please try again.

";
$hide_form = false;
}
}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

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

  • 版权声明: 本文源自互联网, 于4个月前,由整理发表,共 4229字。
  • 原文链接:点此查看原文