当前位置: 首页 > news >正文

基于yakit的dvwa靶场暴力破解和代码执行漏洞

环境部署https://blog.csdn.net/2302_82189125/article/details/135834194

1.Brute Force

low

result

medium

同样的插入方法

high

爆破成功

但是有一个问题需要考虑,为什么要使用热加载,又为什么热加载要那样设置,而且要注释最后一行,接下来我们来分析一下

观察high跟前俩关的题目可以发现high难度多了一个参数 user_token,猜测存在csrf-token检验,所以使用热加载

用这个token的模板

接下来我们分析这个模板

1. 预请求获取页面 <font style="color:rgb(0, 0, 0);">rsp, _, err = poc.HTTP(``)</font> 发送一个GET请求(通常是登录页面),以获取包含最新Token的页面源码和会话Cookie。这是后续所有操作的基础。
2. 提取会话Cookie <font style="color:rgb(0, 0, 0);">cookie = poc.GetHTTPPacketHeader(rsp, "Set-Cookie")</font> 从预请求的响应头中拿到<font style="color:rgb(0, 0, 0);">Set-Cookie</font>值。这是维持会话状态的关键,确保后续登录请求被视为同一会话的一部分。
3. 解析HTML文档 <font style="color:rgb(0, 0, 0);">node, err = xpath.LoadHTMLDocument(rsp)</font> 将页面源码加载为一个可被XPath查询的文档对象模型(DOM),为精准定位Token输入框做准备。
4. 定位Token元素 <font style="color:rgb(0, 0, 0);">tokenNode = xpath.FindOne(node, "//input[@name='token']")</font> 使用XPath语法 <font style="color:rgb(0, 0, 0);">//input[@name='token']</font>在DOM树中查找属性<font style="color:rgb(0, 0, 0);">name</font><font style="color:rgb(0, 0, 0);">'token'</font>的输入框(<font style="color:rgb(0, 0, 0);"><input></font>)元素。在DVWA中,这个Token的名称通常是 **<font style="color:rgb(0, 0, 0);">user_token</font>**,所以这里的XPath可能需要调整为 <font style="color:rgb(0, 0, 0);">//input[@name='user_token']</font>才能正确抓取
5. 获取Token值 <font style="color:rgb(0, 0, 0);">token = xpath.SelectAttr(tokenNode, "value")</font> 从找到的Token输入框元素中提取其<font style="color:rgb(0, 0, 0);">value</font>属性的值,这就是我们需要的、当前有效的Token字符串。
6. 替换请求参数 <font style="color:rgb(0, 0, 0);">req = req.ReplaceAll("__TOKEN__", token)</font> 将主登录请求数据包中的占位符 <font style="color:rgb(0, 0, 0);">__TOKEN__</font>替换为刚刚获取到的真实Token值。
7. 更新请求Cookie <font style="color:rgb(0, 0, 0);">req = poc.AppendHTTPPacketHeader(req, "Cookie", cookie)</font> 将主登录请求的<font style="color:rgb(0, 0, 0);">Cookie</font>头更新为最新获取的会话Cookie,保持会话连续性。

这里为什么要注释最后一行呢,我们来看看不注释最后一行会发生什么

会发现25个包全部302了

<font style="color:rgb(0, 0, 0);">req = poc.AppendHTTPPacketHeader(req, "Cookie", cookie)</font>的逻辑是获取页面的set-cookie然后赋值给cookie,建立新的会话

让我们来看看正常的请求包

但其实正常的响应包是没有set-cookie这个头的,所以这个模板拿不到cookie,就会尝试把新的cookie置空或者是更改格式之类 导致了错误 导致重定向

就出现了set-cookie让我们重新建立会话

让我们把最后一行代码改成

if cookie != "" && cookie != nil { req = poc.AppendHTTPPacketHeader(req, "Cookie", cookie) }

就是在cookie不等于空的时候再进行cookie的替换操作

发现成功爆破了密码出来

Set-Cookie的触发条件

服务器通常只在以下情况下返回<font style="color:rgb(0, 0, 0);">Set-Cookie</font>

  • 创建新会话时
  • 会话过期需要刷新时
  • 用户登录/注销时
  • 安全策略要求更新会话时

但事实上还是发现会出现有一部分302 一部分200的情况

如果正确密码响应302了就会出现爆破失败的情况

这个是因为yakit高速请求之后服务端可能有时候会话失效的情况,解决这个可以降低并发线程

经测试 线程20降到5的时候 25个包会有3个失效 已经降到很低了 很大的改良了出错的情况 但是还是会出错,所以最好的解决方案是进行多次尝试

