news 2026/10/18 14:46:09

SQLi-Labs Less1-20通关指南:从联合查询到盲注的完整注入链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQLi-Labs Less1-20通关指南:从联合查询到盲注的完整注入链路

1. 为什么我建议你从Less1到Less20完整刷一遍

SQL注入是Web安全领域最经典的漏洞类型之一,而SQLi-Labs这个开源靶场几乎是每个安全从业者绕不开的练习场。Less1到Less20这二十关,覆盖了联合查询注入、报错注入、布尔盲注、时间盲注、堆叠注入、HTTP头注入等多种核心手法,基本把MySQL环境下SQL注入的常见形态都串了一遍。很多人刷靶场喜欢跳着做,觉得前面几关太简单,直接跳到盲注或者堆叠。我试过这种玩法,后来发现基础不牢,到了后面连判断注入类型都要卡半天。Less1到Less20的价值不在于难度,而在于它帮你建立一套完整的判断链路:看到参数先测什么、根据回显差异怎么缩小范围、不同注入类型对应什么Payload结构。这套链路一旦形成肌肉记忆,后面遇到真实场景才不会慌。

这篇文章面向的是刚接触SQL注入、想系统练习但不知道从哪下手的人,也适合刷过几关但思路比较零散、想重新梳理一遍的朋友。我会按关卡类型分组拆解,把每关的核心判断逻辑、Payload构造思路、实操中容易踩的坑都讲清楚。不会只丢一串Payload让你复制,而是解释为什么这么构造、参数怎么算出来的。文章里涉及的所有操作都在本地搭建的靶场环境中进行,目的是理解漏洞原理和防御思路。

2. 环境搭建与前期准备

2.1 靶场部署的两种常见方式

SQLi-Labs的部署方式主要有两种:一种是集成环境直接跑,比如用PHPStudy或者XAMPP这类集成包,把源码丢进Web根目录,改一下数据库配置文件就能用;另一种是手动配Apache+PHP+MySQL,适合想顺便练一下环境配置的人。我建议新手直接用集成环境,省下来的时间花在理解注入逻辑上更划算。

数据库配置文件一般在sql-connections/db-creds.inc这个路径下,里面定义了数据库用户名、密码和库名。默认情况下用户是root,密码为空或者root,具体看你本地MySQL的设置。如果连接报错,先检查这个文件,再看MySQL服务有没有正常启动。

# 导入靶场数据库结构 mysql -u root -p < sqli-labs.sql

导入完成后访问http://localhost/sqli-labs/,能看到关卡列表就说明环境通了。这里有个细节:PHP版本建议用5.x或者7.x的早期版本,太新的PHP版本对某些函数的处理有变化,可能导致部分关卡的报错信息显示不出来,影响练习效果。

2.2 练习前必须明确的几个概念

在开始之前,有几个概念必须先理清楚,不然后面会一直混淆。

字符型注入和数字型注入的区别:数字型注入参数直接拼进SQL语句,不需要引号闭合;字符型注入参数被引号包裹,需要先闭合引号再注入。判断方法很简单,输入?id=1正常,输入?id=1'报错,输入?id=1' --+又正常,基本就是字符型。

联合查询的条件:使用UNION注入时,前后两个SELECT语句的列数必须一致。所以第一步永远是判断当前查询有几列,用ORDER BY或者UNION SELECT 1,2,3...逐个试。

回显位的重要性:联合查询注入的核心是找到页面上哪些位置会显示查询结果,这些位置就是回显位。只有找到回显位,才能把想要的数据替换上去显示出来。

提示:练习时养成随手记录的习惯,每关的注入类型、列数、回显位、最终Payload都记下来。二十关刷完回头看这份记录,比任何教程都管用。

3. Less1-4:联合查询注入的入门四关

3.1 Less1:字符型注入的标准流程

Less1是最基础的一关,但它的流程最完整,值得慢慢做一遍。输入?id=1,页面显示正常用户信息。输入?id=1',出现SQL语法报错,说明参数被单引号包裹,是字符型注入。

