High

在这个版本中,代码添加了以下改进:

  1. 添加了一个checkToken()函数来验证Anti-CSRF令牌。它比较传递的用户令牌和会话令牌,如果不匹配,则可能是CSRF攻击,将重定向到指定的页面。
  2. 使用stripslashes()函数对用户名和密码进行了处理,以防止反斜杠被添加。
  3. 增加了一个随机延迟,使用sleep()函数和rand()函数,使登录失败的情况下的延迟时间在0到3秒之间随机选择,进一步增强了反爬虫功能。
  4. 添加了一个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 "

This account has been locked due to too many incorrect logins.
";         // 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) . "
";         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 " ";         // Had the account been locked out since last login?         if( $failed\_login >= $total\_failed\_login ) {             echo "

Warning: Someone might of been brute forcing your account.

";             echo "

Number of login attempts: {$failed\_login}.
Last login attempt was at: {$last\_login}.

";         }         // 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 "

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.
";         // 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(); ?>
  1. 首先,代码检查是否已提交表单($_POST['Login']),以及usernamepassword字段是否设置。如果满足这些条件,登录过程开始执行。
  2. 代码调用checkToken函数来验证防跨站请求伪造(CSRF)令牌。它将表单中提交的令牌($_REQUEST['user_token'])与会话中存储的令牌($_SESSION['session_token'])进行比较。checkToken函数可能执行此比较,并在令牌不匹配时采取适当的措施。
  3. 代码对usernamepassword输入进行了净化处理,使用stripslashesmysqli_real_escape_string函数来防止SQL注入攻击。但需要注意的是,mysqli_real_escape_string函数已被弃用,推荐使用预处理语句或参数化查询来代替。
  4. 代码设定了一些与登录失败和账户锁定相关的默认值。
  5. 代码查询数据库,检查用户是否存在以及账户是否因登录失败次数过多而被锁定。它从数据库中获取指定usernamefailed_loginlast_login值。
  6. 如果用户账户被锁定(即数据库中的failed_login大于等于设定的失败登录次数),代码计算用户下次允许登录的时间。它通过将last_login时间与锁定时间间隔相加来计算。然后,它获取当前时间,并与计算出的时间进行比较。
  7. 代码再次查询数据库,检查usernamepassword是否匹配。它从users表中检索所有列,其中user列匹配提供的usernamepassword列匹配提供的password的MD5哈希值。
  8. 如果登录有效(即查询返回一行)且账户未被锁定,代码获取用户的详细信息,如avatarfailed_loginlast_login。然后,它显示欢迎消息和用户的头像。
  9. 如果账户在上次登录后被锁定,代码显示警告消息、登录尝试次数和上次尝试的时间。
  10. 代码更新数据库中的failed_login列,将失败登录计数重置为0。
  11. 如果登录无效(即查询返回0行)或账户被锁定,代码使用sleep函数引入延迟(2到4秒),以减缓暴力破解尝试。然后,它向用户显示一条包含登录失败原因的消息。
  12. 代码再次更新数据库中的failed_login列,将失败登录计数加1。
  13. 最后,代码更新数据库中的last_login列,将其设置为当前时间,以记录用户的最后登录时间。
  14. 在处理登录表单之前,代码调用generateSessionToken函数生成一个新的会话令牌,并将其存储在$_SESSION['session_token']中。这个令牌用于验证防止跨站请求伪造。

Command Injection

Low

这里让输入一个ip地址,写127.0.0.1

一片红的乱码,所以要打开靶场里面的dvwaPage.ini.php文件,在文件中ctrl+f,用搜索栏查找utf-8,将UTF-8改为GBK或者GB2312。

源码:

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

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

这一个使用127.0.0.1&dir即可

High

这段代码是在之前改进的基础上进一步增强了安全性的PHP脚本。

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

这个使用127.0.0.1|dir即可。

impossible

这段代码是在之前的基础上进一步增强了安全性的PHP脚本。

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

网站跳转,并且有修改成功之后的回显,说明攻击成功