源码分析

<?php
// 检查是否提交了登录请求(是否点击了Login按钮)
if( isset( $_GET[ 'Login' ] ) ) {// Get username$user = $_GET[ 'username' ]; // 直接获取用户输入的用户名,未做任何过滤// Get password$pass = $_GET[ 'password' ]; // 直接获取用户输入的密码$pass = md5( $pass ); // 对密码进行MD5哈希加密(但数据库中存储的很可能就是MD5值,直接对比)// Check the database// 关键问题:直接将用户输入拼接到SQL查询语句中,存在SQL注入漏洞$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 successfulecho "<p>Welcome to the password protected area {$user}</p>";echo "<img src=\"{$avatar}\" />"; // 显示欢迎信息和头像}else {// Login failedecho "<pre><br />Username and/or password incorrect.</pre>"; // 登录失败提示}// 关闭数据库连接((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
if( isset( $_GET[ 'Login' ] ) ) {// Get username$user = $_GET[ 'username' ]; // 直接获取用户输入的用户名,未做任何过滤// Get password$pass = $_GET[ 'password' ]; // 直接获取用户输入的密码$pass = md5( $pass );

这一段代码读取传入的值

 $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>' );

$query = "SELECT * FROMusers WHERE user = '$user' AND password = '$pass';";查询数据库中有没有符合传入的值的数据

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>' );

<font style="color:rgb(0, 0, 0);">$GLOBALS["___mysqli_ston"]</font>这个变量是 DVWA 特有的

这个变量是 DVWA 为了方便内部数据库连接管理而定义的。DVWA 将数据库连接对象存储在 PHP 的 <font style="color:rgb(0, 0, 0);">$GLOBALS</font>超全局数组中,并命名为 <font style="color:rgb(0, 0, 0);">"___mysqli_ston"</font>。这样,在DVWA的不同脚本和函数中,都能通过这个全局变量名来访问同一个数据库连接,而不需要每次都重新创建连接或传递连接对象

不需要每次都创建连接,节省了成本

 // 检查查询结果是否恰好有一条记录if( $result && mysqli_num_rows( $result ) == 1 ) {// Get users details$row    = mysqli_fetch_assoc( $result ); // 获取查询结果数组$avatar = $row["avatar"]; // 从结果中获取头像路径// Login successfulecho "<p>Welcome to the password protected area {$user}</p>";echo "<img src=\"{$avatar}\" />"; // 显示欢迎信息和头像}else {// Login failedecho "<pre><br />Username and/or password incorrect.</pre>"; // 登录失败提示}

这一段检验提交上去的账密是否存在

通过mysqli_fetch_assoc获取这个用户的所有数据,用于返回在页面上

剩下就是一些欢迎 跟失败提示

 ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}

实现数据库的断开

所以爆破就是碰撞数据库中的账密

<?phpif( 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$html .= "<p>Welcome to the password protected area {$user}</p>";$html .= "<img src=\"{$avatar}\" />";}else {// Login failedsleep( 2 );$html .= "<pre><br />Username and/or password incorrect.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>
	$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 );

相比low.php只有这里有区别 先校验了全局变量是否存在 然后把用户提交数据进行mysqli_real_escape_string处理 过滤了',防御了一定的sql注入,后面是一个抛出错误

mysqli_real_escape_string转义的相关字符如下

被转义的字符 含义 转义为 转义目的
**<font style="color:rgb(0, 0, 0);">'</font>**(单引号) 字符串分隔符 <font style="color:rgb(0, 0, 0);">\'</font> 防止提前终止字符串,插入恶意代码
**<font style="color:rgb(0, 0, 0);">"</font>**(双引号) 字符串分隔符(若数据库使用双引号) <font style="color:rgb(0, 0, 0);">\"</font> 同上
``(反斜线) 转义字符本身 <font style="color:rgb(0, 0, 0);">\\</font> 防止转义字符自身被误解,确保其作为普通字符
**<font style="color:rgb(0, 0, 0);">NULL</font>**(ASCII 0) 字符串结束符 <font style="color:rgb(0, 0, 0);">\0</font> 防止在某些特定情况下被截断
**<font style="color:rgb(0, 0, 0);">\n</font>**(换行符) 换行 <font style="color:rgb(0, 0, 0);">\n</font> 避免被解释为命令的一部分
**<font style="color:rgb(0, 0, 0);">\r</font>**(回车符) 回车 <font style="color:rgb(0, 0, 0);">\r</font> 同上
**<font style="color:rgb(0, 0, 0);">Control-Z</font>**(ASCII 26) DOS文件结束符 <font style="color:rgb(0, 0, 0);">\Z</font> 防止在Windows系统等中被误认为文件结束
	else {// Login failedsleep( 2 );$html .= "<pre><br />Username and/or password incorrect.</pre>";}

这里还多了一句 如果登陆失败就sleep2秒 无伤大雅

<?phpif( isset( $_GET[ 'Login' ] ) ) {// Check Anti-CSRF tokencheckToken( $_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 successfulecho "<p>Welcome to the password protected area {$user}</p>";echo "<img src=\"{$avatar}\" />";}else {// Login failedsleep( 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();?> 

加了csrftoken的校验

<font style="color:rgb(0, 0, 0);">stripslashes()</font>反转义函数防御sql注入

Command Injection

由于windows部署dvwa 以whoami或calc执行成功为标志

low
  • <font style="color:rgb(0, 0, 0);">;</font>:按顺序执行多条命令。
  • <font style="color:rgb(0, 0, 0);">&&</font>:只有前一个命令成功才执行后一个。
  • <font style="color:rgb(0, 0, 0);">|</font>:将前一个命令的输出作为后一个命令的输入。
  • <font style="color:rgb(0, 0, 0);"><font style="color:rgb(0, 0, 0);">或 </font>$()`:命令替换。

源码

<?phpif( isset( $_POST[ 'Submit' ]  ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping  ' . $target );}else {// *nix$cmd = shell_exec( 'ping  -c 4 ' . $target );}// Feedback for the end userecho "<pre>{$cmd}</pre>";
}?> 

linux ping -c 4是为了防止一直ping下去

medium
<?phpif( isset( $_POST[ 'Submit' ]  ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Set blacklist$substitutions = array('&&' => '',';'  => '',);// Remove any of the charactars in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping  ' . $target );}else {// *nix$cmd = shell_exec( 'ping  -c 4 ' . $target );}// Feedback for the end userecho "<pre>{$cmd}</pre>";
}?>

过滤了&&``;

还是|即可

high

<?phpif( isset( $_POST[ 'Submit' ]  ) ) {// Get input$target = trim($_REQUEST[ 'ip' ]);// Set blacklist$substitutions = array('&'  => '',';'  => '','| ' => '','-'  => '','$'  => '','('  => '',')'  => '','`'  => '','||' => '',);// Remove any of the charactars in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping  ' . $target );}else {// *nix$cmd = shell_exec( 'ping  -c 4 ' . $target );}// Feedback for the end userecho "<pre>{$cmd}</pre>";
}?>

细看并没有过滤的|过滤的是| ,所以依旧|即可

http://www.hskmm.com/?act=detail&tid=35394

相关文章:

  • 视觉和语言-港科大 NMPC 控制下的高效自主导航!SkyVLN:城市环境无人机视觉语言导航与非线性模型预测控制 - MKT
  • 北航高低无人机协同导航方案:高空掌全局+低空查细节 - MKT
  • sourcetree 克隆项目仓库地址,输入账号密码后提示:这是一个无效的源路径/URL
  • 软工第三次作业-结对作业
  • 20251020 之所思 - 人生如梦
  • Conda、pip以及虚拟环境在Jupyter中的打开方法
  • 以太坊账⼾模型的理解,合约账⼾、EOA账⼾认识
  • Luogu P12376「LAOI-12」Calculate 题解 [ 蓝 ] [ 贪心 ] [ 线性 DP ] [ 前缀和优化 ] [ 范德蒙德卷积 ]
  • 方格图路径计数 dp 的反射路径优化
  • 每日反思(2025_10_20)
  • java基础9-面向对象进阶
  • 企业信息化建设的钱都花在哪儿了?
  • 身份运算符
  • 位运算符
  • 关系运算符
  • 赋值运算符
  • 算术运算符
  • Inno Setup 打包脚本模板
  • LCR 155. 将二叉搜索树转化为排序的双向链表
  • 解释这些区块链核⼼概念:区块、交易、Merkle Tree、共识机制(PoW、PoS)、Gas Fee 原理1
  • Claude code cli 的think mode到底是啥?
  • 【VM虚拟机共享主机代理】2025年10月20日可以pass的一些配置
  • 玄机——Linux后门应急
  • 2025/10/20
  • UI弹窗遮罩屏蔽触发事件的处理
  • newDay13
  • 小整数的地址
  • 概率论
  • 一次XFS死锁问题分析
  • P11150 [THUWC 2018] 字胡串