☰
SQLi-Labs靶场全解析:从搭建到实战的SQL注入修炼指南
2026/10/21 15:16:19 网站建设 项目流程

想系统练 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 + MySQL,Windows 上用集成环境最省事,macOS 和 Linux 上手动装或者用 Docker 都行。我最初踩过一个坑:直接用 PHP 7 以上版本跑,打开关卡页面全屏报错,各种函数被移除或行为改变,连数据库连接都失败。

原因在于靶场代码是面向 PHP 5.x 时代写的,很多老函数和高版本不兼容。如果你不想折腾代码兼容性,建议装 PHP 5.6 左右的版本,配合 MySQL 5.7 能稳定跑完整套关卡。如果只有 PHP 7/8,也不是完全不能跑,但需要你手动改代码里废弃的函数,比如把mysql_*系列替换成mysqli_*,或者找到网上现成的兼容补丁。我实测下来改起来不难,但对新手来说会分散学习注意力,所以版本选择上宁旧勿新。

2.2 安装步骤与数据库初始化

整个搭建流程我整理成下面几步,按顺序操作基本不会卡壳:

  1. 从开源代码托管平台下载 SQLi-Labs 源码包,解压后放到 Web 服务根目录下的sqli-labs文件夹。
  2. 打开sql-connections/db-creds.inc文件,修改数据库用户名、密码、库名三项配置,默认值是 root 和空密码,如果你本地 MySQL 密码不同就改这里。
  3. 启动 Apache 和 MySQL 服务,浏览器访问http://localhost/sqli-labs/或你设置的站点地址。
  4. 页面上会有初始化数据库的入口按钮,点击执行一次 setup,它会自动创建靶场需要的数据库和表结构。
  5. 创建完成后,回到首页点击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 / 4)

Less-1 是经典的字符型注入,URL 长这样:http://localhost/sqli-labs/Less-1/?id=1。你先正常访问?id=1,有数据显示;改成?id=1',页面报 SQL 语法错误,这就说明后端 SQL 是类似WHERE id='$id'的拼接方式——你输入的单引号跑到了 SQL 里,把原本闭合的字符串结构打乱了。

接下来思路要清晰三步走:

  • 第一步,确定闭合方式。试?id=1' --+,如果正常出数据,说明后面用单引号闭合再加注释符把多余的 SQL 注释掉即可。
  • 第二步,判断字段数。用?id=1' 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 / 6)

Less-5 和 Less-6 页面上的数据不回显,但是会把 SQL 报错信息打出来。这意味着常规的 UNION 注入看不到结果,但你可以利用 MySQL 报错机制把数据“挤”到错误信息里。

常用函数是updatexml和extractvalue。以 updatexml 为例,它在解析 XPath 路径时如果遇到非法表达式,会把参数字符串回显在报错信息里。Payload 长这样:

?id=1' AND updatexml(1, CONCAT(0x7e, (SELECT user()), 0x7e), 1) --+

0x7e是波浪号~的十六进制写法,作用是让拼接出来的字符串包含非法字符,从而触发报错。(SELECT user())是你要查询的数据,可以替换成database()、version()或者从数据库中提取的表名、列名、字段值。报错结果会截断显示一部分数据,所以提取长字符串时建议配合substring分段处理,一次只截取一段,比如:

?id=1' AND updatexml(1, CONCAT(0x7e, SUBSTRING((SELECT password FROM users LIMIT 1), 1, 31)), 1) --+

Less-6 和 Less-5 的思路一样,区别在于闭合方式变成了双引号,payload 里的'全部换成"即可。报错注入速度比盲注快得多,但在真实环境中要注意目标站点是否在应用层屏蔽了数据库错误信息,如果看不到报错,就得转盲注思路。

3.3 盲注思路与延时判断(Less-8 / 9)

Less-8 是布尔盲注,页面既不回显数据也不回显报错,只有“正常显示”和“无显示”两种状态。你要做的就是把数据一位一位猜出来:用逻辑判断构造条件,条件为真时页面正常,条件为假时页面异常。

核心函数组合是substring和ascii:

?id=1' AND ASCII(SUBSTRING((SELECT database()), 1, 1)) > 100 --+

如果你猜当前数据库名的第一个字符 ASCII 值大于 100,页面正常;改成小于或等号,页面异常。接下来无非是二分法缩小范围,把每个字符的 ASCII 值精确猜出来。这个过程手工做一遍就知道有多费劲,所以实战中通常配合脚本自动化请求。但你必须理解原理,因为后续写脚本或者判断工具输出时都依赖这套逻辑。

Less-9 和 Less-10 是时间盲注。页面不管条件真假都显示同样的内容,只能靠响应时间差异判断。Paylaod 核心是用sleep函数配合条件判断:

?id=1' AND IF(ASCII(SUBSTRING((SELECT database()), 1, 1)) > 100, SLEEP(3), 0) --+

条件为真时页面卡顿 3 秒,为假时立即返回。时间盲注最怕网络本身不稳定导致误判,所以延时时间不要设太短,一般 3 秒以上比较稳妥。这里有个小技巧:Burp Suite 或者写脚本时记录每次响应的时间,把时间值低于阈值的请求视为条件为假,高于阈值的视为真,再批量跑出完整数据。Less-10 只是换成双引号闭合,方法完全一致。