源码:

  1. 首先,代码检查是否已提交表单($_GET['Change'])。只有在提交表单时才会执行后续的代码。

  2. 代码获取用户输入的新密码($_GET['password_new'])和确认密码($_GET['password_conf'])。

  3. 代码检查新密码和确认密码是否匹配。

  4. 如果新密码和确认密码匹配,代码执行以下操作:

    • 使用mysqli_real_escape_string函数对新密码进行转义,以防止SQL注入攻击。
    • 使用md5函数对新密码进行哈希处理,以增加密码的安全性。
    • 构造一个SQL语句,将新密码更新到数据库中的users表中,使用当前用户(dvwaCurrentUser())作为条件。
    • 执行SQL语句,并将结果赋给变量$result
    • 输出一个成功提示给用户。
  5. 如果新密码和确认密码不匹配,代码输出一个密码不匹配的错误提示给用户。

  6. 在代码的末尾,使用mysqli_close函数关闭数据库连接。

这段代码在处理密码更改时,对新密码进行了一些安全处理,如转义和哈希处理。然而,这段代码仍然存在一些安全风险:

  • 使用md5进行密码哈希处理已经不是最佳实践,推荐使用更安全的哈希算法,如bcrypt或Argon2。
  • 代码中使用了mysqli_real_escape_string对新密码进行转义,但更好的做法是使用参数化查询来处理SQL语句,以避免SQL注入攻击。
  • 代码没有对输入进行验证,如密码长度、复杂度要求等。
Medium

  1. 首先,代码检查是否已提交表单($_GET['Change'])。只有在提交表单时才会执行后续的代码。

  2. 代码使用stripos函数检查请求的来源是否包含服务器的域名($_SERVER['SERVER_NAME'])。这样可以验证请求是否来自于预期的源,以防止跨站请求伪造(CSRF)攻击。

  3. 如果请求的来源是预期的域名,代码执行以下操作:

    • 获取用户输入的新密码($_GET['password_new'])和确认密码($_GET['password_conf'])。
    • 检查新密码和确认密码是否匹配。
    • 如果新密码和确认密码匹配,代码执行与之前相同的操作。
    • 如果新密码和确认密码不匹配,代码输出一个密码不匹配的错误提示给用户。
  4. 如果请求的来源不是预期的域名,代码输出一个请求不正确的错误提示给用户。

  5. 在代码的末尾,使用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

  1. 代码首先初始化一些变量,包括$change(用于标记是否进行密码更改)、$request_type(请求类型,初始为"html")和$return_message(返回的消息,初始为"Request Failed")。

  2. 代码通过检查请求方法($_SERVER['REQUEST_METHOD'])和内容类型($_SERVER['CONTENT_TYPE'])来确定请求的类型。如果请求方法是POST且内容类型是JSON,则将请求类型设置为"json",并解析JSON数据。

  3. 如果是JSON请求类型,并且请求中包含了必要的参数(HTTP_USER_TOKENpassword_newpassword_confChange),则将相关数据赋值给相应的变量。

  4. 如果不是JSON请求类型,代码检查请求中是否包含了必要的参数(user_tokenpassword_newpassword_confChange),如果存在,则将相关数据赋值给相应的变量。

  5. 如果满足进行密码更改的条件,代码执行以下操作:

    • 检查防CSRF令牌的有效性,调用checkToken函数进行验证。
    • 检查新密码和确认密码是否匹配。
    • 如果匹配,对新密码进行转义和哈希处理,然后更新数据库中的密码。
    • 根据请求类型设置返回消息。
    • 关闭数据库连接。
  6. 如果请求类型是JSON,代码生成新的防CSRF令牌,设置响应头的内容类型为JSON,并输出包含返回消息的JSON数据。然后退出程序。

  7. 如果请求类型不是JSON,代码生成新的防CSRF令牌,并以HTML格式输出返回消息。

可以看出high等级的主要区别是增加了一个token值的校验,每次登录都会校验token是否正确,若想要执行更改密码操作必须知道正常用户的token

因为该请求是get请求,所以token验证会被放在请求URL中,随便输入密码验证一下,可以看到,在请求的URL中最末尾加入了token