接下来判断列数。用?id=1' ORDER BY 3 --+,正常;?id=1' ORDER BY 4 --+,报错。说明当前查询是3列。这里--+的作用是注释掉后面的引号和限制条件,--是SQL注释符,+在URL中代表空格,确保注释生效。

然后找回显位:?id=-1' UNION SELECT 1,2,3 --+。注意这里id用-1而不是1,目的是让前面的查询返回空结果,这样页面显示的就是我们UNION后面的内容。页面上会显示2和3的位置,这两个就是回显位。

最后拿数据:?id=-1' UNION SELECT 1,database(),version() --+,把回显位替换成想要的函数或字段。查表名用group_concat(table_name) from information_schema.tables where table_schema=database(),查列名类似,最后查具体数据。

3.2 Less2:数字型注入的差异

Less2和Less1的Payload几乎一样,唯一的区别是参数没有被引号包裹。输入?id=1'报错,但输入?id=1' --+依然报错,说明引号不是闭合用的,参数本身就是数字型。这时候Payload去掉单引号即可:?id=-1 UNION SELECT 1,2,3 --+。

很多人做Less2的时候习惯性带上单引号,结果一直报错,以为是别的问题。判断注入类型这一步不能省,字符型和数字型的Payload差一个引号,但就是这一个引号决定成败。

3.3 Less3和Less4:括号与变形闭合

Less3的参数被包裹在('')这样的结构里,输入?id=1'报错,需要构造?id=1') --+来闭合。Less4则是双引号加括号(""),闭合方式变成?id=1") --+。

这两关的意义在于告诉你:闭合方式不只有单引号一种,实际场景中可能是括号、双引号、甚至更复杂的组合。判断方法还是靠报错信息,报错会告诉你哪里语法有问题,根据报错反推闭合结构。

关卡闭合方式典型Payload前缀
Less1单引号?id=-1' UNION SELECT ...
Less2无引号?id=-1 UNION SELECT ...
Less3单引号加括号?id=-1') UNION SELECT ...
Less4双引号加括号?id=-1") UNION SELECT ...

注意:每关的列数可能不同,不要死记Payload,每次都要重新用ORDER BY判断列数。我见过有人拿着Less1的3列Payload去做Less3,结果列数不对一直报错。

4. Less5-6:报错注入与盲注的分水岭

4.1 Less5:没有回显位怎么办

Less5输入正常id页面显示"You are in...",输入错误id显示同样的话,没有数据回显,也没有明显报错。这时候联合查询就失效了,因为没有回显位可以放数据。

但Less5其实有报错信息,只是需要触发它。用?id=1' and extractvalue(1,concat(0x7e,database())) --+,页面会返回XPATH报错,报错内容里就包含了数据库名。extractvalue和updatexml是两个经典的报错注入函数,原理是构造非法的XPATH路径,让MySQL在报错信息中带出查询结果。

concat(0x7e,...)里的0x7e是波浪号,作用是分隔符,方便在报错信息中定位数据。报错注入适合页面有报错回显但无数据回显的场景,Payload构造比盲注快得多。

4.2 Less6:双引号版的报错注入

Less6和Less5逻辑一样,只是闭合方式变成双引号。Payload改成?id=1" and extractvalue(1,concat(0x7e,database())) --+即可。

这两关放在一起练,目的是让你形成条件反射:遇到无回显但有报错的场景,先试报错注入。报错注入能出数据就优先用,出不来再考虑盲注。盲注是最后的手段,因为效率低。

4.3 报错注入的常见函数对比

函数适用版本特点
extractvalueMySQL 5.1+报错长度有限,约32字符
updatexmlMySQL 5.1+同样有长度限制
floor+rand+group by老版本构造复杂,新版本可能失效
expMySQL 5.5+溢出报错,长度也有限

报错注入的长度限制是个坑。如果查询结果超过32个字符,需要用substr或limit分段读取。我一般用limit 0,1逐行读,配合substr截取,虽然麻烦但稳定。

5. Less7-10:文件操作与时间盲注

