dvwa靶场,2024年最新网络安全最新实习面试经验总结
password_new=987654&password_conf=987654&Change=Change#,并进行抓包,可以发现没有referer,需要进行伪造,Referer: http://127.0.0.1/DVWA/vulnerabilities/csrf/ ,然后放包,即可。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,
High

在这个版本中,代码添加了以下改进:
- 添加了一个
checkToken()函数来验证Anti-CSRF令牌。它比较传递的用户令牌和会话令牌,如果不匹配,则可能是CSRF攻击,将重定向到指定的页面。 - 使用
stripslashes()函数对用户名和密码进行了处理,以防止反斜杠被添加。 - 增加了一个随机延迟,使用
sleep()函数和rand()函数,使登录失败的情况下的延迟时间在0到3秒之间随机选择,进一步增强了反爬虫功能。 - 添加了一个
generateSessionToken()函数来生成Anti-CSRF令牌,并将其存储在会话中。
这个前面步骤相同到这里

设置token参数:
跳转到Resource pool选项页面
修改线程为1,大于1可能会有问题,因为token是每次验证完后才会新生成token,所以不能使用多线程进行爆破

之后跳转到options界面:

点击add之后返回到payloads界面

还是根据长度来区分。
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 ""; // 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) . "
This account has been locked due to too many incorrect logins.
"; print "The timenow is: " . date ("h:i:s", $timenow) . "
"; print "The timeout is: " . date ("h:i:s", $timeout) . "
"; \*/ // 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
"; } } // 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 "
Welcome to the password protected area {$user}
"; echo "Warning: Someone might of been brute forcing your account.
"; echo "Number of login attempts: {$failed\_login}.
Last login attempt was at: {$last\_login}.
"; // 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(); ?>
Username and/or password incorrect.
Alternative, the account has been locked because of too many failed logins.
If this is the case, please try again in {$lockout\_time} minutes.
- 首先,代码检查是否已提交表单(
$_POST['Login']),以及username和password字段是否设置。如果满足这些条件,登录过程开始执行。 - 代码调用
checkToken函数来验证防跨站请求伪造(CSRF)令牌。它将表单中提交的令牌($_REQUEST['user_token'])与会话中存储的令牌($_SESSION['session_token'])进行比较。checkToken函数可能执行此比较,并在令牌不匹配时采取适当的措施。 - 代码对
username和password输入进行了净化处理,使用stripslashes和mysqli_real_escape_string函数来防止SQL注入攻击。但需要注意的是,mysqli_real_escape_string函数已被弃用,推荐使用预处理语句或参数化查询来代替。 - 代码设定了一些与登录失败和账户锁定相关的默认值。
- 代码查询数据库,检查用户是否存在以及账户是否因登录失败次数过多而被锁定。它从数据库中获取指定
username的failed_login和last_login值。 - 如果用户账户被锁定(即数据库中的
failed_login大于等于设定的失败登录次数),代码计算用户下次允许登录的时间。它通过将last_login时间与锁定时间间隔相加来计算。然后,它获取当前时间,并与计算出的时间进行比较。 - 代码再次查询数据库,检查
username和password是否匹配。它从users表中检索所有列,其中user列匹配提供的username,password列匹配提供的password的MD5哈希值。 - 如果登录有效(即查询返回一行)且账户未被锁定,代码获取用户的详细信息,如
avatar、failed_login和last_login。然后,它显示欢迎消息和用户的头像。 - 如果账户在上次登录后被锁定,代码显示警告消息、登录尝试次数和上次尝试的时间。
- 代码更新数据库中的
failed_login列,将失败登录计数重置为0。 - 如果登录无效(即查询返回0行)或账户被锁定,代码使用
sleep函数引入延迟(2到4秒),以减缓暴力破解尝试。然后,它向用户显示一条包含登录失败原因的消息。 - 代码再次更新数据库中的
failed_login列,将失败登录计数加1。 - 最后,代码更新数据库中的
last_login列,将其设置为当前时间,以记录用户的最后登录时间。 - 在处理登录表单之前,代码调用
generateSessionToken函数生成一个新的会话令牌,并将其存储在$_SESSION['session_token']中。这个令牌用于验证防止跨站请求伪造。
Command Injection
Low