进行抓包,使用burpsuite中的从csrf token tracker

有了这个插件之后,每次重放这个数据包都会自动更新user_token

设置对应的参数,返回repeater模块即可修改成功。

impossible

  1. 代码首先检查是否存在$_GET['Change']参数,以确定是否进行密码更改。

  2. 代码调用checkToken函数来验证防CSRF令牌的有效性,使用$_REQUEST['user_token']$_SESSION['session_token']作为参数。

  3. 代码获取输入的当前密码($_GET['password_current'])、新密码($_GET['password_new'])和确认密码($_GET['password_conf'])。

  4. 代码对当前密码进行处理,使用stripslashes函数去除可能存在的反斜杠,然后使用mysqli_real_escape_string函数进行转义,最后使用md5函数进行哈希处理。

  5. 代码使用预处理语句和参数化查询来检查当前密码是否正确,查询数据库中与当前用户和当前密码匹配的记录数。

  6. 如果新密码和确认密码匹配,并且当前密码正确(记录数为1),代码执行以下操作:

    • 对新密码进行处理,使用stripslashes函数去除反斜杠,然后使用mysqli_real_escape_string函数进行转义,最后使用md5函数进行哈希处理。
    • 使用预处理语句和参数化查询更新数据库中的密码。
    • 输出一个密码已更改的消息给用户。
  7. 如果新密码和确认密码不匹配,或者当前密码不正确(记录数不为1),代码输出一个密码不匹配或当前密码不正确的消息给用户。

  8. 在代码的末尾,生成新的防CSRF令牌。

这个要求先知道旧密码才能改动。

File Inclusion

Low

可以直接查看文件

Medium

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

可以通过输入绝对路径绕过

High

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

输入?page=file4.php

impossible

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

File Upload

Low

没有过滤,可直接上传

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

对文件类型和大小做出了限制   必须是jpeg 或者 png

使用burpsuite抓包进行修改

High
  1. if( isset( $_POST[ 'Upload' ] ) ) { ... }
    这个条件语句检查是否有名为"Upload"的表单字段提交。它用于确定是否有文件上传请求。
  2. $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
    这两行代码与之前的代码相同,用于指定上传文件的目标路径和最终的目标文件路径。
  3. $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' ];
    这些变量与之前的代码相同,用于存储上传文件的名称、扩展名、大小和临时文件路径。
  4. 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函数验证文件是否为有效的图像文件。
  5. if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { ... }
    这个条件语句与之前的代码相同,用于将上传的临时文件移动到目标路径。
  6. echo '<pre>Your image was not uploaded.</pre>';
    这行代码在文件上传失败时输出错误消息。
  7. echo "<pre>{$target_path} succesfully uploaded!</pre>";
    这行代码在文件上传成功时输出成功消息,并显示上传后的文件路径。
  8. echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    这行代码在上传的文件类型不符合要求时输出错误消息。

getimagesize(string filename)
函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。
可以看到,High级别的代码读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”*.jpg”所以制作图片马,将图片与木马合并为图片

impossible

  1. checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    这行代码调用了一个名为checkToken的函数,用于验证Anti-CSRF令牌。它比较了来自请求的user_token和存储在会话中的session_token,以确保请求是合法的。
  2. $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' ];
    这些变量与之前的代码相同,用于存储上传文件的名称、扩展名、大小、类型和临时文件路径。
  3. $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;
    这些变量用于指定上传文件的目标路径、目标文件名和临时文件路径。目标文件名使用了一个唯一的文件名,以确保文件名的唯一性。
  4. 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 ) ) { ... }
    这个条件语句进行了一些改进。它检查文件的扩展名、大小、类型和是否为有效的图像文件。只有当文件满足这些条件时,才会执行条件语句块内的代码。
  5. if( $uploaded_type == 'image/jpeg' ) { ... } else { ... }
    这个条件语句根据上传文件的类型,使用imagecreatefromjpeg函数或imagecreatefrompng函数创建图像对象,并将图像重新编码为JPEG或PNG格式。这样可以去除图像中的任何元数据,增加安全性。
  6. if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { ... }
    这个条件语句使用rename函数将临时文件移动到目标路径,并使用唯一的目标文件名。如果移动文件成功,则会执行条件语句块内的代码。
  7. echo "<pre><a href='{$target_path}{$target_file}'>{$target_file}</a> succesfully uploaded!</pre>";
    这行代码在文件上传成功时输出成功消息,并显示上传后的文件链接。
  8. echo '<pre>Your image was not uploaded.</pre>';
    这行代码在文件上传失败时输出错误消息。
  9. if( file_exists( $temp_file ) ) unlink( $temp_file );
    这行代码用于删除临时文件。
  10. generateSessionToken();
    这行代码调用了一个名为generateSessionToken的函数,用于生成Anti-CSRF令牌,并将其存储在会话中。

