DVWA通关教程

[复制链接]
发表于 2026-1-26 01:57:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×


Brute Force

Low

先辈行一下代码审计
  1. <?php  
  2.   
  3. // 检查是否通过GET请求传递了'Login'参数(注意:这里应该是'username'或类似的,但代码逻辑有误)  
  4. if( isset( $_GET[ 'Login' ] ) ) {  
  5.     // 从GET请求中获取用户名  
  6.     $user = $_GET[ 'username' ]; // 注意:这里应该是'$_GET['username']',但键名写错了  
  7.   
  8.     // 从GET请求中获取密码,并使用MD5算法进行哈希  
  9.     $pass = $_GET[ 'password' ];  
  10.     $pass = md5( $pass ); // MD5已不被认为是安全的哈希算法,不应在新开发的应用中使用  
  11.   
  12.     // 准备SQL查询语句,用于从数据库中查找用户  
  13.     $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";  
  14.     // 执行SQL查询,使用全局变量$GLOBALS["___mysqli_ston"]作为数据库连接  
  15.     // 如果查询失败,则输出错误信息  
  16.     $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>' );  
  17.   
  18.     // 检查查询结果是否存在且仅有一行(即一个匹配的用户)  
  19.     if( $result && mysqli_num_rows( $result ) == 1 ) {  
  20.         // 从结果集中获取用户详细信息  
  21.         $row    = mysqli_fetch_assoc( $result );  
  22.         $avatar = $row["avatar"]; // 获取用户的头像URL  
  23.   
  24.         // 登录成功,输出欢迎信息和用户头像  
  25.         echo "<p>Welcome to the password protected area {$user}</p>";  
  26.         echo "<img src="{$avatar}" />";  
  27.     }  
  28.     else {  
  29.         // 登录失败,输出错误信息  
  30.         echo "<pre><br />Username and/or password incorrect.</pre>";  
  31.     }  
  32.   
  33.     // 关闭数据库连接(尽管这里使用了三元运算符,但实际上并没有对结果进行任何处理)  
  34.     ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);  
  35. }  
  36.   
  37. ?>
复制代码
先在这里说一下,这段php代码没有对输入举行任何的过滤,我们可以采取sql注入去爆破数据库登录,大概利用万能暗码 比如 admin or 1 = 1 (原理是or只要前后创建一个即可),这题必要用单引号举行闭合注入

打开BP,举行抓包,输入用户名和暗码(随意)

抓包分析,右键发送到intruder中



下面先容一下BP的爆破方式

Sniper

单参数爆破,多参数时利用同一个字典按次序更换各参数,只有一个数据会被更换
在password的变蓝的地方添加playload



在这里设置字典,可以看到payload是password的时间长度有变革,以是password就是暗码

Battering ram

  多参数同时爆破,但用的是同一个字典,每个参数数据都是划一的

我们可以看到没有一样的,分析没乐成
Pichfork

  多参数同时爆破,但用的是差别的字典,差别字典间数据逐行匹配

可以看到当username = admin 而且password= password 是长度和其他的都差别,以是纵然账户和暗码
Cluster bamb

多参数做笛卡尔乘积模式爆破

可以显着的看到长度不一样
Medium