5.1 Less7:outfile写入的利用

Less7的闭合方式是)) --+,但它的特殊之处在于可以利用into outfile写文件。Payload类似?id=1')) UNION SELECT 1,'<?php ... ?>',3 into outfile '/var/www/html/shell.php' --+。

这关的意义是展示SQL注入的延伸危害:不仅能读数据,还能写文件。当然实际环境中能否写入取决于数据库用户的文件权限和secure_file_priv配置。练习时如果写入失败,先检查MySQL的secure_file_priv设置,这个参数限制了可以写文件的目录。

-- 查看当前配置 show variables like 'secure_file_priv';

5.2 Less8-10:布尔盲注与时间盲注

Less8是布尔盲注,页面只有两种状态:有结果和无结果。通过构造真/假条件,观察页面变化来逐字符推断数据。Payload结构是?id=1' and substr(database(),1,1)='s' --+,如果页面正常说明首字母是s,否则不是。

布尔盲注的效率取决于字符集大小和猜解策略。我一般先用二分法确定字符范围,再逐个字符猜。手工做太慢,通常写脚本跑。Python的requests库配合二分查找,一个数据库名几分钟能出来。

Less9是时间盲注,页面无论真假都返回一样的内容,只能靠响应时间判断。Payload用if(condition,sleep(3),0),条件为真时延迟3秒。时间盲注最慢,但适用场景最广,因为只要有SQL执行就能用。

Less10是双引号版的时间盲注,逻辑一样,闭合方式不同。

提示:时间盲注的sleep时间不要设太短,网络波动容易误判。我一般设3秒,连续测两次结果一致才确认。另外可以用benchmark函数替代sleep,但benchmark消耗CPU,不如sleep稳定。

6. Less11-16:POST注入与HTTP头注入

6.1 Less11-12:POST表单注入

从Less11开始,注入点从GET参数变成了POST表单。用Burp Suite抓包或者浏览器开发者工具看请求体,发现uname和passwd两个参数。注入点在uname上,闭合方式是单引号。

POST注入和GET注入的Payload构造逻辑完全一样,区别只是传参方式。用Burp的Repeater模块改包测试最方便,或者用curl:

curl -X POST http://localhost/sqli-labs/Less-11/ \ -d "uname=admin' and extractvalue(1,concat(0x7e,database())) --+&passwd=1&submit=Submit"

Less12是括号加双引号的闭合,Payload相应调整。

6.2 Less13-14:无回显的POST注入

Less13和Less14是POST场景下的盲注,页面只返回登录成功或失败。Less13用布尔盲注,Less14用时间盲注。逻辑和Less8、Less9一样,只是传参方式变了。

这两关的难点在于判断注入是否生效。因为POST请求不像GET那样直接在URL里改,需要抓包改包。我建议用Burp的Intruder或者写Python脚本,手工在浏览器里改POST数据太费劲。

6.3 Less15-16:HTTP头注入的引入

Less15和Less16虽然还是POST表单,但它们的特殊之处在于可以尝试HTTP头注入。比如User-Agent、Referer、X-Forwarded-For这些头,如果后端把它们拼进SQL语句,就可能存在注入。

判断方法:在User-Agent里加单引号,看是否报错。如果报错,说明User-Agent被带入了SQL查询。Payload构造和普通注入一样,只是注入点变成了HTTP头。

curl -H "User-Agent: Mozilla' and extractvalue(1,concat(0x7e,database())) --+" \ http://localhost/sqli-labs/Less-15/

注意:HTTP头注入在实际场景中很常见,因为很多应用会记录User-Agent或Referer到数据库。但靶场里这两关主要还是POST注入,HTTP头注入是额外练习。不要混淆主次。

7. Less17-20:密码修改、头部注入与Cookie注入

7.1 Less17:密码修改场景的注入

Less17是一个密码修改功能,注入点在uname参数上。但它的特殊之处在于,修改密码前会先查询原密码,如果原密码不对就不会执行更新。所以注入需要绕过这个检查。