3.4 手工注入的通用五步流程

刷完这 8 关,你已经建立了手工注入的基本框架,我把它提炼成一套通用流程,后面所有关卡都可以套用:

  1. 判断注入点类型:数字型还是字符型,括号、引号怎么闭合。
  2. 判断字段数:用ORDER BY逐级试探,直到报错为止。
  3. 确认回显位:用 UNION 查询插入占位数字,找到页面上显示的数字位置。
  4. 枚举数据库信息:database()、user()、version()起步,再到表名、列名、数据。
  5. 针对性调整 payload:根据关卡的过滤逻辑,修改注释符、大小写、编码方式或拼接方式。

这套流程熟练之后,你面对的不再是“SQL 注入”这个大概念,而是“给定一个页面,判断闭合、确定位置、取数”这三件事。刷 SQLi-Labs 的目的就是让这套流程变成肌肉记忆。

4. 进阶关卡与更隐蔽的手法

4.1 宽字节注入与二次注入

Less-32 到 Less-37 是宽字节注入的典型场景,背景是代码对单引号加了反斜杠转义,也就是你把'传进去,后端拼 SQL 时变成\',导致引号被当作普通字符串的一部分。但这里有个前提——如果数据库和页面使用的是 GBK 等多字节编码,反斜杠本身可以跟前一个字节拼成一个宽字符。

具体操作是在引号前加%df,当后端把%df\'拼接进 SQL 时,MySQL 在 GBK 编码下会把%df%5c识别成一个中文字符,后面的单引号就逃逸出来变成了闭合符。典型 payload:

?id=1%df' UNION SELECT 1,2,3 --+

这道题我第一次刷的时候完全摸不着头脑,因为普通注入思路到这里全部失败,报错信息显示反斜杠已经把引号“吃掉”了。但理解了字符编码原理之后就会发现,这本质上是开发者和数据库之间的编码认知差,这种漏洞在今天的高版本 MySQL 里已经很少见,但理解它对你排查旧系统和维护遗留代码很有帮助。

二次注入则换了种思路。它的精髓是先通过正常请求把恶意数据存进数据库,之后再在另一个逻辑里把数据拼接到 SQL 中触发。Less-24 模拟的场景是注册和登录后的密码修改流程:注册时用户名写成admin'#,注册功能本身会做参数转义,所以安全入库;但修改密码的 SQL 拼接直接把用户名带进语句,#注释掉后面的条件,就实现了改别人密码的效果。

二次注入在审计时极具迷惑性,因为入口处你看到的防御是完好的,问题藏在更深的数据消费环节。刷这道题时建议把注册、登录、改密三个页面的源码都打开对照看,重点观察用户名在哪个环节被拼接进 SQL。

4.2 堆叠注入和 HTTP Head 注入

Less-38 开始引入堆叠注入,核心是后端用支持多语句的数据库接口执行 SQL,比如mysqli_multi_query。你在一个参数里同时传多条 SQL 语句,用分号分隔:

?id=1'; 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 编码后传输,你看到的是id=MSUyMQ==这种字符串。原理是后端先对参数解码再拼接 SQL,所以你在 Payload 前要先用 base64 编码一次。这是一个很好的提醒:参数在进 SQL 之前可能经过任意处理流程,审计时不能只盯着前端传的原始值。

Less-23 把注释符#和--都过滤掉了,这时闭合尾部没法用注释,思路要换成让多余部分自然合法。比如:

?id=1' 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 没问题,但页面就是不回显。我建议按照下面顺序排查:

  1. 先确认闭合方式真的对吗?我把靶场源码打开,按实际拼接逻辑重写一遍 SQL,一条一条手工拼出来看语法错误点。
  2. 检查注释符有没有保留下来。URL 编码、WAF 拦截、代码过滤都可能把注释符消除,换一种闭合手法测试。
  3. 确认字段数正确。ORDER BY的报错页面如果被吞掉,就改用ORDER BY和UNION SELECT交叉验证。
  4. 检查是否真的没有回显位。有些关卡页面只显示第一条查询结果的某一列,你放1,2,3时可能只显示一个数字,不代表其他位置不存在,换成-1强制左侧查询为空再试。
  5. 查看代码里有没有隐藏的过滤函数。打开源码全局搜索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_sleep,SQLi-Labs 虽然是 MySQL 的靶场,但思路可以平移。另一个方向是组合手法,比如宽字节配合时间盲注、堆叠注入配合存储过程,真实攻击中很多漏洞是多种基础手法的叠加。

我个人比较推荐的刷题顺序是:先手工打通前 22 关,再回头用脚本或 sqlmap 对比验证明流程,最后集中精力啃 23 关之后的绕过类题目。前 22 关是基本功和手感,之后才是真正的对抗思路。每刷完一组关卡,就用笔记记一下思路和卡点,过阵子回头翻比临时记忆有效得多。

这个靶场刷完,下一站可以去试一些带真实业务逻辑的系统或者 CTF 题目里的 SQL 注入题,把这里的判断流程用到更复杂的请求链路里。但在此之前,先把 SQLi-Labs 里的每一道题吃透,地基越扎实,后面越省力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询