DVWA-CSRF(跨站请求伪造)
csrf(Cross-site request forgery)跨站请求伪造:攻击者诱导用户访问第三方网站,在第三方网站中携带恶意代码,向被攻击者发送请求
原理可以这样来说
用户在访问了一个后台管理网站后,例如用户更改密码,但是更改密码需要登录认证的,用户在登录后,浏览器会保存认证一段时间,叫做
cookie,保证下次访问不在弹出登录框
这个时候攻击者恶意伪造一个网站,这个网站的功能是更改用户的密码,但是攻击者没有用户的cookie,是无法更改密码的,攻击者通过诱导用户点击恶意网页,由于用户浏览器带有后台管理网站的cookie,恶意网页这是请求更改密码就会执行成功,导致用户什么都不知道,密码就被修改了
LOW
审计源码
<?php
// 判断有没有接收到Change
if( isset( $_GET[ 'Change' ] ) ) {
// 获取输入的两次密码
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// 两次输入密码是否相同
if( $pass_new == $pass_conf ) {
// 相同
$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)) ? "" : ""));
// 使用md5加密新密码
$pass_new = md5( $pass_new );
// 更新数据库密码
$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>' );
// 密码更改成功
echo "<pre>Password Changed.</pre>";
}
else {
// 两次密码输入不相同,密码更改失败
echo "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
通过代码可以看出,Low没有做任何过滤,并且是一个更改密码操作
首先更改一次密码,获取更改密码的url

http://172.16.1.103/dvwa/vulnerabilities/csrf/?password_new=Admin123&password_conf=Admin123&Change=Change#
这里可以看到将密码更改为了Admin123
再次打开一个firefox,将链接粘贴的url中
http://172.16.1.103/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

退出DVWA登录,使用123456登录靶场


可以看到,密码已经被更改成功
构造恶意网页
在kali另一台机器上写入恶意网页
<img src="http://172.16.1.103/dvwa/vulnerabilities/csrf/?password_new=Admin123&password_conf=Admin123&Change=Change#" style="display:none"/>
<h1>Test HTML</h1>

使用登录DVWA靶场的firefox访问恶意网页

在访问后,回到DVWA退出登录,查看密码是否更改成功

可以看到,只有使用Admin123才可以登录成功
Medium
审计源码
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// 检查 http_referer 是否是 访问服务器的访问名称
// stripos() 查找字符串首次出现的位置
// $_SERVER变量 获取 http_referer 的值,server_name获取服务端的名称例如:www.baidu.com
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// 获取两次输入的密码
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// 判断两次密码是否相同
if( $pass_new == $pass_conf ) {
// 相同
$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)) ? "" : ""));
// 使用 md5 加密输入新密码
$pass_new = md5( $pass_new );
// 更改密码
$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>' );
// 更改密码成功
echo "<pre>Password Changed.</pre>";
}
else {
// 更改密码失败
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// rerfer值不正确
echo "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
通过观察,使用
stripos()查找一个字符串时第一次出现的位置,
HTTP_REFERER是获取HTTP请求头中的REferer值,用来告诉服务端来自哪里的请求
SERVER_NAME访问目标的主机名
总体来说就是检查header中的Referer中是否含有服务端主机名
修改密码抓包查看数据包结构

可以看到,Referer中默认就是来自http://172.16.1.103/dvwa/vulnerabilities/csrf/
所以密码可以修改成功,这里密码修改,只要是Referer中含有服务器主机名就可以成功
我们直接修改为Referer:172.16.1.103


可以看到密码是可以修改成功的
但是,真正的CSRF是,诱导点击,访问恶意网页进行修改,我们这里只是通过burpsuite更改密码成功
恶意文件修改密码
这里我已失败告终了,学术不精,到底该如何在点击访问时更改Referer的值呢?
High
审计源码
<?php
// 定义改变默认为 false
$change = false;
// 定义请求类型默认为 html
$request_type = "html";
// 定义返回信息默认为 返回失败
$return_message = "Request Failed";
// 判断 change 是否为true
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) {
// 检查 user_token
checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );
// 判断输入的两次密码是否相同
if( $pass_new == $pass_conf ) {
// 使用md5加密输入的新密码
$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
$pass_new = md5( $pass_new );
// 更新用户的密码
$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );
// 密码更改成功提示
$return_message = "Password Changed.";
}
else {
// 提示密码更改失败
$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>";
}
}
// 生成token
generateSessionToken();
?>
通过观察这里引入了一个user_token,防止点击修改密码,但是可以通过抓包的方式修改user_token
更改密码为Admin123抓包查看

这里的user_token在每一次更改密码服务器都会刷新返回一个;
通过同等级XSS (Reflected)漏洞获取user_token,然后通过获取的user_token更改密码,
<iframe src="../csrf" onload=alert(frames[0].document.getElementsByName('user_token')[0].value) />

这样确实可以获取user_token.
上述这个方法其实和在CSRF页面查看源代码获取user_token的方法类似

