前言

CSRF 介绍

CSRF(跨站请求伪造),全称为Cross-site request forgery,简单来说,是指利用受害者尚未失效的身份认证信息(Cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。

CSRF 与 XSS 区别

  1. XSS是通过修改页面Javascript等代码后,发给用户从而实现盗取Cookie信息,之后利用Cookie进行登陆网站等操作。非法操作是黑客。
  2. CSRF并没有盗取Cookie信息,而是通过用户直接利用Cookie进行操作。非法操作并不是黑客,而是用户本身。
  3. CSRF通常和XSS联合利用。

Low

源码分析

通过GET方式获取两次密码,两次密码输入一致的话,就可以直接带入数据中修改密码。

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
<?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);
}

?>

漏洞复现

  1. Test Credentials 使用 DVWA 默认密码(admin/password)进行登录
  1. CSRF 输入两次密码123456进行修改密码
  1. 然后我们就能获取到CSRF漏洞利用链接
1
http://192.168.199.236/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
  1. 使用 admin/123456Test Credentials 进行测试发现账户密码已被更改

Medium

源码分析

  • HTTP_REFERER表示数据包中的referer字段数据包的来源链接
  • SERVER_NAME表示数据包中的host访问的主机地址
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
36
37
38
39
<?php

if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
//stripos(str1, str2) 检查 str2 在 str1 中出现的位置(不区分大小写),如果有返回为 True,反之为False
//判断 Host 字段是否出现在 referer 字段中
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);
}

?>

漏洞利用

  1. 使用BurpSuite抓包获取Referer
  1. 我们想直接尝试使用Low漏洞利用方法
  • 构建利用链接如下
1
http://192.168.199.236/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
  • BurpSuite返回信息如下
  • 相比之下缺少了Referer
  1. 因为 HTTP_REFERERSERVER_NAME 的限制我们无法进行修改
  1. 那我们就在BurpSuite上补充回Referer
1
Referer: http://192.168.199.236/dvwa/vulnerabilities/csrf/
  1. BurpSuite修改放行后成功修改

High

源码分析

High引入了Anti-CSRF Token机制进行身份验证

用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

<?php

$change = false;
$request_type = "html";
$return_message = "Request Failed";

if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
$data = json_decode(file_get_contents('php://input'), true);
$request_type = "json";
if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
array_key_exists("password_new", $data) &&
array_key_exists("password_conf", $data) &&
array_key_exists("Change", $data)) {
$token = $_SERVER['HTTP_USER_TOKEN'];
$pass_new = $data["password_new"];
$pass_conf = $data["password_conf"];
$change = true;
}
} else {
if (array_key_exists("user_token", $_REQUEST) &&
array_key_exists("password_new", $_REQUEST) &&
array_key_exists("password_conf", $_REQUEST) &&
array_key_exists("Change", $_REQUEST)) {
$token = $_REQUEST["user_token"];
$pass_new = $_REQUEST["password_new"];
$pass_conf = $_REQUEST["password_conf"];
$change = true;
}
}

if ($change) {
// Check Anti-CSRF token
checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );

// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
$pass_new = md5( $pass_new );

// Update the database
$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );

// Feedback for the user
$return_message = "Password Changed.";
}
else {
// Issue with passwords matching
$return_message = "Passwords did not match.";
}

mysqli_close($GLOBALS["___mysqli_ston"]);

if ($request_type == "json") {
generateSessionToken();
header ("Content-Type: application/json");
print json_encode (array("Message" =>$return_message));
exit;
} else {
echo "<pre>" . $return_message . "</pre>";
}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

漏洞利用

  • 本题难点:需要用到存储型 XSS获取token

XSS (Stored) High

  1. 由于输入长度有限度,先使用BurpSuite进行抓包
  1. 修改txtName里面的内容
1
<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>
  1. 逐一放包就会找到我们需要的Token

CSRF High

  1. 输入需要修改的密码并使用BurpSuite抓包
  1. token值修改为我们从XSS(Stored)抓取到的token
  1. BurpSuite 翻新后密码就已经被修改了

参考 & 引用

https://zhuanlan.zhihu.com/p/565945383
https://www.cnblogs.com/chadlas/articles/15708801.html
https://blog.csdn.net/weixin_46709219/article/details/109325123