
1. 项目概述与核心症结先别急着打开靶场咱们先把这道Supersqli的“脾气”摸清楚。攻防世界WEB区的Supersqli在圈内还有个外号叫“胎教版习题”意思是哪怕你刚接触SQL注入只要跟着思路走一遍就能把堆叠注入、预编译绕过和参数干扰这几座大山一次翻过去。我第一次做这题的时候心态是比较炸的。常规的union select打过去一脸懵。后来仔细复盘才发现这题的难点根本不是“会不会注入”而是“注入之后能不能拿到你要的数据”。官方环境给出的提示很明确一个输入框让你“试试注入”没有任何额外的过滤规则说明。但当你真的提交了id1页面正常回显提交id1报错信息直接甩你脸上——这时候你才发现SQL语句已经拼接到了一个可以控制的结构里。这道题适合什么人如果你已经能独立完成sqli-labs前20关或者做过靶场里中级难度的注入题那么这道题能帮你把“堆叠注入”这个听起来高大上、实战却不怎么用得上但竞赛爱考的技术彻底吃透。如果你是纯小白刚学会and 11、or 11那也没关系跟着后面的步骤一步步来你也能看懂为什么预编译能“封死”你的常规注入。简单概括这题的解题链条发现注入点 - 尝试联合注入被拦截 - 确认堆叠注入可用 - 利用handler语法绕开select过滤 - 拿到flag。每一个环节都有坑但每一个坑都对应一个明确的绕过思路这就是“胎教”二字的由来——你不需要会什么花里胡哨的骚操作只需把SQL语法本身玩明白。2. 解题环境准备与靶场结构分析2.1 环境与工具链清单开始动手前把工具准备好。我习惯用的是Chrome浏览器Burp Suite Community版一个本地的SQL字典。Burp在这道题里不是必需品但如果你想观察请求包和响应包的细节尤其是报错信息的完整返回Burp的Repeater模块会非常方便。如果你嫌麻烦直接用浏览器地址栏改参数也能做——只要你能忍受来回切换输入框的麻烦。工具用途推荐指数Chrome DevTools快速查看请求与响应修改参数重放五星Burp Suite抓包改包观察完整报错回显四星HackBar浏览器插件快速构造并发送注入payload四星本地终端curl备用方案适合命令行爱好者三星环境上攻防世界的靶场是网页版不需要本地搭建任何虚拟机或Docker容器。唯一要注意的是访问靶场前记得确认账号能正常登录且目标题目处于“开启”状态。有些题目在非比赛期间可能需要重新启动环境如果打开页面显示超时或404先去个人中心确认环境状态。2.2 靶场页面结构拆解这个题的页面极简一个输入框一个提交按钮没有任何其他提示。但千万别小看这个输入框它的参数名是id这意味着后端大概率是从GET请求中取id参数直接拼接进SQL语句里。用Burp看一眼请求包确认参数传递方式这是做注入题的基本素养。很多新手上来就在输入框里一顿乱试其实更好的做法是先发送最基础的id1和id2观察回显差异。如果两个不同的id返回不同的查询结果说明后端确实在做数据库查询且参数位置在WHERE子句后面。这时候你再判断这是一个数字型注入还是字符型注入输入id1如果页面报错说明参数是字符型且单引号被直接拼进了SQL导致语法错误。从报错信息里你还能观察到后端使用的数据库类型和SQL语句的大致结构。很多情况下报错信息会把完整的SQL语句片段带出来这就省去了不少盲猜的时间。Supersqli这个题报错信息中会直接出现SQL语法相关的内容仔细观察就能锁定数据库是MySQL——这为后续选择注入技术提供了方向。3. 从联合注入失败到堆叠注入的转折点3.1 联合注入被毙的完整测试过程拿id1正常注入验证后我习惯先测order by判断字段数量。输入id1 order by 2--页面正常回显改成order by 3页面报错。这说明当前查询只有2个字段。很多文章到这里就直接告诉你“union select 1,2”但实际测试中你会发现id1 union select 1,2--会返回一个非常有意思的拦截提示——return a regular select或者类似的过滤信息。这里必须停下来想一想为什么order by没被过滤union select却被拦截了说明后端不是简单地黑名单过滤关键词而是对正则表达式做了匹配锁死了select这个关键字。有的版本过滤规则是/select|union/i有的会更严格把from、where也一并过滤。无论哪种都说明常规的联合注入路径已经走不通了。那order by为什么能过因为order by是排序语法不涉及select所以正则没匹配上。这个细节暗示我们后端过滤的核心是select关键字而不是所有SQL关键字。知道这一点就有了突破口——只要我的查询操作不出现select是不是就能绕过3.2 堆叠注入验证与原理阐述堆叠注入简单说就是在一句SQL语句结束后用分号;隔开继续执行下一句SQL。比如后端原本执行的是select * from articles where id1我们提交1;show tables;--最终后端执行的其实是select * from articles where id1;show tables;-- 这里的关键是后端是否使用了支持多语句执行的API。PHP的mysqli_query()不支持多语句但PDO或者mysqli_multi_query()支持。这个题既然能出堆叠注入说明后端用的就是支持多语句的数据库接口。实测下来提交id1;show tables;--页面会直接返回两张表的名称1919810931114514和words。看到这种带数字的表名第一反应就是“flag藏在数字表里”但怎么把数据取出来才是难点。堆叠注入的优势在于它能执行任意的SQL语句包括show、set、update、insert等而常规注入只能执行查询。但它的劣势也很明显你无法通过union直接控制回显位置也就是说查询结果不一定能显示到页面上。这就导致了后面你必须想尽办法把目标数据“搬运”到一个能回显的查询里。提示堆叠注入并非所有数据库都支持SQL Server、PostgreSQL和MySQL部分API支持但Oracle和SQLite在默认情况下通常不允许。实战中遇到报错“You have an error in your SQL syntax”但分号前面的语句不报错大概率就是支持多语句的。3.3 绕不过的select过滤与关键词黑名单既然堆叠注入可用那我能不能直接在堆叠里select数据实测id1;select * from 1919810931114514;--依旧被拦截。这里值得仔细琢磨过滤规则select被过滤但show没被过滤说明过滤规则不是“去掉SQL关键词”而是“检测到select就不给过”。有的版本过滤规则是/select|update|delete|insert|where/i还有可能把information_schema也过滤了。遇到这种情况第一反应是大小写混淆SeLeCt第二反应是编码绕过%73elect实测下来在这个题里都没用——因为过滤规则用的是正则的i修饰符忽略大小写且直接在请求参数层做匹配没有解码过程。那怎么办思路有几个一是不用select改用MySQL特有的handler语法读数据二是用alter table配合rename把目标表改名为一个不回显select的新表再让系统内置的select * from words去查询它。这两条路是这个题的两种正统解法也是“胎教”的精华部分。4. 核心解法一handler语句读表4.1 handler语法基础如果你没接触过handler会觉得很陌生。它是MySQL存储引擎层的一个底层接口语法作用类似于select但不太为人熟知因此在很多黑名单过滤的SQL注入题里handler往往能绕过那些只过滤select的规则。基本用法如下handler TableName open; -- 打开一张表 handler TableName read first; -- 读取第一行数据 handler TableName read next; -- 依次读取后续行 handler TableName close; -- 关闭表handler能读取表中所有字段的数据且不会触发select关键词的过滤。这在CTF题目里是一个极其好用的绕过技巧甚至在一些真实的代码审计里如果开发者没有禁用handler攻击者同样能借它读取敏感数据。4.2 完整payload构造与执行过程回到题目我们需要读取1919810931114514这张表里的数据。提交参数id1;handler 1919810931114514 open;handler 1919810931114514 read first;--注意表名是纯数字MySQL要求纯数字表名必须用反引号包裹否则会报语法错误。这里还有一个细节分号后面的语句是连续执行的所以payload里可以写多条语句用分号分隔。提交后页面上会直接返回表里第一行数据通常就是flag。如果flag所在行不是第一行那就继续执行id1;handler 1919810931114514 open;handler 1919810931114514 read next;--read next每执行一次就会读取下一行。持续刷新提交直到把整张表读完。大多数情况下flag就在第一行这步操作一次就够。注意如果提交handler语句后页面返回“Table 1919810931114514 doesnt exist”之类的错误先检查反引号是否用对了再确认表名是否完全一致。数字表名的反引号是很多新手踩坑的第一站我看到不下十个选手在这卡了半小时。4.3 为什么handler能绕过滤原理深挖核心原因是过滤规则写得太“表面”。后端只是用正则匹配了输入参数中是否存在select如果存在就拒绝而handler这个词和select八竿子打不着自然就放行了。这就像小区保安只查身份证你拿驾照也能进——漏洞不在于你能伪造身份而是保安的检查清单本身就不完整。但这个技巧在实战中也有局限handler只能用于读取表数据不能用于判断表结构不能配合where条件灵活过滤。如果题目要求从两张表里关联查询handler就抓瞎了。所以CTF里handler通常是备用钥匙不是万能钥匙。5. 核心解法二alter table改表名绕过5.1 另辟蹊径的思路解析如果你觉得handler太“非主流”想着用更常规的方式拿到flag那么第二条路就是改表名。后端的初始查询大概率是这样的select * from words where id 输入的内容这里的words表是页面本身要查询的表它的字段有id和data。而flag藏在1919810931114514表里且words表没有flag字段。如果我们能通过堆叠注入执行alter table语句把flag表改名为words再把原来的words表改成别的名字那么原来的select * from words where id就会去查flag表——flag自然就回显到页面上了。5.2 三步搬运法详细步骤第一步把原words表改名为words2或者其他不存在冲突的表名id1;alter table words rename to words2;--第二步把flag表改名为wordsid1;alter table 1919810931114514 rename to words;--第三步提交一个普通的查询id1 union select 1,2-- 不这会被过滤等等这里要修正一下。执行完改名后id1就能正常查询到flag了吗不一定。因为flag表只有一列flag字段而原查询是select * from words两个字段。如果表的列数不一致select *不会报错但页面回显可能出现只有一列数据的情况不够直观更稳妥的做法是改名后直接用id1 union select 1,flag字段名 from words--——但select被过滤了这条路又断了。所以这道题里alter route通常配合一个技巧既然select被过滤那我们可以把flag表的字段也改名为id让回显更可控。实测中更简单粗暴的做法是直接将flag表改名为words然后正常提交id1让后端执行select * from words where id1此时words已经是flag表虽然字段名对不上但flag数据会以文本形式输出在页面上。不同攻防世界版本的回显机制有细微差异如果id1查不到就试id0或id1 --。5.3 注意事项与改动风险改表名操作属于破坏性操作一旦执行靶场环境就被“污染”了。解题完毕后如果想重新测试最好重置环境。另外当表名和字段名都是纯数字时rename语句里也必须加反引号id1;alter table 1919810931114514 rename to words;--不加反引号会导致SQL语法错误页面直接500。这个坑非常隐蔽因为报错信息里显示的SQL片段可能不完整新手容易误判为“payload被过滤了”实际上只是语法问题。实操心得我建议先把两种解法都在草稿纸上走一遍逻辑再动手操作。alter table的思路虽然符合常规SQL直觉但执行过程比较“脏”handler的思路干净利落不用改表我自己更推荐。6. 常见问题与排查技巧实录6.1 经典报错与对应解决方案我把实操中遇到频率最高的问题列了个表方便你直接对照排查现象可能原因解决方案id1后页面空白或500字符型注入语句被截断确认闭合符尝试id1 --或id1#union select被拦截select关键词过滤切换handler或alter table思路handler打开表时报错“doesnt exist”表名反引号缺失/表名拼写错误加上反引号确认表名与show tables一致执行alter table后id1无回显表列数不一致或字段名变化尝试id0、id1或先查看新表结构注入后页面显示SQL语法错误堆叠语句分号位置错误检查每条语句是否用分号正确分隔末尾加--使用#注释但请求无效特殊字符被URL编码错误使用--或%23即#的编码6.2 为什么用--而不是#或--这个看似细节的问题其实很关键。#是MySQL注释符但在URL传输中#会被浏览器当作锚点截断导致后续payload丢失。所以要在URL中传#必须写成%23。而--中--是标准SQL注释符在URL解码后变成空格组成完整的--注释符。实操里我更常用--因为在GET请求里不用额外编码直接粘贴就能生效。如果你用Burp的Repeater或命令行curl#的编码也完全没问题但要注意HTTP请求中的字符编码一致性。6.3 信息收集阶段的几个小技巧在正式解题前多花两分钟做信息收集能省不少时间。利用堆叠注入可以执行一系列show语句比如id1;show databases;--查看当前有哪些数据库。虽然页面回显不一定把所有结果都列全但至少能看到库名。下一步id1;show tables;--这是解题最关键的一步能看到所有表名。如果页面回显了不止两张表先思考哪张表的命名更像flag容器。数字表名、随机字符串表名、flag直接作为表名都是CTF惯用套路。show columns from 表名也可以做但需要注意from和表名的组合关系数字表名同样需要反引号id1;show columns from 1919810931114514;--6.4 如何判断过滤规则的具体内容遇到过滤时不要瞎猜更不要一个payload一个payload盲试。我建议用二分法测试过滤规则先用一个肯定合法的关键词如show测试是否被拦再用select测试再用union测试最后用handler测试。这样就能画出一张过滤规则的“地图”。拿这个题来说实测结果显示show可用set可用alter可用select不可用union不可用部分版本会拦部分版本过了select才拦这张地图足以指导我们选择解法既然alter可用那改表名思路成立既然show可用那信息收集没问题既然select不可用那常规注入就别挣扎了直接上handler。7. 从Supersqli到真实渗透的经验迁移7.1 堆叠注入的实战场景在CTF里见过堆叠注入不代表在真实渗透中也能直接套用。实战中堆叠注入的利用条件非常苛刻后端必须允许在一条连接里执行多条SQL且注入点必须位于支持多语句的API中。大多数PHP项目用mysqli_query()单语句执行堆叠注入直接失效。但如果你遇到的是Python的pymysql、Java的JDBC或者使用了ORM框架的某些配置多语句执行就有可能被开启。这里给一个判断技巧在真实环境中如果注入点报错但分号后语句执行成功比如;select sleep(5)出现延迟那大概率存在堆叠注入。此时不仅限于读数据还能执行update、insert甚至drop——安全风险成倍上升。7.2 SQL注入绕过滤的通用思路Supersqli这个题给的最重要启示是过滤规则永远有盲区绕过本质是寻找过滤规则没有覆盖到的SQL语法功能。黑名单拦了select你还有handler拦了union你还有alter拦了information_schema你还有sys.schema、mysql.innodb_table_stats拦了你还有like、in、regexp。我总结了一套通用流程先判断过滤粒度是过滤单个关键词还是过滤整条语句再判断过滤位置是参数值层、WAF层还是代码层针对过滤粒度选择绕过技巧单关键词层大小写混淆、双写、等价函数替换语法层handler、预处理语句prepare、join别名一层WAF编码绕、注释分隔、参数污染这个题的handler就属于“语法层绕过”的经典代表。它的思路可以迁移到很多场景比如遇到过滤select的WAF时优先考虑handler TableName open是否能凑效。7.3 防御视角的思考从防御角度看Supersqli相当于是给所有开发者敲了一次警钟仅仅过滤关键词并不能防住SQL注入。正确的做法永远是使用预编译语句PreparedStatement或参数化查询让用户输入的内容永远只是“数据”而不是“代码”。如果你在代码审计里看到类似mysqli_query($conn, select * from words where id$id)这种字符串拼接哪怕外面套了三层黑名单也应该立刻标记为高危漏洞。因为任何黑名单都可能被绕过而参数化查询从机制上就杜绝了注入的可能。8. 总结与扩展思考让我把这条解题链路再串一遍单引号闭合探测-order by确定字段数-union select被过滤-分号堆叠验证成功-show tables信息收集-handler/alter表名绕过select-拿到flag。每一步都环环相扣没有一步是多余的。从我的实际做题感受来看Supersqli之所以被称为“胎教版”是因为它精准地展示了一个常规注入思路被堵死后如何通过SQL语法本身的特性找到新的突破口。它不是靠旁门左道取胜而是逼迫你真正理解MySQL的执行语法。如果你还想深入拓展建议把下面几个方向都自己动手做一遍尝试用预处理语句Prepare绕过select过滤set aconcat(sel,ect * from 1919810931114514);prepare stmt from a;execute stmt;尝试用show create table查看建表语句确认字段名后针对性改名尝试结合时间盲注id1 and sleep(5)--考验手速和耐心操作过程中我自己最喜欢的一个技巧是在Burp的Repeater里顺手保存每次成功的payload方便回头复盘。CTF的乐趣就在于“当时差点放弃最后恍然大悟”的那种爽感。Supersqli这道题教会我的不只是怎么绕过过滤更是面对一道道“此路不通”的关卡时怎么静下心来把SQL手册翻得更深一点。下一次当你被一个诡异的正则卡住动弹不得时想想handler——数据库的功能那么多过滤规则的作者不一定都记得。