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 ...
随机推荐
- 实现MybatisPlus乐观锁
1.实体类中添加version字段及相关注解 @Version@TableField(fill = FieldFill.INSERT)//第一次添加数据时使其有个默认值1private Integer ...
- [419] C1 Harbingers Of War OpCodez
[419] C1 Harbingers Of War Client 00 SendProtocolVersion 01 MoveBackwardToLocation 02 Say 03 Request ...
- RN 使用react-native-video 播放视频(包含进度条、全屏)
21年12月3日,阐述上有问题:应该将问题拆分,不该将代码整一大堆,看着很不舒适 目标需求:1. 实现视频播放 2. 进度条 3. 进入全屏 目标图是这样的: 需要三个组件 1. 播放视频组件, re ...
- AngularJs directive详解及示例代码
Directive(指令)笔者认为是AngularJ非常强大而有有用的功能之一.它就相当于为我们写了公共的自定义DOM元素或CLASS属性或ATTR属性,并且它不只是单单如此,你还可以在它的基础上来操 ...
- redis - 常用方法封装总结
package com.citydo.utils; import org.springframework.data.redis.connection.DataType; import org.spri ...
- 前端vue框架上手记录
---恢复内容开始--- Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: 通过 @vue/cli 搭建交互式的项目脚手架. 通过 @vue/cli + @vue/cli-se ...
- webpack打包分析webpack-bundle-analyzer 打包文件分析工具
最近需要减少项目打包后的文件(bundle)的大小,那么首先需要了解bundle的构成. 目前我已知的方法有两种: 利用webpack-bundle-analyzer插件 利用webpack官方提供的 ...
- python之基本数据类型--数字类型
变量是为了让计算机像人一样的去记录事务的某种状态,那变量值就是用来存储事务的状态,在现实生活中事物状态明显的分为不同的种类,比如人类的年龄.身高.体重.工资.等等,所以变量也是有不同类型的,变量的几种 ...
- redis 配置哨兵模式时出现的问题(redis 版本 6.2.5)
今天准备搭建一个 redis 集群(redis 版本 6.2.5),在这之前要先配置好哨兵模式. 但是在配置哨兵模式时出现了问题.之前没有搭建集群时(一主两从,三台虚拟机)可以顺利配置好,而搭建集群时 ...
- 基于predis高并发情况下实现频率控制的函数
/** * 频率控制函数 * @param string $product 保持唯一 * @param string $key 限制频率的维度 比如uid * @param int $millisec ...