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

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

会发现25个包全部302了

req = poc.AppendHTTPPacketHeader(req, "Cookie", cookie)的逻辑是获取页面的set-cookie然后赋值给cookie,建立新的会话

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

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

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

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

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

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

发现成功爆破了密码出来

Set-Cookie的触发条件

服务器通常只在以下情况下返回Set-Cookie

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

但事实上还是发现会出现有一部分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 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);
}
?>
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>' );

$GLOBALS["___mysqli_ston"]这个变量是 DVWA 特有的

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

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

 // 检查查询结果是否恰好有一条记录
    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
        echo "<pre><br />Username and/or password incorrect.</pre>"; // 登录失败提示
    }

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

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

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

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

实现数据库的断开

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

<?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
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed
		sleep( 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转义的相关字符如下

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

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

<?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();

?> 

加了csrftoken的校验

stripslashes()反转义函数防御sql注入

Command Injection

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

low
  • ;:按顺序执行多条命令。
  • &&:只有前一个命令成功才执行后一个。
  • |:将前一个命令的输出作为后一个命令的输入。
  • ```或 $():命令替换。

源码

<?php

if( 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 user
    echo "<pre>{$cmd}</pre>";
}

?> 

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

medium
<?php

if( 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 user
    echo "<pre>{$cmd}</pre>";
}

?>

过滤了&&``;

还是|即可

high

<?php

if( 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 user
    echo "<pre>{$cmd}</pre>";
}

?>



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

posted @ 2025-10-21 01:03  fortune_h2c  阅读(9)  评论(0)    收藏  举报