Payload思路是用and构造条件,让查询返回真,同时利用报错或盲注带出数据。这关的闭合方式是单引号,但查询语句里可能有and password='...'这样的结构,需要注释掉后面的部分。

7.2 Less18-19:User-Agent和Referer注入

Less18的注入点在User-Agent,Less19在Referer。这两关的POST表单本身没有注入,但后端把User-Agent和Referer插入了数据库的日志表,导致注入。

判断方法:在User-Agent里加单引号,如果页面报错或行为异常,说明存在注入。Payload构造和普通注入一样,只是注入点藏在HTTP头里。

# Less18 User-Agent注入 curl -H "User-Agent: Mozilla' and extractvalue(1,concat(0x7e,database())) --+" \ -d "uname=admin&passwd=1&submit=Submit" \ http://localhost/sqli-labs/Less-18/

7.3 Less20:Cookie注入的实战意义

Less20的注入点在Cookie的uname字段。登录成功后,后端会把uname写入Cookie,下次请求时从Cookie读取并拼进SQL查询。如果在Cookie里加单引号,就能触发注入。

Cookie注入在实际场景中很常见,因为很多应用用Cookie做身份识别,如果直接拼接Cookie值到SQL语句,就存在注入风险。这关的Payload和普通注入一样,只是注入点变成了Cookie。

curl -b "uname=admin' and extractvalue(1,concat(0x7e,database())) --+" \ http://localhost/sqli-labs/Less-20/
关卡注入点注入类型闭合方式
Less17POST uname报错/盲注单引号
Less18User-Agent报错注入单引号
Less19Referer报错注入单引号
Less20Cookie uname报错注入单引号

8. 常见问题与排查技巧实录

8.1 报错信息不显示怎么办

有时候输入单引号页面没有报错,只是显示空白或跳转。可能原因有几个:一是PHP的display_errors关闭了,需要在php.ini里打开;二是代码里用了@抑制错误;三是查询本身没有报错,只是返回空结果。

排查顺序:先看php.ini的display_errors和error_reporting设置,确保错误能显示。如果还是不行,尝试用时间盲注的Payload,通过响应时间判断注入是否生效。

8.2 联合查询列数判断不准

用ORDER BY判断列数时,有时候报错信息不明确,不知道是列数超了还是语法错了。这时候可以用UNION SELECT 1,2,3...逐个增加数字,直到页面正常显示。两种方法结合用,互相验证。

另外注意,ORDER BY后面跟数字表示按第几列排序,如果数字超过列数会报错。但有些版本的MySQL对ORDER BY的错误处理不一样,可能不报错只是排序无效。所以UNION SELECT更可靠。

8.3 盲注脚本跑不出数据

盲注脚本常见问题:一是URL编码没处理好,Payload里的特殊字符被转义;二是sleep时间太短,网络延迟导致误判;三是字符集判断错误,比如大小写没区分。

我的做法是先用单个字符测试,确认脚本能正确判断真假,再跑完整流程。sleep时间设3秒,连续两次结果一致才记录。字符集先用substr配合ascii函数转成数字比较,避免大小写和编码问题。

8.4 文件写入失败的原因

into outfile写入失败通常是因为secure_file_priv限制。这个参数如果是NULL,表示禁止文件写入;如果是空字符串,表示不限制目录;如果指定了路径,只能写入该路径。

show variables like 'secure_file_priv';

另外,数据库用户需要有FILE权限,目标目录需要MySQL进程有写权限。这几个条件缺一不可。

8.5 HTTP头注入不生效

HTTP头注入不生效的原因可能是:后端没有把该头拼进SQL,或者头被过滤了。判断方法是在头里加单引号,看是否报错。如果不报错,换个头试试,比如从User-Agent换到Referer或X-Forwarded-For。

有些应用会对HTTP头做长度限制或字符过滤,这时候需要编码绕过。比如把单引号换成URL编码%27,或者用双写、大小写混合等方式绕过简单过滤。

9. 从靶场到实战的思维转换

