首页 » 安全研究 » 正文

DVWA下的CSRF通关

CSRF跨站点请求伪造(Cross—Site Request Forgery):

跟XSS攻击一样,存在巨大的危害性,你可以这样来理解:
攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。 如下:其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。

CSRF攻击攻击原理及过程如下:

1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

5.  浏览器在接收到这些攻击性代码后,根据网站 B 的请求,在用户不知情的情况下携带 Cookie 信息,向网站 A 发出请求。网站 A 并不知道该请求其实是由 B 发起的,所以会根据用户 C 的 Cookie 信息以 C 的权限处理该请求,导致来自网站 B 的恶意代码被执行。

与XSS的区别:

XSS是通过修改页面Javascript等代码后,发给用户从而实现盗取cookie信息,之后利用cookie进行登陆网站等操作。非法操作是黑客。
CSRF并没有盗取cookie信息,而是通过用户直接利用cookie进行操作。非法操作并不是黑客,而是用户本身。

1、low级别

<?php

if( isset( $_GET[ 'Change' ] ) ) {
// Get input
$pass_new  = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

// Do the passwords 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 the 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 user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}

((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

可以看到,源码中的mysql_real_escape_string() 函数有防护sql注入的作用,然后就只进行了$pass_new == $pass_conf判断,没有进行任何的验证。

在新密码和确认密码之处填写123456

1604307164_5f9fc8dc280a1f3e0de32.png!small?1604307127111

burp suite抓包发现,通过get请求执行

1604307194_5f9fc8fab4446dc1af1a2.png!small?1604307157773
http://172.21.98.85/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

根据上面的url,进行构造payload:

http://172.21.98.85/DVWA-master/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change#

这样的payload,一般人都可以看出来存在陷进,往往不会去点击,因此我们还需要进一步伪装,把它缩短。

1604307422_5f9fc9decb0e649f08a56.png!small?1604307386035
1604307435_5f9fc9ebc00fcd09afb3a.png!small?1604307398986

得到一个短的url:http://r6d.cn/9rGy,提醒一句,以后但凡看见很短的url,然后以很不常见的格式出现,千万别着急点击浏览。

1604308403_5f9fcdb39a90c12928ae5.png!small?1604308366978

现在的密码已经变成test,不再是123456。

我们还可以编辑一个页面,填好payload,等正常用户浏览访问就执行了。

1604308732_5f9fcefc5395d56f0f1b7.png!small?1604308695313

后缀改成.html.就行了。

1604309076_5f9fd05446371cd0c3a04.png!small?1604309039302

真实情况下,这个html文件的表象应该具备各种诱惑力,例如,如何一夜暴富、xxx照等等,让你情不自禁的想去浏览访问。

1604309202_5f9fd0d2b981f2ed2cf1e.png!small?1604309165611

正当你想开喷时,说好的惊喜呢,怎么是404,哈哈,但其实你已经将你的密码从test修改为password了。

1604309253_5f9fd105287258adae7f2.png!small?1604309216884

2、medium

<?php

if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// Get input
$pass_new  = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

// Do the passwords 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 the 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 user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}

((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

先看看里面几个重要的函数

eregi()函数: 匹配符合条件的字符串,已经被preg_match() 替代,加i不区分大小写。
stripos函数:返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
$_SERVER[‘HTTP_REFERER’] #链接到当前页面的前一页面的 URL 地址。
$_SERVER[‘SERVER_NAME’] #当前运行脚本所在服务器主机的名称。

同样的内容,换成1.html

1604311211_5f9fd8ab4b71d1d52bffe.png!small?1604311174200
1604312268_5f9fdcccf15f9f9352e30.png!small?1604312231873
1604324441_5fa00c59104420351e389.png!small?1604324404347

但是实际上没修改完成,因为这个级别的增加了Referer认证,那么将http包头的Referer参数的值中包含主机名就行了。

3、High级别

先放弃,这个token值我一直绕不过去,表示先放弃了,大概绕过的思路知道就行,不求甚解!

4、mpossible级别

<?php 

if( isset( $_GET[ ‘Change’ ] ) ) { 
// Check Anti-CSRF token 
checkToken( $_REQUEST[ ‘user_token’ ], $_SESSION[ ‘session_token’ ], ‘index.php’ ); 

// Get input 
$pass_curr = $_GET[ ‘password_current’ ]; 
$pass_new  = $_GET[ ‘password_new’ ]; 
$pass_conf = $_GET[ ‘password_conf’ ]; 

// Sanitise current password input 
$pass_curr = stripslashes( $pass_curr ); 
$pass_curr = mysql_real_escape_string( $pass_curr ); 
$pass_curr = md5( $pass_curr ); 

// 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 passwords match and does the current password match the user? 
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) { 
// It does! 
$pass_new = stripslashes( $pass_new ); 
$pass_new = mysql_real_escape_string( $pass_new ); 
$pass_new = md5( $pass_new ); 

// Update database with new password 
$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 user 
echo “<pre>Password Changed.</pre>”; 

else { 
// Issue with passwords matching 
echo “<pre>Passwords did not match or current password incorrect.</pre>”; 



// Generate Anti-CSRF token 
generateSessionToken(); 

?>

也是采用PDO技术防护了,根据现实情况,一般防护csrf的办法最常见的就是增加原始密码验证,短信验证、验证码验证,这样基本就很难进行scrf攻击了。

本文章属于转载,来自FreeBuf.COM

发表评论