Medium步调和low品级完全一样,重要是源码多了一点东西导致难度会进步一点,但无伤风雅,low品级能跑出来的Medium也能跑出来,接下来就让我们举行一下代码审计吧
  1. <?php  
  2.   
  3. // 检查是否通过GET请求提交了Login参数  
  4. if( isset( $_GET[ 'Login' ] ) ) {  
  5.     // 从GET请求中获取用户名,但这里应该使用POST而不是GET来传输敏感信息  
  6.     $user = $_GET[ 'username' ];  
  7.   
  8.     // 尝试对用户名进行转义,但依赖于全局变量___mysqli_ston,这不是一个好的做法  
  9.     // 更好的做法是将数据库连接作为参数传递给函数或方法  
  10.     $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)) ? "" : ""));  
  11.   
  12.     // 从GET请求中获取密码,同样应该使用POST  
  13.     $pass = $_GET[ 'password' ];  
  14.   
  15.     // 对密码进行转义,然后MD5加密。MD5不再被认为是安全的密码存储方式  
  16.     $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)) ? "" : ""));  
  17.     $pass = md5( $pass );  
  18.   
  19.     // 构造SQL查询语句,但直接将变量插入SQL语句中是不安全的(SQL注入风险)  
  20.     $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";  
  21.   
  22.     // 执行SQL查询,如果失败则显示错误信息  
  23.     $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>' );  
  24.   
  25.     // 检查查询结果是否存在且只有一行  
  26.     if( $result && mysqli_num_rows( $result ) == 1 ) {  
  27.         // 获取用户信息  
  28.         $row    = mysqli_fetch_assoc( $result );  
  29.         $avatar = $row["avatar"];  
  30.   
  31.         // 登录成功,但输出用户信息到HTML中可能导致XSS攻击  
  32.         echo "<p>Welcome to the password protected area {$user}</p>";  
  33.         echo "<img src="{$avatar}" />";  
  34.     }  
  35.     else {  
  36.         // 登录失败,延迟2秒响应  
  37.         sleep( 2 );  
  38.         // 输出错误信息  
  39.         echo "<pre><br />Username and/or password incorrect.</pre>";  
  40.     }  
  41.   
  42.     // 关闭数据库连接,但这里的错误处理是多余的  
  43.     ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);  
  44. }  
  45.   
  46. ?>
复制代码
High

high品级相较于Medium的变革是sleep(rand(0,3))函数随机休眠0到3秒,重点是增长了token值,这增长了难度,接下来看看我们怎么在有token验证的环境下得到精确的用户名和暗码
  1. <?php  
  2.   
  3. if( isset( $_GET[ 'Login' ] ) ) {  
  4.     // 检查是否存在CSRF令牌,并与会话令牌匹配,以防止跨站请求伪造  
  5.     // 假设checkToken函数已经定义,并接受请求令牌、会话令牌和失败时重定向的页面  
  6.     checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );  
  7.   
  8.     // 清理用户名输入中的反斜杠  
  9.     $user = $_GET[ 'username' ];  
  10.     $user = stripslashes( $user );  
  11.     // 使用mysqli_real_escape_string防止SQL注入,如果数据库连接存在  
  12.     $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)) ? "" : ""));  
  13.   
  14.     // 清理密码输入中的反斜杠  
  15.     $pass = $_GET[ 'password' ];  
  16.     $pass = stripslashes( $pass );  
  17.     // 使用mysqli_real_escape_string防止SQL注入,然后加密密码  
  18.     $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)) ? "" : ""));  
  19.     $pass = md5( $pass ); // 使用MD5加密密码,尽管这不是最佳实践(应使用更安全的哈希算法)  
  20.   
  21.     // 构造SQL查询语句  
  22.     $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";  
  23.     // 执行查询,如果失败则显示错误信息  
  24.     $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>' );  
  25.   
  26.     if( $result && mysqli_num_rows( $result ) == 1 ) {  
  27.         // 从结果中获取一行用户数据  
  28.         $row    = mysqli_fetch_assoc( $result );  
  29.         $avatar = $row["avatar"]; // 获取用户头像URL  
  30.   
  31.         // 登录成功  
  32.         echo "<p>Welcome to the password protected area {$user}</p>";  
  33.         echo "<img src="{$avatar}" />"; // 显示用户头像  
  34.     }  
  35.     else {  
  36.         // 登录失败,随机延迟0到3秒以减缓暴力破解攻击  
  37.         sleep( rand( 0, 3 ) );  
  38.         echo "<pre><br />Username and/or password incorrect.</pre>";  
  39.     }  
  40.   
  41.     // 关闭数据库连接(尽管这里的结果赋值操作可能不是最佳实践)  
  42.     ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);  
  43. }  
  44.   
  45. // 生成新的CSRF令牌并存储在会话中,以便下次请求时验证  
  46. generateSessionToken();  
  47.   
  48. ?>