这里让输入一个ip地址,写127.0.0.1
一片红的乱码,所以要打开靶场里面的dvwaPage.ini.php文件,在文件中ctrl+f,用搜索栏查找utf-8,将UTF-8改为GBK或者GB2312。
源码:

- 首先,代码检查是否已提交表单(
$_POST['Submit'])。只有在提交表单时才会执行后续的代码。 - 代码获取用户输入的目标IP地址(
$_REQUEST['ip'])。 - 代码使用
php_uname('s')函数确定操作系统类型。如果操作系统是Windows NT,则执行Windows系统的ping命令;否则,执行*nix系统的ping命令。 - 代码使用
shell_exec函数执行ping命令,并将结果赋给变量$cmd。shell_exec函数用于执行系统命令,并返回命令的输出。 - 最后,代码将ping命令的结果以预格式化文本的形式输出给用户,使用
echo语句和<pre>标签将结果包裹起来。
Medium

- 首先,代码检查是否已提交表单(
$_POST['Submit'])。只有在提交表单时才会执行后续的代码。 - 代码获取用户输入的目标IP地址(
$_REQUEST['ip'])。 - 代码定义了一个黑名单数组
$substitutions,其中包含一些特殊字符的替代项。这些特殊字符包括&&和;,它们通常用于命令注入攻击。 - 代码使用
str_replace函数将目标IP地址中的黑名单字符替换为空字符串。这样做的目的是移除用户输入中的特殊字符,以减少命令注入的风险。 - 代码使用
php_uname('s')函数确定操作系统类型。如果操作系统是Windows NT,则执行Windows系统的ping命令;否则,执行*nix系统的ping命令。 - 代码使用
shell_exec函数执行ping命令,并将结果赋给变量$cmd。这里改进的地方是,在目标IP地址经过过滤后再用于构造命令,减少了命令注入的可能性。 - 最后,代码将ping命令的结果以预格式化文本的形式输出给用户,使用
echo语句和<pre>标签将结果包裹起来。
这一个使用127.0.0.1&dir即可

High