刷完Less1到Less20,你手里应该有一套完整的判断流程:先测闭合方式,再判断注入类型,然后根据回显情况选择联合查询、报错注入还是盲注。这套流程在靶场里是线性的,但实战中往往更复杂。

实战中遇到的注入点可能没有明显的报错,回显也不规律,甚至参数经过多层编码。这时候需要更耐心地测试,用不同的Payload组合去探测。另外,实战中要注意注入点的上下文,比如参数是在WHERE条件里还是在ORDER BY后面,不同的位置Payload构造方式不同。

还有一个重要的思维转换:靶场里我们追求的是拿到数据,实战中更重要的是理解漏洞的成因和影响范围。一个SQL注入能读到什么数据、能不能写文件、能不能执行命令,这些决定了漏洞的严重程度。刷靶场的时候多问自己几个为什么,比单纯追求通关更有价值。

我个人在实际操作中的体会是,Less1到Less20这二十关,真正需要反复练的是Less1、Less5、Less8、Less9这四关,它们分别代表了联合查询、报错注入、布尔盲注、时间盲注四种核心手法。把这四关的Payload构造逻辑吃透,后面的关卡都是在这四种手法上做变形。另外,每关做完后试着不看笔记重新构造一遍Payload,能独立构造出来才算真正掌握。最后分享一个小技巧:把每关的Payload按类型整理成一个速查表,遇到新场景时先对照速查表判断类型,再调整Payload细节,效率会高很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 14:44:06

TCP/IP 协议栈与 HTTP 协议原理:从分层模型到报文传输全过程

网络通信是 Web 接口开发的底层基础。本文讲解 TCP/IP 四层协议栈分层职责&#xff0c;数据包封装和解封装流程&#xff1b;深入讲解 HTTP 协议请求报文、响应报文、HTTP 方法、状态码、无状态特性&#xff1b;对比 HTTP/1.1、HTTP/2、HTTP/3&#xff1b;HTTPS 加密原理&#x…

作者头像 李华
网站建设 2026/10/18 14:43:57

编程农场通关代码:BFS寻路、状态机与步数优化实战解析

简介&#xff1a;编程农场通关代码是一份面向编程游戏爱好者与算法初学者的项目源码&#xff0c;用来解决农场自动化管理与迷宫寻宝两类核心玩法&#xff0c;帮助玩家快速通关并理解编程在游戏中的落地方式。压缩包共4个文件&#xff0c;包含Python主脚本、inscode配置、HTML说…

作者头像 李华
网站建设 2026/10/18 14:41:05

餐饮扫码点单方案选型:单店、连锁、堂食、外卖四类场景的能力差异

扫码点单方案的能力差异&#xff0c;主要体现在四类场景&#xff1a;单店堂食、连锁门店、外卖接单与到店自提。本文以收钱吧、美团收银、客如云、二维火为样本&#xff0c;按点单链路、菜单管理、跨店归集、上线验收四个维度对照&#xff0c;给出分场景的选型参考。评测声明&a…

作者头像 李华
网站建设 2026/10/18 14:41:04

Vibe Coding 是什么?从 Cursor Base URL 改到 TaoToken 的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 14:38:59

or-tools人员排班建模实战:CP-SAT求解器与JavaAPI实现

简介&#xff1a;面向Java开发者与运筹优化初学者的OR-Tools人员排班建模求解示例包&#xff0c;聚焦IT行业常见的调度与资源配置难点。资源通过Java API&#xff0c;将员工技能匹配、工作时长上限、连续工作日之间至少休息一天等实际约束转化为整数规划模型&#xff0c;并调用…

作者头像 李华
网站建设 2026/10/18 14:37:53

大模型辅助论文写作:5天完成高质量初稿的实操流程

先说一个我见过很多次的场景&#xff1a;拿到大模型之后&#xff0c;第一反应是让它直接“帮我写一段正文”&#xff0c;结果生成的内容看起来像模像样&#xff0c;放回论文里全是正确的废话。另一批人又走到另一个极端&#xff0c;坚持全部手写&#xff0c;AI只用来查文献&…

作者头像 李华