复制代码

进入payload模块第一个选择字典本身添加,第二个选择递归查询

找到Grep-Extract :这个设置可以大概被用来通过哀求返回的信息来获取有效的信息供你利用,也就是说,可以通过它来得到每次哀求后返回的Token,关联到Payload中举行暴力破解



在Payload Options中就可以看到自动加载过来的值,将哀求包里user_token的值填入到Initial payload for first request(第一个哀求的初始负载)中,也可不填,点击Start attack开启爆破!

如下乐成了

Impossible

  1. <?php  
  2.   
  3. // 检查是否提交了登录表单  
  4. if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {  
  5.     // 检查Anti-CSRF令牌  
  6.     checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );  
  7.   
  8.     // 清理用户名输入(尽管在PDO中不需要,但可能是从旧代码迁移而来)  
  9.     $user = $_POST[ 'username' ];  
  10.     $user = stripslashes( $user ); // 移除反斜杠(在魔术引号开启时有用,但现代PHP中已废弃)  
  11.     // 尝试使用全局mysqli连接进行转义,否则报错  
  12.     $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)) ? "" : ""));  
  13.   
  14.     // 清理密码输入(同上)  
  15.     $pass = $_POST[ 'password' ];  
  16.     $pass = stripslashes( $pass );  
  17.     $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)) ? "" : ""));  
  18.     $pass = md5( $pass ); // 使用MD5哈希密码(不推荐,因为MD5不安全)  
  19.   
  20.     // 设置默认值和变量  
  21.     $total_failed_login = 3;  
  22.     $lockout_time       = 15;  
  23.     $account_locked     = false;  
  24.   
  25.     // 检查数据库中的用户信息  
  26.     $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );  
  27.     $data->bindParam( ':user', $user, PDO::PARAM_STR );  
  28.     $data->execute();  
  29.     $row = $data->fetch();  
  30.   
  31.     // 检查用户是否被锁定  
  32.     if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {  
  33.         // 计算用户何时可以重新登录  
  34.         $last_login = strtotime( $row[ 'last_login' ] );  
  35.         $timeout    = $last_login + ($lockout_time * 60);  
  36.         $timenow    = time();  
  37.   
  38.         // 如果时间未到,则账户被锁定  
  39.         if( $timenow < $timeout ) {  
  40.             $account_locked = true;  
  41.         }  
  42.     }  
  43.   
  44.     // 检查用户名和密码是否匹配  
  45.     $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );  
  46.     $data->bindParam( ':user', $user, PDO::PARAM_STR);  
  47.     $data->bindParam( ':password', $pass, PDO::PARAM_STR );  
  48.     $data->execute();  
  49.     $row = $data->fetch();  
  50.   
  51.     // 如果登录有效且账户未锁定  
  52.     if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {  
  53.         // 获取用户详细信息  
  54.         $avatar = $row[ 'avatar' ];  
  55.         // ...(其他用户信息)  
  56.   
  57.         // 登录成功  
  58.         echo "<p>Welcome to the password protected area <em>{$user}</em></p>";  
  59.         // ...(其他成功消息)  
  60.   
  61.         // 重置登录失败次数  
  62.         $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );  
  63.         $data->bindParam( ':user', $user, PDO::PARAM_STR );  
  64.         $data->execute();  
  65.     } else {  
  66.         // 登录失败  
  67.         sleep( rand( 2, 4 ) ); // 延迟响应,可能是为了简单的DoS保护  
  68.   
  69.         // 给出反馈  
  70.         echo "<pre><br />Usern
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表