这段代码是在之前改进的基础上进一步增强了安全性的PHP脚本。
- 首先,代码检查是否已提交表单(
$_POST['Submit'])。只有在提交表单时才会执行后续的代码。 - 代码获取用户输入的目标IP地址(
$_REQUEST['ip']),并使用trim函数去除输入字符串的首尾空格。 - 代码定义了一个黑名单数组
$substitutions,其中包含一些特殊字符的替代项。这些特殊字符包括&、;、|、-、空格、(、)、```和||,它们常用于命令注入和其他攻击。 - 代码使用
str_replace函数将目标IP地址中的黑名单字符替换为空字符串。这样做的目的是移除用户输入中的特殊字符,以进一步减少命令注入的风险。 - 代码使用
php_uname('s')函数确定操作系统类型。如果操作系统是Windows NT,则执行Windows系统的ping命令;否则,执行*nix系统的ping命令。 - 代码使用
shell_exec函数执行ping命令,并将结果赋给变量$cmd。这里同样注意到,在目标IP地址经过过滤后再用于构造命令,以减少命令注入的可能性。 - 最后,代码将ping命令的结果以预格式化文本的形式输出给用户,使用
echo语句和<pre>标签将结果包裹起来。
这个使用127.0.0.1|dir即可。
impossible

这段代码是在之前的基础上进一步增强了安全性的PHP脚本。
- 首先,代码检查是否已提交表单(
$_POST['Submit'])。只有在提交表单时才会执行后续的代码。 - 代码调用
checkToken函数来验证反跨站请求伪造(Anti-CSRF)令牌。该函数接受三个参数:用户提交的令牌($_REQUEST['user_token'])、会话中的令牌($_SESSION['session_token'])和重定向的页面(‘index.php’)。这样可以确保表单提交是合法的,并防止跨站请求伪造攻击。 - 代码获取用户输入的目标IP地址(
$_REQUEST['ip']),并使用stripslashes函数去除可能的反斜杠转义。 - 代码使用
explode函数将目标IP地址按照.分割成4个部分(octet)。 - 代码使用
is_numeric函数检查每个octet是否为数字,并且检查octet的数量是否为4。这样可以验证用户输入的IP地址是否符合预期的格式。 - 如果所有4个octet都是数字,并且octet的数量为4,则将IP地址重新组合。
- 代码使用
php_uname('s')函数确定操作系统类型。如果操作系统是Windows NT,则执行Windows系统的ping命令;否则,执行*nix系统的ping命令。 - 代码使用
shell_exec函数执行ping命令,并将结果赋给变量$cmd。这里同样注意到,在目标IP地址经过验证后再用于构造命令,以减少命令注入的可能性。 - 最后,代码将ping命令的结果以预格式化文本的形式输出给用户,使用
echo语句和<pre>标签将结果包裹起来。 - 如果用户输入的IP地址不符合预期的格式,代码将输出一个错误提示。
- 在代码的末尾,调用
generateSessionToken函数生成Anti-CSRF令牌,以便在下次请求时进行验证。
CSRF
Low

在输入要更改的密码后,有回显更改成功,在url里http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change# 可以分析出password_new是输入的密码,password_conf是确认的密码,说明我们在网站上输入的信息是会在url栏这里进行一个传输执行
在url里修改密码确认密码,在新标签页里进行访问http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=987654&password_conf=987654&Change=Change#
网站跳转,并且有修改成功之后的回显,说明攻击成功
源码:

-
首先,代码检查是否已提交表单(
$_GET['Change'])。只有在提交表单时才会执行后续的代码。 -
代码获取用户输入的新密码(
$_GET['password_new'])和确认密码($_GET['password_conf'])。 -
代码检查新密码和确认密码是否匹配。
-
如果新密码和确认密码匹配,代码执行以下操作:
- 使用
mysqli_real_escape_string函数对新密码进行转义,以防止SQL注入攻击。 - 使用
md5函数对新密码进行哈希处理,以增加密码的安全性。 - 构造一个SQL语句,将新密码更新到数据库中的
users表中,使用当前用户(dvwaCurrentUser())作为条件。 - 执行SQL语句,并将结果赋给变量
$result。 - 输出一个成功提示给用户。
- 使用
-
如果新密码和确认密码不匹配,代码输出一个密码不匹配的错误提示给用户。
-
在代码的末尾,使用
mysqli_close函数关闭数据库连接。
这段代码在处理密码更改时,对新密码进行了一些安全处理,如转义和哈希处理。然而,这段代码仍然存在一些安全风险:
- 使用
md5进行密码哈希处理已经不是最佳实践,推荐使用更安全的哈希算法,如bcrypt或Argon2。 - 代码中使用了
mysqli_real_escape_string对新密码进行转义,但更好的做法是使用参数化查询来处理SQL语句,以避免SQL注入攻击。 - 代码没有对输入进行验证,如密码长度、复杂度要求等。
Medium

-
首先,代码检查是否已提交表单(
$_GET['Change'])。只有在提交表单时才会执行后续的代码。 -
代码使用
stripos函数检查请求的来源是否包含服务器的域名($_SERVER['SERVER_NAME'])。这样可以验证请求是否来自于预期的源,以防止跨站请求伪造(CSRF)攻击。 -
如果请求的来源是预期的域名,代码执行以下操作:
- 获取用户输入的新密码(
$_GET['password_new'])和确认密码($_GET['password_conf'])。 - 检查新密码和确认密码是否匹配。
- 如果新密码和确认密码匹配,代码执行与之前相同的操作。
- 如果新密码和确认密码不匹配,代码输出一个密码不匹配的错误提示给用户。
- 获取用户输入的新密码(
-
如果请求的来源不是预期的域名,代码输出一个请求不正确的错误提示给用户。
-
在代码的末尾,使用
mysqli_close函数关闭数据库连接。
源码是通过referrer这个字段的参数进行判断的,通常情况下在增加referrer验证时就是网站本身当前页面的ip地址,通过抓包查看信息

在新的标签页中打开http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=987654&password_conf=987654&Change=Change#,并进行抓包,可以发现没有referer,需要进行伪造,Referer: http://127.0.0.1/DVWA/vulnerabilities/csrf/ ,然后放包,即可。

High

-
代码首先初始化一些变量,包括
$change(用于标记是否进行密码更改)、$request_type(请求类型,初始为"html")和$return_message(返回的消息,初始为"Request Failed")。 -
代码通过检查请求方法(
$_SERVER['REQUEST_METHOD'])和内容类型($_SERVER['CONTENT_TYPE'])来确定请求的类型。如果请求方法是POST且内容类型是JSON,则将请求类型设置为"json",并解析JSON数据。 -
如果是JSON请求类型,并且请求中包含了必要的参数(
HTTP_USER_TOKEN、password_new、password_conf和Change),则将相关数据赋值给相应的变量。 -
如果不是JSON请求类型,代码检查请求中是否包含了必要的参数(
user_token、password_new、password_conf和Change),如果存在,则将相关数据赋值给相应的变量。 -
如果满足进行密码更改的条件,代码执行以下操作:
- 检查防CSRF令牌的有效性,调用
checkToken函数进行验证。 - 检查新密码和确认密码是否匹配。
- 如果匹配,对新密码进行转义和哈希处理,然后更新数据库中的密码。
- 根据请求类型设置返回消息。
- 关闭数据库连接。
- 检查防CSRF令牌的有效性,调用
-
如果请求类型是JSON,代码生成新的防CSRF令牌,设置响应头的内容类型为JSON,并输出包含返回消息的JSON数据。然后退出程序。
-
如果请求类型不是JSON,代码生成新的防CSRF令牌,并以HTML格式输出返回消息。
可以看出high等级的主要区别是增加了一个token值的校验,每次登录都会校验token是否正确,若想要执行更改密码操作必须知道正常用户的token
因为该请求是get请求,所以token验证会被放在请求URL中,随便输入密码验证一下,可以看到,在请求的URL中最末尾加入了token
进行抓包,使用burpsuite中的从csrf token tracker
有了这个插件之后,每次重放这个数据包都会自动更新user_token
设置对应的参数,返回repeater模块即可修改成功。

impossible

-
代码首先检查是否存在
$_GET['Change']参数,以确定是否进行密码更改。 -
代码调用
checkToken函数来验证防CSRF令牌的有效性,使用$_REQUEST['user_token']和$_SESSION['session_token']作为参数。 -
代码获取输入的当前密码(
$_GET['password_current'])、新密码($_GET['password_new'])和确认密码($_GET['password_conf'])。 -
代码对当前密码进行处理,使用
stripslashes函数去除可能存在的反斜杠,然后使用mysqli_real_escape_string函数进行转义,最后使用md5函数进行哈希处理。 -
代码使用预处理语句和参数化查询来检查当前密码是否正确,查询数据库中与当前用户和当前密码匹配的记录数。
-
如果新密码和确认密码匹配,并且当前密码正确(记录数为1),代码执行以下操作:
- 对新密码进行处理,使用
stripslashes函数去除反斜杠,然后使用mysqli_real_escape_string函数进行转义,最后使用md5函数进行哈希处理。 - 使用预处理语句和参数化查询更新数据库中的密码。
- 输出一个密码已更改的消息给用户。
- 对新密码进行处理,使用
-
如果新密码和确认密码不匹配,或者当前密码不正确(记录数不为1),代码输出一个密码不匹配或当前密码不正确的消息给用户。
-
在代码的末尾,生成新的防CSRF令牌。
这个要求先知道旧密码才能改动。
File Inclusion
Low

可以直接查看文件

Medium

- 代码首先从
$_GET['page']获取要显示的页面。 - 代码使用
str_replace函数替换输入中的一些字符串,包括将"http://"和"https://"替换为空字符串,将"../"和"..\\"替换为空字符串。
可以通过输入绝对路径绕过

High

- 代码首先从
$_GET['page']获取要显示的页面。 - 代码使用条件语句进行输入验证。条件语句中使用了
fnmatch函数来比较输入的值是否匹配模式"file*",并且输入的值不等于"include.php"。 - 如果输入的值不匹配模式
"file*"且不等于"include.php",代码输出错误消息"ERROR: File not found!",然后使用exit函数终止脚本的执行。
输入?page=file4.php

impossible

- 代码首先从
$_GET['page']获取要显示的页面。 - 代码使用条件语句进行输入验证。条件语句中使用了多个逻辑运算符
&&来比较输入的值是否等于"include.php"、"file1.php"、"file2.php"或"file3.php"。 - 如果输入的值不等于上述任何一个值,代码输出错误消息
"ERROR: File not found!",然后使用exit函数终止脚本的执行。
File Upload
Low
没有过滤,可直接上传

Medium
if( isset( $_POST[ 'Upload' ] ) ) { ... }
这个条件语句检查是否有名为"Upload"的表单字段提交。它用于确定是否有文件上传请求。$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";$target_path变量指定上传文件的目标路径。它使用了一个名为DVWA_WEB_PAGE_TO_ROOT的常量,这个常量可能在其他地方定义了。$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
这一行将上传文件的基本名称附加到$target_path变量的末尾,以确定最终的目标文件路径。$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
这些变量分别存储上传文件的名称、类型和大小。它们从$_FILES数组中获取,$_FILES是PHP中用于处理文件上传的预定义变量。if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) { ... }
这个条件语句检查上传文件的类型和大小是否满足要求。只有当文件类型为JPEG或PNG,并且文件大小小于100,000字节(约100KB)时,才会执行条件语句块内的代码。if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { ... }
这个条件语句使用move_uploaded_file函数将上传的临时文件移动到目标路径。如果移动文件失败,则会执行条件语句块内的代码。echo '<pre>Your image was not uploaded.</pre>';
这行代码在文件上传失败时输出错误消息。echo "<pre>{$target_path} succesfully uploaded!</pre>";
这行代码在文件上传成功时输出成功消息,并显示上传后的文件路径。echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
这行代码在上传的文件类型不符合要求时输出错误消息。
对文件类型和大小做出了限制 必须是jpeg 或者 png
使用burpsuite抓包进行修改

High
if( isset( $_POST[ 'Upload' ] ) ) { ... }
这个条件语句检查是否有名为"Upload"的表单字段提交。它用于确定是否有文件上传请求。$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
这两行代码与之前的代码相同,用于指定上传文件的目标路径和最终的目标文件路径。$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
这些变量与之前的代码相同,用于存储上传文件的名称、扩展名、大小和临时文件路径。if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { ... }
这个条件语句进行了一些改进。它使用strtolower函数将上传文件的扩展名转换为小写,并检查扩展名是否为"jpg"、“jpeg"或"png”。它还检查文件大小是否小于100,000字节,并使用getimagesize函数验证文件是否为有效的图像文件。if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { ... }
这个条件语句与之前的代码相同,用于将上传的临时文件移动到目标路径。echo '<pre>Your image was not uploaded.</pre>';
这行代码在文件上传失败时输出错误消息。echo "<pre>{$target_path} succesfully uploaded!</pre>";
这行代码在文件上传成功时输出成功消息,并显示上传后的文件路径。echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
这行代码在上传的文件类型不符合要求时输出错误消息。
getimagesize(string filename)
函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。
可以看到,High级别的代码读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”*.jpg”所以制作图片马,将图片与木马合并为图片

impossible

checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
这行代码调用了一个名为checkToken的函数,用于验证Anti-CSRF令牌。它比较了来自请求的user_token和存储在会话中的session_token,以确保请求是合法的。$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
这些变量与之前的代码相同,用于存储上传文件的名称、扩展名、大小、类型和临时文件路径。$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
这些变量用于指定上传文件的目标路径、目标文件名和临时文件路径。目标文件名使用了一个唯一的文件名,以确保文件名的唯一性。if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) && ( $uploaded_size < 100000 ) && ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && getimagesize( $uploaded_tmp ) ) { ... }
这个条件语句进行了一些改进。它检查文件的扩展名、大小、类型和是否为有效的图像文件。只有当文件满足这些条件时,才会执行条件语句块内的代码。if( $uploaded_type == 'image/jpeg' ) { ... } else { ... }
这个条件语句根据上传文件的类型,使用imagecreatefromjpeg函数或imagecreatefrompng函数创建图像对象,并将图像重新编码为JPEG或PNG格式。这样可以去除图像中的任何元数据,增加安全性。if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { ... }
这个条件语句使用rename函数将临时文件移动到目标路径,并使用唯一的目标文件名。如果移动文件成功,则会执行条件语句块内的代码。echo "<pre><a href='{$target_path}{$target_file}'>{$target_file}</a> succesfully uploaded!</pre>";
这行代码在文件上传成功时输出成功消息,并显示上传后的文件链接。echo '<pre>Your image was not uploaded.</pre>';
这行代码在文件上传失败时输出错误消息。if( file_exists( $temp_file ) ) unlink( $temp_file );
这行代码用于删除临时文件。generateSessionToken();
这行代码调用了一个名为generateSessionToken的函数,用于生成Anti-CSRF令牌,并将其存储在会话中。
Impossible级别的代码对上传文件进行了重命名(为md5值,导致%00截断无法绕过过滤规则),加入Anti-CSRF token防护CSRF攻击,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。
SQL Injection
Low

首先在表单中输入1
输入2.
注入点判断,可知受到单引号闭合影响
直接把SQL语句中后续语句全部注释掉不执行,在判断语句后加#或者–+即可,全部注释掉就可以
语句1’ or 1=1#可以查出所有ID内容
找出注入点以及符号问题,然后判断字段
之后结合union联合查询

- 获取名为"id"的请求变量的值。
- 使用switch语句根据$_DVWA[‘SQLI_DB’]的值执行不同的数据库查询操作。
如果$_DVWA[‘SQLI_DB’]的值为MYSQL,它会执行以下操作:
- 构建一个SQL查询语句,从名为"users"的表中选择"first_name"和"last_name"列,条件是"user_id"等于$id的值。
- 使用mysqli_query函数执行查询,并将结果存储在$result变量中。
- 使用mysqli_fetch_assoc函数从$result中获取每一行的结果。
- 将每一行的"first_name"和"last_name"值存储在 f i r s t 和 first和 first和last变量中。
- 使用echo语句将 i d 、 id、 id、first和$last的值以HTML格式输出给用户。
如果$_DVWA[‘SQLI_DB’]的值为SQLITE,它会执行以下操作:
- 声明一个全局变量$sqlite_db_connection,用于存储SQLite数据库连接。
- 构建一个SQL查询语句,从名为"users"的表中选择"first_name"和"last_name"列,条件是"user_id"等于$id的值。
- 使用 s q l i t e _ d b _ c o n n e c t i o n − > q u e r y 函数执行查询,并将结果存储在 sqlite\_db\_connection->query函数执行查询,并将结果存储在 sqlite_db_connection−>query函数执行查询,并将结果存储在results变量中。
- 使用while循环从$results中获取每一行的结果。
- 将每一行的"first_name"和"last_name"值存储在 f i r s t 和 first和 first和last变量中。
- 使用echo语句将 i d 、 id、 id、first和$last的值以HTML格式输出给用户。
Medium
(1)判断注入类型 我们可以看到无法输入数字,所以我们进行抓包在bp中进行SQL注入


输入id=1’ and ‘1’='1看见报错了,输入id=1 and 1=1没有报错。
所以为数字类型
(2)判断列数

可知为两列
(3)判断显示位
可以知道是2
(4)判断数据库
(5)判断表名

(6)判断列名

输入users之后,发现没有如何反应
通过源代码我们可以知道,发现它对单引号进行了转义,我们采用16进制绕过,得知users的十六进制为 0x75736572
(7)获取数据

相比于low级别首先,它使用了mysqli_real_escape_string函数对$id变量进行了转义,以防止特殊字符对SQL查询造成影响。
接下来,它执行与之前相同的操作来获取用户信息并将其显示给用户。
在代码的最后,它执行了另一个查询来获取用户表中的行数,并将结果存储在$number_of_rows变量中。然后,它关闭了数据库连接。
high
首先判断为字符型注入
之后步骤与前面一样

-
它使用了 _ S E S S I O N [ ′ i d ′ ] 来获取用户的 I D ,而不是之前的 \_SESSION['id']来获取用户的ID,而不是之前的 _SESSION[′id′]来获取用户的ID,而不是之前的_POST[‘id’]。这意味着用户的ID是通过会话(session)来传递的,可能是在登录过程中设置的。
-
在SQL查询语句中,添加了LIMIT 1来限制结果只返回一行。这是一个好的做法,可以提高查询效率并减少返回结果的数量。
-
在关闭数据库连接的代码中,使用了((is_null( _ _ _ m y s q l i _ r e s = m y s q l i _ c l o s e ( \_\_\_mysqli\_res = mysqli\_close( ___mysqli_res=mysqli_close(GLOBALS[“___mysqli_ston”]))) ? false : $___mysqli_res)来关闭连接。这是因为之前的mysqli_close函数可能会返回一个布尔值,而不是void。这个改变没有直接的安全影响,只是一种不同的关闭连接的方式。
Impossible
**
**Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提高了安全性。
SQL Injection (Blind)
Low
与一般注入的区别是,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知。
输入1显示存在
输入1 and 1=1 或 1 and 1=2均显示存在
输入1’ and 1=1 #显示存在(这三种都显示存在)
输入1’ and 1=2 #不存在
说明存在注入
1.查数据库前要先判断数据库的长度
依次输入1’ and length(database())=x #(x为大于等于1的整数)
当显示存在时即为数据库长度
发现当x=4时显示存在,故数据库长度为4
2.二分法找数据库名
依次输入1’ and ascii(substr(databse(),1,1))>或<字母的ascii值 # 通过比较输入字母的ascii值的显示正常与否来逐个确定库名
3.找数据库中的表
首先确定数据库中表的数量
1’ and (select count (table_name) from information_schema.tables where table_schema=database())=x # (x为大于等于1的整数)
当显示存在时即可判断表的数量
最终当x=2显示存在即表的数量为2
然后确定表的长度
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=x #(x为大于等于1的整数)
当显示存在时即可判断表的长度
当x=9显示存在即表的长度为9
然后同样二分法确定表名
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>或<字母的ascii值 #
通过比较输入字母的ascii值的显示正常与否来逐个确定表名 步骤同第二步
4.找字段名
同上一步 先确定数量 再用二分法确定名称
sqlmap
使用sqlmap,用以下命令进行跑包:
python sqlmap.py -u “http://127.0.0.1/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit” --cookie “PHPSESSID=amv13uu59s3bk067v8fphftos0; security=low” --batch
–batch:在进行命令交互式的时候,默认选择为YES
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。





既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
如何自学黑客&网络安全
黑客零基础入门学习路线&规划
初级黑客
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k
到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?
如果你想要入坑黑客&网络安全,笔者给大家准备了一份:282G全网最全的网络安全资料包评论区留言即可领取!
7、脚本编程(初级/中级/高级)
在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力.
如果你零基础入门,笔者建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习;搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime;·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;·用Python编写漏洞的exp,然后写一个简单的网络爬虫;·PHP基本语法学习并书写一个简单的博客系统;熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选);·了解Bootstrap的布局或者CSS。
8、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
网络安全工程师企业级学习路线

如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的
视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

一些笔者自己买的、其他平台白嫖不到的视频教程。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
框架 (可选);·了解Bootstrap的布局或者CSS。
8、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
网络安全工程师企业级学习路线

如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的
视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

一些笔者自己买的、其他平台白嫖不到的视频教程。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-kwHoVAek-1712949856818)]
更多推荐



所有评论(0)