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(跨站请求伪造)的更多相关文章

  1. python CSRF跨站请求伪造

    python CSRF跨站请求伪造 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...

  2. Django之CSRF跨站请求伪造(老掉牙的钓鱼网站模拟)

    首先这是一个测试的代码 请先在setting页面进行下面操作 注释完成后,开始模拟钓鱼网站的跨站请求伪造操作: 前端代码: <!DOCTYPE html> <html lang=&q ...

  3. ajax向Django前后端提交请求和CSRF跨站请求伪造

    1.ajax登录示例 urls.py from django.conf.urls import url from django.contrib import admin from app01 impo ...

  4. python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)

    一.ajax登录示例 新建项目login_ajax 修改urls.py,增加路径 from app01 import views urlpatterns = [ path('admin/', admi ...

  5. 第三百一十五节,Django框架,CSRF跨站请求伪造

    第三百一十五节,Django框架,CSRF跨站请求伪造  全局CSRF 如果要启用防止CSRF跨站请求伪造,就需要在中间件开启CSRF #中间件 MIDDLEWARE = [ 'django.midd ...

  6. Django中的CSRF(跨站请求伪造)

    Django中的CSRF(跨站请求伪造) Django CSRF  什么是CSFR 即跨站请求伪装,就是通常所说的钓鱼网站. 钓鱼网站的页面和正经网站的页面对浏览器来说有什么区别? (页面是怎么来的? ...

  7. Django框架 之 基于Ajax中csrf跨站请求伪造

    Django框架 之 基于Ajax中csrf跨站请求伪造 ajax中csrf跨站请求伪造 方式一 1 2 3 $.ajaxSetup({     data: {csrfmiddlewaretoken: ...

  8. 十三 Django框架,CSRF跨站请求伪造

     全局CSRF 如果要启用防止CSRF跨站请求伪造,就需要在中间件开启CSRF #中间件 MIDDLEWARE = [ 'django.middleware.security.SecurityMidd ...

  9. Web框架之Django_09 重要组件(Django中间件、csrf跨站请求伪造)

    摘要 Django中间件 csrf跨站请求伪造 一.Django中间件: 什么是中间件? 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于 ...

  10. django上课笔记3-ORM补充-CSRF (跨站请求伪造)

    一.ORM补充 ORM操作三大难点: 正向操作反向操作连表 其它基本操作(包含F Q extra) 性能相关的操作 class UserInfo(models.Model): uid = models ...

随机推荐

  1. 实现MybatisPlus乐观锁

    1.实体类中添加version字段及相关注解 @Version@TableField(fill = FieldFill.INSERT)//第一次添加数据时使其有个默认值1private Integer ...

  2. [419] C1 Harbingers Of War OpCodez

    [419] C1 Harbingers Of War Client 00 SendProtocolVersion 01 MoveBackwardToLocation 02 Say 03 Request ...

  3. RN 使用react-native-video 播放视频(包含进度条、全屏)

    21年12月3日,阐述上有问题:应该将问题拆分,不该将代码整一大堆,看着很不舒适 目标需求:1. 实现视频播放 2. 进度条 3. 进入全屏 目标图是这样的: 需要三个组件 1. 播放视频组件, re ...

  4. AngularJs directive详解及示例代码

    Directive(指令)笔者认为是AngularJ非常强大而有有用的功能之一.它就相当于为我们写了公共的自定义DOM元素或CLASS属性或ATTR属性,并且它不只是单单如此,你还可以在它的基础上来操 ...

  5. redis - 常用方法封装总结

    package com.citydo.utils; import org.springframework.data.redis.connection.DataType; import org.spri ...

  6. 前端vue框架上手记录

    ---恢复内容开始--- Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: 通过 @vue/cli 搭建交互式的项目脚手架. 通过 @vue/cli + @vue/cli-se ...

  7. webpack打包分析webpack-bundle-analyzer 打包文件分析工具

    最近需要减少项目打包后的文件(bundle)的大小,那么首先需要了解bundle的构成. 目前我已知的方法有两种: 利用webpack-bundle-analyzer插件 利用webpack官方提供的 ...

  8. python之基本数据类型--数字类型

    变量是为了让计算机像人一样的去记录事务的某种状态,那变量值就是用来存储事务的状态,在现实生活中事物状态明显的分为不同的种类,比如人类的年龄.身高.体重.工资.等等,所以变量也是有不同类型的,变量的几种 ...

  9. redis 配置哨兵模式时出现的问题(redis 版本 6.2.5)

    今天准备搭建一个 redis 集群(redis 版本 6.2.5),在这之前要先配置好哨兵模式. 但是在配置哨兵模式时出现了问题.之前没有搭建集群时(一主两从,三台虚拟机)可以顺利配置好,而搭建集群时 ...

  10. 基于predis高并发情况下实现频率控制的函数

    /** * 频率控制函数 * @param string $product 保持唯一 * @param string $key 限制频率的维度 比如uid * @param int $millisec ...