文章目录
DVWA:Cross Site Request Forgery (CSRF)源码解析
简介
CSRF:跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。
源码解析
Low CSRF Source
无验证可随意更改密码
<?php
if( isset( $_GET[ 'Change' ] ) ) { //如果利用了Change进行了传值
// 获取输入的值
$pass_new = $_GET[ 'password_new' ]; //获取参数password_new对应的值存储到pass_new中
$pass_conf = $_GET[ 'password_conf' ]; //获取参数password_conf对应的值存储到pass_conf中
// 看密码和确认密码(pass_new和pass_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)) ? "" : ""));
$pass_new = md5( $pass_new ); //将密码用ma5进行加密
/* 数据库中存储的密码实际为密码的md5值,用于防止密码的泄露*/
// 更新数据库
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; //将计算好的MD5值添加到当前用户
// 执行sql语句,如果失败报错输出
$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);
}
?>
Medium CSRF Source
添加了对服务器进行验证,对本地搭建的DVWA靶场无效
<?php
if( isset( $_GET[ 'Change' ] ) ) { // 如果前端用了get方法进行传参
// 查看请求的来源
// 查看前端请求中是否存在服务器名称,如果存在则继续进行,详见下方补充
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)) ? "" : ""));
$pass_new = md5( $pass_new ); //将密码用md5进行加密
// 更新数据库信息
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; //将用户密码和当前用户添加进sql语句中
// 执行sql语句,如果失败报错输出
$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 {
// 如果前端请求中不存在服务器名称,则这不是一个可信的源,给出回显
echo "<pre>That request didn't look correct.</pre>";
}
// 关闭数据库连接
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
/*
补充:
1. function stripos($haystack, $needle, $offset = 0): bool|int { },
该函数用于在查询在变量"haystack"所存的字符串中,
不分大小写地查询在变量"needle"中所存字符串是否存在,
若存在返回第一次出现的位置,
若不存在返回false,offset为偏移量,默认为0,可省略
$haystack:样本字符串
$needle:要匹配的字符串
$offset:偏移量
2. $_SERVER 是一个在PHP中预定义的超全局变量,用于存储关于服务器和请求的信息
*/
?>
High CSRF Source
设置了防CSRF令牌,用户没错方位页面时服务器都会生成一个令牌,当数据传输的后端时会验证该令牌
- 打开burpsuite下载CSRF Token Tracker
2. 进行抓包
- 将令牌名称和令牌内容添加到CSRF Token Tracker中,(这里数据包让我放了,找了张别的意思是这个意思)
- 将数据包发送到重发器中修改传参进行发送,发现可成功修改密码
 ) {
// 查看防CSRF令牌
// 查看Request中是否存在"user_token"和Session中是否有"session_token",范围为index.php
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取前端输入的值
$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)) ? "" : ""));
$pass_new = md5( $pass_new ); // 将密码进行md5加密
// 更新数据库
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; //将用户密码和当前用户添加进sql语句中
// 执行sql语句,失败则报错
$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);
}
// 生成防CSRF令牌
generateSessionToken();
?>
Impossible CSRF Source
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// 查看防CSRF令牌
// 查看Request中是否存在"user_token"和Session中是否有"session_token",范围为index.php
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中的值进行转义
$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)) ? "" : ""));
$pass_curr = 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 ); //将:user占位符替换为当前用户
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR ); //将:password占位符替换为$pass_curr所对应的md5值
$data->execute(); // 执行数据库语句
// 检查新密码和确认密码是否一致,并检查当前用户和旧密码是否匹配
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// 如果新密码和确认密码一致,并且查询只有一条数据
$pass_new = stripslashes( $pass_new ); // 将$pass_new中去除转义字符
// 查看数据库是连接,并对$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)) ? "" : ""));
$pass_new = md5( $pass_new ); // 对新密码进行md5加密
// 更新数据库
// 预处理sql语句
$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
$data->bindParam( ':password', $pass_new, PDO::PARAM_STR ); // 将:password占位符替换为$pass_new(新密码)
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR ); // 将:user占位符替换为当前用户
$data->execute(); //执行数据库语句
// 给用户回显
echo "<pre>Password Changed.</pre>";
}
else {
// 如果两项中有任意一项不匹配在给用户回显
echo "<pre>Passwords did not match or current password incorrect.</pre>";
}
}
// 生成防CSRF令牌
generateSessionToken();
?>