SQLi-Labs靶场全解析:从搭建到实战的SQL注入修炼指南 想系统练 SQL 注入又不想拿线上站点开刀SQLi-Labs 是我这几年练手用过最顺手的靶场之一。它不跟你玩虚的打开就是一个真实的注入场景从最简单的整型注入到报错注入、盲注、宽字节、二次注入一层一层往上加难度每一关都能看到原始代码和报错信息。如果你正准备学 SQL 注入或者已经会用工具但看不懂原理这个靶场都值得认真过一遍。工具一把梭谁都会但把闭合方式搞清楚、能手工判断字段数和回显点才是真能上场的本事。我这篇文章就把这个靶场从搭建到关键关卡的思路完整拆一遍照着走你也能把它吃透。1. SQLi-Labs 是什么我为什么推荐拿它练手1.1 一个 SQL 注入实验平台解决了什么问题SQLi-Labs 是一套开源的 SQL 注入实验靶场代码跑在 PHP MySQL 环境里通过访问不同的关卡页面来模拟真实站点的注入点。它解决的问题很直接很多人想学注入但既不能拿公司线上库试又不好意思用公共测试站刷最后只能对着理论知识发呆。这个靶场把从入门到进阶的注入手法全部封装好让你在完全合法的环境里把每一种姿势都练到顺手。靶场里每一关就是一个独立的 PHP 页面你传参的方式、参数名、过滤逻辑都不同。页面底部会显示后端拼接 SQL 的部分代码这个设计对新手特别友好——它逼着你看代码、理解为什么这个参数注入不了、为什么换一种闭合方式就能出数据。我见过不少上来就开 sqlmap 扫的人扫完也不知道发生了什么换到 SQLi-Labs 里如果还这么玩那基本等于白练。1.2 关卡多不代表乱递进式设计怎么排的整个靶场有几十道关卡按注入类型分成多个主题组。我按自己的理解重新排了一下方便你对照着刷关卡范围学习主题核心手法Less-1 ~ Less-4基础注入整型、字符型、括号闭合、双引号闭合Less-5 ~ Less-6报错注入updatexml、extractvalue、floor 报错Less-7 ~ Less-10盲注布尔盲注、时间盲注、文件读写Less-11 ~ Less-17POST 注入登录表单绕过、基于 POST 的注入Less-18 ~ Less-22HTTP 头注入User-Agent、Referer、Cookie 注入Less-23 ~ Less-31过滤器绕过注释符过滤、关键字过滤、参数绕过Less-32 ~ Less-37宽字节注入GBK 编码绕过转义Less-38 ~ Less-53堆叠注入多语句执行、存储过程Less-54 ~ Less-65挑战关综合运用、次数限制这个分组不是随意的它暗中对应了真实场景里常见的坑。比如 Less-1 到 Less-4 让你理解“单引号闭合”和“双引号闭合”的区别到了 Less-23 突然把注释符过滤掉你之前习惯性末尾加-- 的思路就失效了必须换一种方式闭合。这种递进设计逼你从“背 payload”变成“理解语法结构”我觉得这是靶场最有价值的部分。2. 环境搭建跑起来是第一要务2.1 软件栈选择与版本避坑SQLi-Labs 要求的环境是 Apache PHP MySQLWindows 上用集成环境最省事macOS 和 Linux 上手动装或者用 Docker 都行。我最初踩过一个坑直接用 PHP 7 以上版本跑打开关卡页面全屏报错各种函数被移除或行为改变连数据库连接都失败。原因在于靶场代码是面向 PHP 5.x 时代写的很多老函数和高版本不兼容。如果你不想折腾代码兼容性建议装 PHP 5.6 左右的版本配合 MySQL 5.7 能稳定跑完整套关卡。如果只有 PHP 7/8也不是完全不能跑但需要你手动改代码里废弃的函数比如把mysql_*系列替换成mysqli_*或者找到网上现成的兼容补丁。我实测下来改起来不难但对新手来说会分散学习注意力所以版本选择上宁旧勿新。2.2 安装步骤与数据库初始化整个搭建流程我整理成下面几步按顺序操作基本不会卡壳从开源代码托管平台下载 SQLi-Labs 源码包解压后放到 Web 服务根目录下的sqli-labs文件夹。打开sql-connections/db-creds.inc文件修改数据库用户名、密码、库名三项配置默认值是 root 和空密码如果你本地 MySQL 密码不同就改这里。启动 Apache 和 MySQL 服务浏览器访问http://localhost/sqli-labs/或你设置的站点地址。页面上会有初始化数据库的入口按钮点击执行一次 setup它会自动创建靶场需要的数据库和表结构。创建完成后回到首页点击Less-1开始第一关。第 4 步初始化有时会被漏掉但这一步不做后面所有关卡都会报找不到表的错误。执行 setup 的时候注意看页面有没有红色报错有报错就优先排查数据库账号密码和权限问题。别跳过这个验证步骤直接刷关。2.3 靶场目录结构与代码阅读技巧跑起来之后不要急着到处点先把目录结构摸一遍。靶场代码按关卡编号组织主目录下每个Less-*文件夹就是一关里面有对应的 PHP 文件。很多关卡的过滤逻辑写在单独的 PHP 文件里你在页面上看到的只是结果真正的注入判断逻辑需要打开源码看。我强烈建议你配合代码阅读来刷题。比如打开 Less-1 的源码核心拼接语句就是SELECT ... WHERE id$id你能直观看到单引号是怎么包住参数的以及为什么输入一个就会造成语法错误。这种读代码的习惯越早养成越好因为后面所有绕过思路本质上都是跟代码开发者“搏斗”。你在调整注入语句时要多回头对照源码确认你的闭合思路是否和实际拼接逻辑一致而不是盲猜。3. 上手实战从 Less-1 到 Less-8 的注入手法拆解3.1 字符型注入的闭合本质Less-1 / 2 / 3 / 4Less-1 是经典的字符型注入URL 长这样http://localhost/sqli-labs/Less-1/?id1。你先正常访问?id1有数据显示改成?id1页面报 SQL 语法错误这就说明后端 SQL 是类似WHERE id$id的拼接方式——你输入的单引号跑到了 SQL 里把原本闭合的字符串结构打乱了。接下来思路要清晰三步走第一步确定闭合方式。试?id1 --如果正常出数据说明后面用单引号闭合再加注释符把多余的 SQL 注释掉即可。第二步判断字段数。用?id1 ORDER BY 3 --如果报错说明只有两列换成ORDER BY 2正常。第三步确定回显位。用?id-1 UNION SELECT 1,2,3 --因为 id 为负数没有数据UNION 后面前面的空结果集就会露出后面的查询内容页面上能直接看到数字“2”和“3”的位置这两个就是回显点。Less-2 是整型注入SQL 拼接是WHERE id$id不用单引号直接?id-1 UNION SELECT 1,2,3就能打通。Less-3 和 Less-4 则是在原有关闭方式上加了括号闭合逻辑变成($id)和($id)你需要在 payload 里补右括号再去注释。判断方法是先随手输入一个单引号或双引号观察报错信息里残留的引号和括号痕迹然后反推闭合结构。我多说一句这前四关很多新手会用 sqlmap 一把过但我建议你手工把每一步都打一遍。因为闭合方式不是背出来的是“看报错信息猜出来的”这个手感到了真实环境下能救命。3.2 报错注入的原理与常用函数Less-5 / 6Less-5 和 Less-6 页面上的数据不回显但是会把 SQL 报错信息打出来。这意味着常规的 UNION 注入看不到结果但你可以利用 MySQL 报错机制把数据“挤”到错误信息里。常用函数是updatexml和extractvalue。以 updatexml 为例它在解析 XPath 路径时如果遇到非法表达式会把参数字符串回显在报错信息里。Payload 长这样?id1 AND updatexml(1, CONCAT(0x7e, (SELECT user()), 0x7e), 1) --0x7e是波浪号~的十六进制写法作用是让拼接出来的字符串包含非法字符从而触发报错。(SELECT user())是你要查询的数据可以替换成database()、version()或者从数据库中提取的表名、列名、字段值。报错结果会截断显示一部分数据所以提取长字符串时建议配合substring分段处理一次只截取一段比如?id1 AND updatexml(1, CONCAT(0x7e, SUBSTRING((SELECT password FROM users LIMIT 1), 1, 31)), 1) --Less-6 和 Less-5 的思路一样区别在于闭合方式变成了双引号payload 里的全部换成即可。报错注入速度比盲注快得多但在真实环境中要注意目标站点是否在应用层屏蔽了数据库错误信息如果看不到报错就得转盲注思路。3.3 盲注思路与延时判断Less-8 / 9Less-8 是布尔盲注页面既不回显数据也不回显报错只有“正常显示”和“无显示”两种状态。你要做的就是把数据一位一位猜出来用逻辑判断构造条件条件为真时页面正常条件为假时页面异常。核心函数组合是substring和ascii?id1 AND ASCII(SUBSTRING((SELECT database()), 1, 1)) 100 --如果你猜当前数据库名的第一个字符 ASCII 值大于 100页面正常改成小于或等号页面异常。接下来无非是二分法缩小范围把每个字符的 ASCII 值精确猜出来。这个过程手工做一遍就知道有多费劲所以实战中通常配合脚本自动化请求。但你必须理解原理因为后续写脚本或者判断工具输出时都依赖这套逻辑。Less-9 和 Less-10 是时间盲注。页面不管条件真假都显示同样的内容只能靠响应时间差异判断。Paylaod 核心是用sleep函数配合条件判断?id1 AND IF(ASCII(SUBSTRING((SELECT database()), 1, 1)) 100, SLEEP(3), 0) --条件为真时页面卡顿 3 秒为假时立即返回。时间盲注最怕网络本身不稳定导致误判所以延时时间不要设太短一般 3 秒以上比较稳妥。这里有个小技巧Burp Suite 或者写脚本时记录每次响应的时间把时间值低于阈值的请求视为条件为假高于阈值的视为真再批量跑出完整数据。Less-10 只是换成双引号闭合方法完全一致。3.4 手工注入的通用五步流程刷完这 8 关你已经建立了手工注入的基本框架我把它提炼成一套通用流程后面所有关卡都可以套用判断注入点类型数字型还是字符型括号、引号怎么闭合。判断字段数用ORDER BY逐级试探直到报错为止。确认回显位用 UNION 查询插入占位数字找到页面上显示的数字位置。枚举数据库信息database()、user()、version()起步再到表名、列名、数据。针对性调整 payload根据关卡的过滤逻辑修改注释符、大小写、编码方式或拼接方式。这套流程熟练之后你面对的不再是“SQL 注入”这个大概念而是“给定一个页面判断闭合、确定位置、取数”这三件事。刷 SQLi-Labs 的目的就是让这套流程变成肌肉记忆。4. 进阶关卡与更隐蔽的手法4.1 宽字节注入与二次注入Less-32 到 Less-37 是宽字节注入的典型场景背景是代码对单引号加了反斜杠转义也就是你把传进去后端拼 SQL 时变成\导致引号被当作普通字符串的一部分。但这里有个前提——如果数据库和页面使用的是 GBK 等多字节编码反斜杠本身可以跟前一个字节拼成一个宽字符。具体操作是在引号前加%df当后端把%df\拼接进 SQL 时MySQL 在 GBK 编码下会把%df%5c识别成一个中文字符后面的单引号就逃逸出来变成了闭合符。典型 payload?id1%df UNION SELECT 1,2,3 --这道题我第一次刷的时候完全摸不着头脑因为普通注入思路到这里全部失败报错信息显示反斜杠已经把引号“吃掉”了。但理解了字符编码原理之后就会发现这本质上是开发者和数据库之间的编码认知差这种漏洞在今天的高版本 MySQL 里已经很少见但理解它对你排查旧系统和维护遗留代码很有帮助。二次注入则换了种思路。它的精髓是先通过正常请求把恶意数据存进数据库之后再在另一个逻辑里把数据拼接到 SQL 中触发。Less-24 模拟的场景是注册和登录后的密码修改流程注册时用户名写成admin#注册功能本身会做参数转义所以安全入库但修改密码的 SQL 拼接直接把用户名带进语句#注释掉后面的条件就实现了改别人密码的效果。二次注入在审计时极具迷惑性因为入口处你看到的防御是完好的问题藏在更深的数据消费环节。刷这道题时建议把注册、登录、改密三个页面的源码都打开对照看重点观察用户名在哪个环节被拼接进 SQL。4.2 堆叠注入和 HTTP Head 注入Less-38 开始引入堆叠注入核心是后端用支持多语句的数据库接口执行 SQL比如mysqli_multi_query。你在一个参数里同时传多条 SQL 语句用分号分隔?id1; INSERT INTO users (username, password) VALUES (hacker, 123456) --如果页面正常返回并且数据库里多出一条用户说明多语句执行成功。堆叠注入的危害一般比单个 SELECT 大得多因为它不仅能查数据还能插入、删除、修改、调用存储过程。不过这种场景在现代 ORM 和框架封装下比较少见更多出现在某些老旧运维后台和定制开发系统里。Less-18 到 Less-20 是 HTTP 头注入技能点完全不同。页面不接收 URL 参数而是读取你的User-Agent、Referer或 Cookie 字段拼接进 SQL。我第一次刷的时候在 URL 里试了半天才发现要打开 Burp Suite 抓包修改请求头User-Agent: 1 AND updatexml(1, CONCAT(0x7e, (SELECT user()), 0x7e), 1) --这提醒我们凡是服务端会记录的请求头字段都可能成为注入点。真实环境中很多系统会把 UA 和 Referer 写入数据库做访问统计如果这些字段没做参数化查询就有被注入的风险。这类问题在安全测试中经常被忽略因为大家习惯盯着 URL 参数看。4.3 参数加密、过滤绕过与防御视角Less-21 和 Less-22 里参数通过 base64 编码后传输你看到的是idMSUyMQ这种字符串。原理是后端先对参数解码再拼接 SQL所以你在 Payload 前要先用 base64 编码一次。这是一个很好的提醒参数在进 SQL 之前可能经过任意处理流程审计时不能只盯着前端传的原始值。Less-23 把注释符#和--都过滤掉了这时闭合尾部没法用注释思路要换成让多余部分自然合法。比如?id1 UNION SELECT 1,2,3 后面的单引号和前面闭合的单引号形成配对让整条语句在语法上成立。这个技巧在真实绕 WAF 时也很有用核心逻辑是不要让 SQL 语句“看起来奇怪”尽量用成对的方式补齐结构。刷到高级关卡之后建议切换视角想一想防御侧的事情。你会发现很多漏洞源于同一个根因直接把外部输入拼进 SQL 字符串。修复思路无非三种——参数化查询/预编译语句、白名单校验、最小权限数据库账号。你在靶场里每打通一关对应想一想这一段代码如果交给一个水平不错的开发来改他会怎么修这种“攻防对照”比单纯刷题更有价值。5. 常见问题与排查心得5.1 环境类问题速查表刷题过程中超过一半的问题其实出现在环境搭建环节不在 SQL 语法上。我把自己遇到过的情况整理成一张速查表现象可能原因处理方式页面出现mysql_*函数不存在PHP 版本过高换 PHP 5.6或手动替换为mysqli_*初始化数据库页面报错数据库密码配置错误检查db-creds.inc中账号密码提示表不存在忘记执行 setup 初始化访问初始化入口重新建库页面中文乱码数据库/页面编码不一致确认使用utf8或gbk与关卡要求一致访问不到/sqli-labs/未放到 Web 根目录确认路径大小写和目录权限Payload 里--无效在 URL 中被解码为空格改用%23即#或--%20第 7 条我单独说一下--这个写法是因为在 URL 中会被解码成空格而后端拼出来就是合法的 SQL 注释符--。有的环境下不按预期解码注释失效SQL 语句后半部分没被注释掉导致报错。这时换成%23会让后端收到#同样能注释掉尾部。5.2 注入不出数据的排查思路有时候你觉得自己写的 Payload 没问题但页面就是不回显。我建议按照下面顺序排查先确认闭合方式真的对吗我把靶场源码打开按实际拼接逻辑重写一遍 SQL一条一条手工拼出来看语法错误点。检查注释符有没有保留下来。URL 编码、WAF 拦截、代码过滤都可能把注释符消除换一种闭合手法测试。确认字段数正确。ORDER BY的报错页面如果被吞掉就改用ORDER BY和UNION SELECT交叉验证。检查是否真的没有回显位。有些关卡页面只显示第一条查询结果的某一列你放1,2,3时可能只显示一个数字不代表其他位置不存在换成-1强制左侧查询为空再试。查看代码里有没有隐藏的过滤函数。打开源码全局搜索preg_replace、str_replace、addslashes等关键字看哪些字符被处理了。这个排查顺序不是随便排的核心思路是从“注入是否成立”逐步过渡到“取数是否被阻断”。大部分新手卡关都是因为前面两步没确认清楚就急着在后面调整 Payload 细节白费功夫。5.3 我踩过的几个坑第一坑是刷高处关卡时盲目沿用低处的闭合思路。Less-32 用宽字节刷通了到了 Less-38 发现堆叠注入的数据库连接方式完全不同还想着加%df反而把 Payload 搞乱。正确做法是每关先看源码连接方式再决定手法选型。第二坑是忽略 POST 注入的请求格式。Less-11 开始是 POST 传参我在浏览器地址栏里改 URL 半天没反应后来才意识到要改用 Burp Suite 的 Repeater 或者浏览器的开发者工具改请求体。虽然听起来很简单但当时确实被卡住了。第三坑是过度依赖工具。我见过有人用 sqlmap 把所有关卡都跑完然后信心满满地觉得自己会了。但换一个真实目标、参数被过滤、WAF 挡在前面工具跑不动就完全不知道问题出在哪。刷靶场还是建议先手工打至少七成的关卡再用工具辅助验证结果。靶场存在的意义不是让你跑通流程交差而是让你看明白底层每一层的逻辑。6. 实战经验与进阶建议刷完整个 SQLi-Labs 再回头看它给我的最大收获不是会背了多少条 Payload而是建立了一套稳定的分析路径看到参数先判断拼接位置看到过滤先想编码和语法替代看到不回显就转条件判断。这些东西在任何 SQL 注入场景里都一样适用。另外我建议你把每一关的源码也当作学习材料来读关注开发者写 SQL 的方式、参数是否经过转义、数据库连接使用了什么接口。对照来看你能更清楚地知道什么样代码是安全的什么样代码在真实项目里随时会出问题。进阶方向一个是把这些手法映射到不同数据库上比如把 MySQL 的updatexml换成sqlite或postgresql的报错函数、把sleep换成pg_sleepSQLi-Labs 虽然是 MySQL 的靶场但思路可以平移。另一个方向是组合手法比如宽字节配合时间盲注、堆叠注入配合存储过程真实攻击中很多漏洞是多种基础手法的叠加。我个人比较推荐的刷题顺序是先手工打通前 22 关再回头用脚本或 sqlmap 对比验证明流程最后集中精力啃 23 关之后的绕过类题目。前 22 关是基本功和手感之后才是真正的对抗思路。每刷完一组关卡就用笔记记一下思路和卡点过阵子回头翻比临时记忆有效得多。这个靶场刷完下一站可以去试一些带真实业务逻辑的系统或者 CTF 题目里的 SQL 注入题把这里的判断流程用到更复杂的请求链路里。但在此之前先把 SQLi-Labs 里的每一道题吃透地基越扎实后面越省力。