Vulnerability: Cross Site Request Forgery (CSRF)

时间:2023-03-10 05:45:35
Vulnerability: Cross Site Request Forgery (CSRF)

CSRF跨站请求伪造

这是一种网络攻击方式,也被称为one-click attack或者session riding

攻击原理

CSRF攻击利用网站对于用户网页浏览器的信任,挟持用户当前已登陆的Web应用程序,去执行并非用户本意的操作。

CSRF图示解析

参考如下:

https://blog.csdn.net/diu_brother/article/details/88367029

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

$GLOBALS:是引用全局变量。$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)

在php中$GLOBALS[index]的数组储存了所有的全局变量,变量的名字就是数组的键。

从源码可以看出这里只是作一个两次输入密码是否一致的判断,如果是一致的就查看有没有设置数据库连接的全局变量和其是否为一个对象。再用mysqli_real_escape_string()函数去转义。

密码不一致就输出报错:

Vulnerability: Cross Site Request Forgery (CSRF)

这里是一个get提交 我们在url中可以看得到

Vulnerability: Cross Site Request Forgery (CSRF)

这里没有作其他防护措施,我们可以类似地构造这样的url通过该用户来点击,就造成攻击

http://192.168.71.128/dvwa/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#

这样的链接发给已经用户在浏览器*问,就可以修改密码为hack,

当然这种链接直接发过去太明显了,这里我们可以构造短链接,这样的迷惑性就比较强了。

Vulnerability: Cross Site Request Forgery (CSRF)

我们输入短链接后成功修改密码

Vulnerability: Cross Site Request Forgery (CSRF)

Vulnerability: Cross Site Request Forgery (CSRF)

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

相比与low的这里添加了一个Referer字段的验证

if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

这里的$_SERVER['SERVVER_NAME']是PHP中的超全局变量,详细可参考:

https://www.cnblogs.com/rendd/p/6182918.html

HTTP_REFERER是Referer参数值,即来源地址
SERVER_NAME是host参数及主机ip名(我这里是192.168.71.128)

这两个变量进行对比HTTP_REFERER和SERVER_NAME

$_SERVER["SERVER_NAME"]  输出配置文件httpd.conf中的ServerName,一般情况下与HTTP_HOST值相同,但如果服务器端口不是默认的80端口,或者协议规范不是HTTP/1.1时,HTTP_HOST会包含这些信息,而SERVER_NAME不一定包含。(主要看配置文件的设置)

我们先直接更改密码,然后抓包看看,

Vulnerability: Cross Site Request Forgery (CSRF)

可以看到成功地更改密码是在host里的值有和referer中一样的值,通过验证才可以成功修改,与我分析的一样。

那我们如何来攻击呢?我们看看直接用之前那种直接访问呢

Vulnerability: Cross Site Request Forgery (CSRF)

可以看到直接报错了,这下我们用抓包来具体看一下

Vulnerability: Cross Site Request Forgery (CSRF)

我们再看看之前修改成功的包是有referer的,而这里是没有的,我们可以添加referer,只需要里面包含有192.168.71.128字段就可以了

Vulnerability: Cross Site Request Forgery (CSRF)

注意:

但是csrf攻击我们挂载的恶意页面一定是在外网服务器上,肯定是不能这样攻击的,referer是外网的。

我看了网上的教程是在攻击者服务器里面放一个包含更改密码的html页面文件,然后让受害者去访问。

网上看到的漏洞利用

过滤规则是http包头的Referer参数的值中必须包含主机名(这里是192.168.153.130)

我们可以将攻击页面命名为192.168.153.130.html(页面被放置在攻击者的服务器里,这里是10.4.253.2)就可以绕过了。

但是我这里放好访问后,host是攻击服务器的IP了不是原服务器IP,而别人的是这样的

Vulnerability: Cross Site Request Forgery (CSRF)

这里就不太搞得懂,尝试了好久,

不懂放在攻击服务器上的攻击页面,如何被用户访问,如何来实现攻击了。

我的是这样

Vulnerability: Cross Site Request Forgery (CSRF)

Vulnerability: Cross Site Request Forgery (CSRF)

High

还是来看一下high级别的代码

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

可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

这里想要利用漏洞必须要获取token,要利用受害者的cookie去修改密码的页面获取关键的token。

里牵扯到了跨域问题,而现在的浏览器是不允许跨域请求的。这里简单解释下跨域,我们的框架iframe访问的地址是http://192.168.71.128/dvwa/vulnerabilities/csrf,位于服务器192.168.71.128上,而我们的攻击页面位于黑客服务器上,两者的域名不同,域名B下的所有页面都不允许主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。

还是有很多不懂,慢慢来吧~~