Impossible级别的代码对上传文件进行了重命名(为md5值,导致%00截断无法绕过过滤规则),加入Anti-CSRF token防护CSRF攻击,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。

SQL Injection

Low

首先在表单中输入1

输入2.

注入点判断,可知受到单引号闭合影响

直接把SQL语句中后续语句全部注释掉不执行,在判断语句后加#或者–+即可,全部注释掉就可以

语句1’ or 1=1#可以查出所有ID内容

找出注入点以及符号问题,然后判断字段

之后结合union联合查询

  1. 获取名为"id"的请求变量的值。
  2. 使用switch语句根据$_DVWA[‘SQLI_DB’]的值执行不同的数据库查询操作。

如果$_DVWA[‘SQLI_DB’]的值为MYSQL,它会执行以下操作:

  1. 构建一个SQL查询语句,从名为"users"的表中选择"first_name"和"last_name"列,条件是"user_id"等于$id的值。
  2. 使用mysqli_query函数执行查询,并将结果存储在$result变量中。
  3. 使用mysqli_fetch_assoc函数从$result中获取每一行的结果。
  4. 将每一行的"first_name"和"last_name"值存储在 f i r s t 和 first和 firstlast变量中。
  5. 使用echo语句将 i d 、 id、 idfirst和$last的值以HTML格式输出给用户。

如果$_DVWA[‘SQLI_DB’]的值为SQLITE,它会执行以下操作:

  1. 声明一个全局变量$sqlite_db_connection,用于存储SQLite数据库连接。
  2. 构建一个SQL查询语句,从名为"users"的表中选择"first_name"和"last_name"列,条件是"user_id"等于$id的值。
  3. 使用 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变量中。
  4. 使用while循环从$results中获取每一行的结果。
  5. 将每一行的"first_name"和"last_name"值存储在 f i r s t 和 first和 firstlast变量中。
  6. 使用echo语句将 i d 、 id、 idfirst和$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

首先判断为字符型注入

之后步骤与前面一样

  1. 它使用了 _ S E S S I O N [ ′ i d ′ ] 来获取用户的 I D ,而不是之前的 \_SESSION['id']来获取用户的ID,而不是之前的 _SESSION[id]来获取用户的ID,而不是之前的_POST[‘id’]。这意味着用户的ID是通过会话(session)来传递的,可能是在登录过程中设置的。

  2. 在SQL查询语句中,添加了LIMIT 1来限制结果只返回一行。这是一个好的做法,可以提高查询效率并减少返回结果的数量。

  3. 在关闭数据库连接的代码中,使用了((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年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
img

如何自学黑客&网络安全

黑客零基础入门学习路线&规划

初级黑客
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、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
img

网络安全工程师企业级学习路线

img
如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的

视频配套资料&国内外网安书籍、文档&工具

当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

img
一些笔者自己买的、其他平台白嫖不到的视频教程。
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

框架 (可选);·了解Bootstrap的布局或者CSS。

8、超级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。
img

网络安全工程师企业级学习路线

img
如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的

视频配套资料&国内外网安书籍、文档&工具

当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

img
一些笔者自己买的、其他平台白嫖不到的视频教程。
img

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

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