上述两个方法确实都获取到了user_token,但是并没有点击就可以修改用户密码,还是需要通过抓包更改密码;
所以如果要实现真正的CSRF,需要写一个javascript诱导用户点击,获取其user_token,然后通过获取的user_token更改密码
到底该怎么实现?由于我没有学过javascript,所以如何获取就不得而知,后续再来补坑吧。
Impossible
审计源码
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// 获取并检查 user_token 是否正常
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取 上次密码 和 两次输入的新密码
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// 去除获取当前密码中的 /
$pass_curr = stripslashes( $pass_curr );
$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// 使用 md5 加密获取的当前密码
$pass_curr = md5( $pass_curr );
// 检查当前密码是否正确
$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();
// 判断输入两次新密码是否相同 和 输入当前密码是否正确
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// 正确
// 去除新密码中的 /
$pass_new = stripslashes( $pass_new );
$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)) ? "" : ""));
// 使用 md5 加密新密码
$pass_new = md5( $pass_new );
// 更新数据库中的密码为新密码
$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();
// 返回更改密码成功
echo "<pre>Password Changed.</pre>";
}
else {
// 否则返回错误
echo "<pre>Passwords did not match or current password incorrect.</pre>";
}
}
// 生成 user_token 认证
generateSessionToken();
?>
可以看到这里既引入了user_token认证,也去除了密码中的/,最重要的是需要输入当前的密码,才可以更改密码。
唯一不好的就是更改密码为GET请求方式,换为POST更好,可能是作者想方便测试吧
DVWA-CSRF(跨站请求伪造)的更多相关文章
- python CSRF跨站请求伪造
python CSRF跨站请求伪造 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...
- Django之CSRF跨站请求伪造(老掉牙的钓鱼网站模拟)
首先这是一个测试的代码 请先在setting页面进行下面操作 注释完成后,开始模拟钓鱼网站的跨站请求伪造操作: 前端代码: <!DOCTYPE html> <html lang=&q ...
- ajax向Django前后端提交请求和CSRF跨站请求伪造
1.ajax登录示例 urls.py from django.conf.urls import url from django.contrib import admin from app01 impo ...
- python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)
一.ajax登录示例 新建项目login_ajax 修改urls.py,增加路径 from app01 import views urlpatterns = [ path('admin/', admi ...
- 第三百一十五节,Django框架,CSRF跨站请求伪造
第三百一十五节,Django框架,CSRF跨站请求伪造 全局CSRF 如果要启用防止CSRF跨站请求伪造,就需要在中间件开启CSRF #中间件 MIDDLEWARE = [ 'django.midd ...
- Django中的CSRF(跨站请求伪造)
Django中的CSRF(跨站请求伪造) Django CSRF 什么是CSFR 即跨站请求伪装,就是通常所说的钓鱼网站. 钓鱼网站的页面和正经网站的页面对浏览器来说有什么区别? (页面是怎么来的? ...
- Django框架 之 基于Ajax中csrf跨站请求伪造
Django框架 之 基于Ajax中csrf跨站请求伪造 ajax中csrf跨站请求伪造 方式一 1 2 3 $.ajaxSetup({ data: {csrfmiddlewaretoken: ...
- 十三 Django框架,CSRF跨站请求伪造
全局CSRF 如果要启用防止CSRF跨站请求伪造,就需要在中间件开启CSRF #中间件 MIDDLEWARE = [ 'django.middleware.security.SecurityMidd ...
- Web框架之Django_09 重要组件(Django中间件、csrf跨站请求伪造)
摘要 Django中间件 csrf跨站请求伪造 一.Django中间件: 什么是中间件? 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于 ...
- django上课笔记3-ORM补充-CSRF (跨站请求伪造)
一.ORM补充 ORM操作三大难点: 正向操作反向操作连表 其它基本操作(包含F Q extra) 性能相关的操作 class UserInfo(models.Model): uid = models ...
随机推荐
- git stash (pycharm/vscode的gui演示)
git stash (pycharm/vscode的gui演示) 场景 代码刚写到一半,发现线上bug需要马上修改部署上线 此时手头的代码写一半,提交根本跑不动甚至影响原来的业务了 回滚就白瞎搬了好几 ...
- The operation was rejected by your operating system.
我在新项目开启的时候使用npm install来初始化前端代码的开发环境 但是遇到一个问题,一直报: The operation was rejected by your operating syst ...
- linux安装jdk脚本
使用Shell远程给Linux安装JDK #1.下载JDK(不同版本JDK更换链接即可) wget命令下载的文件会在当前目录下,所以如果在下载前最好先 cd 到想把JDK安装的位置,如果没有,可以 ...
- uniapp使用rich-text内容过长在真机无法滚动
解决方案:在rich-text标签上加scroll-view解决 <scroll-view scroll-y="true" style="height: 745rp ...
- Flask的介绍、安装和使用
Flask的介绍 Flask其实就是Python-web中的一个框架,也就是说Flask是一个工具,提供了库和技术来让你建立一个web的应用程序.这个程序可以使一些web页面.博客.基于web的日历应 ...
- .NET CORE 下收发邮件之 MAILKIT
背景 利用代码发送邮件在工作中还是比较常见的,相信大家都用过SmtpClient来处理发送邮件的操作,不过这个类以及被标记已过时,所以介绍一个微软推荐的库MailKit来处理. MailKit开源地址 ...
- HTML复习(19.背景样式)
重点 掌握背景颜色属性 掌握背景图片属性 背景样式简介 在CSS中,背景样式包括2个方面:①背景颜色:②背景图片.在Web1.0时代,都是使用background或者bgcolor这两个"H ...
- mysql datetime和timestamp区别
datetime: 保存格式为YYYYMMDDHHMMSS的整数,与时区无关,存入什么值就是什么值,不会根据当前时区进行转换.mysql5.6.4中可以存储小数片段,最多小数点后6位,在mysql5. ...
- mysql循环插入分隔符分开的字符串
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()BEGIN DECLARE i,help_topic_id INT; SET i=10469; ...
- SpringBoot整合其他框架
SpringBoot整合Junit 实现步骤 搭建SpringBoot工程 引入starter-test起步依赖 编写测试类 添加测试相关注解 @RunWith(SpringRunner.class) ...