sqli-labs Less 1 新手教程:SQL注入从判断注入点到联合查询脱库
sqli-labs 是我见过最适合新手入门 SQL 注入的靶场没有之一。Less 1 又是整个靶场的第一关难度最低但它把 SQL 注入最核心的“判断注入点、确认列数、找显示位、联合查询脱库”这条完整链路都串起来了。我第一次接触 SQL 注入就是从这一关开始的当时照着网上的教程一步步敲磕磕绊绊半天才搞明白每个参数为什么要这么改。这关如果吃透了后面几十关的核心思路都会顺畅很多。这篇文章我会把 Less 1 从环境准备到最终拿到数据的全过程完整走一遍每一步都会解释“为什么要这么做”而不是单纯丢给你几条 SQL 语句。适合完全零基础、刚搭好靶场但不知道从哪下手的初学者也适合对注入原理一知半解、想回头补基础的人。1. 环境搭建与靶场准备1.1 本地实验环境怎么配sqli-labs 是一个基于 PHP MySQL 的靶场所以本地环境需要能跑 PHP 和 MySQL。新手最省事的方案是装一个集成环境常见的几款各有优劣phpStudy国内用的人多启动快自带 PHP 和 MySQL非常适合新手。注意选一个稳定的版本别追最新版。XAMPP跨平台干净清爽适合喜欢自己动手折腾的。默认面板是英文对部分新手可能有点门槛。LAMP / LNMP 手动部署适合有一定 Linux 基础的人熟悉一下原生环境没有坏处但对纯新手不推荐容易在环境问题上卡住。我用的是 phpStudy版本是较稳定的 2018 版PHP 版本选的 5.6后面会说为什么不建议用 7.x。安装完成后把 Web 服务器的根目录设为sqli-labs所在的目录然后启动 Apache 和 MySQL。1.2 靶场源码部署与常见坑从官方仓库把靶场源码下载下来后解压到 Web 根目录比如C:\phpstudy_pro\WWW\sqli-labs。这时候访问http://localhost/sqli-labs/会看到靶场的首页。第一次进来要初始化数据库页面顶部会提示你创建一个数据库。正常流程是点击“Setup/reset Database”链接它会自动创建security库和对应表。这一关数据量不大整个过程几秒钟。我遇到过几个常见问题提前写在这里给你避坑页面能打开但点链接报数据库连接错误大概率是数据库账号密码不对。默认配置里连接信息写在sql-connections/sql-connect.php里确认用户名和密码跟你环境里 MySQL 的一致。创建数据库时报错先检查 MySQL 是不是真的启动了再确认当前用户有没有建库权限。phpStudy 默认 root 用户密码是空的如果改了密码记得同步改靶场配置文件。PHP 版本问题如果你用的是 PHP 7.x 或更高版本某些代码会报mysql_*函数未定义的错误因为老靶场用的是 PHP 5 时代的mysql_系列函数。最简单的办法是切换 PHP 版本到 5.6或者开启兼容扩展。环境一旦通了能正常打开 Less 1 的页面接下来的所有操作就都能在自己电脑上复现。这也是靶场最大的价值你可以随便折腾不用担心把别人的系统搞坏。2. SQL 注入原理与 Less 1 关卡分析2.1 为什么会存在 SQL 注入漏洞要理解 Less 1得先明白一句话SQL 注入的本质是用户输入被拼进了 SQL 语句并且被执行了。举个生活化的例子。你在一家餐厅点餐菜单上是固定的菜品。正常流程是你在菜单上勾选“红烧肉”后厨照着菜单做菜。但是如果服务员偷懒直接把你的原话当成菜名递给后厨你说“红烧肉再给我两瓶酒”后厨可能真会照做。SQL 注入就是这么回事——程序把用户输入的参数原封不动地塞进 SQL 查询里没有做任何过滤和校验。Less 1 的后台查询逻辑大致是这样$sql SELECT * FROM users WHERE id$id LIMIT 0,1;注意看这里的$id是从 URL 参数?id直接取出来的外面加了一对单引号。如果你正常请求?id1最终执行的 SQL 就长这样SELECT * FROM users WHERE id1 LIMIT 0,1看起来没什么问题。但如果我传的id不是数字而是一段精心构造的字符串呢比如id1那 SQL 就会变成SELECT * FROM users WHERE id1 LIMIT 0,1单引号数量不配对SQL 语法错误数据库就会抛异常。网页上通常会把错误信息直接显示出来——这就是我们用来判断注入点的突破口。2.2 请求特征与注入点判断打开 Less 1 的页面点一下“Lets inject”URL 会变成类似这样http://localhost/sqli-labs/Less-1/?id1页面上一大段文字最显眼的是“You are in...”以及一行看似莫名其妙的代码。这行代码其实就是后台查询语句的预览它非常诚实地告诉了你你的输入被直接拼接到 SQL 里了。接下来我们做第一步测试把id1改成id1数字后面加一个单引号回车。你会发现页面变了不再显示“You are in...”而是蹦出一段错误You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 1 LIMIT 0,1 at line 1不要急着跳过这段报错它透露了三个关键信息后台 SQL 的闭合方式是单引号。因为多加了一个单引号导致语法错误报错信息里出现了1这正好对应查询语句WHERE id$id中夹着输入的那两个单引号。SQL 语句末尾还有一段LIMIT 0,1。这是靶场自己加的限制只显示一条结果。我们后面联合查询时不用管它只需要在输入末尾加上注释符把 LIMIT“吃掉”就行。报错信息直接回显到页面上。这意味着后面的手工注入里我们能看到很多有用的数据库反馈信息。到这一步已经可以确认这个参数存在字符型注入并且是通过单引号闭合的。但还不够我们还需要确认查询结果是几列数据也就是字段数量。这一步是联合查询的前提也是 Less 1 的核心考点。3. Less 1 超详细实战过程3.1 第一步判断字段数量知道存在注入之后第一步就是确定当前的查询返回了几个字段。最经典的办法是用ORDER BY子句。ORDER BY在 SQL 里是用来排序的它后面可以跟列名也可以跟数字。数字表示“按第几列排序”。如果指定的列数超出了查询结果的总列数数据库就会报错。比如我们依次尝试?id1 ORDER BY 1 -- ?id1 ORDER BY 2 -- ?id1 ORDER BY 3 -- ?id1 ORDER BY 4 --前三次页面都正常显示到了ORDER BY 4时又报错了报错信息里会出现Unknown column 4 in order clause。这说明当前查询一共只有 3 列。为什么 1、2、3 都行4 就不行因为users表恰好有 3 个字段ORDER BY最多只能排到 3。这一步的结果决定了后面UNION SELECT里我们得填几个占位符。这里有个细节值得注意我用的注释符是--不是#也不是单纯的--。原因后面专门讲先记住这个写法。3.2 第二步构造 UNION SELECT知道了 3 列就可以上UNION SELECT了。UNION的作用是把两条查询语句的结果合并在一起但有个硬性条件前后两条查询返回的列数必须相等。所以我们这么写?id1 UNION SELECT 1,2,3 --页面还是正常显示但等一等细看页面中的内容会发现它多显示了两行原本一张表格的位置出现了2和3。这说明什么说明UNION SELECT 1,2,3里的第 2 个和第 3 个字段被直接展示在页面上。为什么要这么做因为我们要知道哪些列的数据会回显到前端。如果页面上显示 2 和 3就代表第 2 列和第 3 列的数据能让我们看到。后面我们把 2 和 3 替换成数据库的函数或字段名就能把信息读出来。但这里有个小坑现在的输出是 1、2、3因为第一条查询其实也返回了一行数据就是id1那条。为了让页面显示UNION SELECT的这部分通常把前面的id改成不存在的值比如-1?id-1 UNION SELECT 1,2,3 --改成-1后第一条查询结果为空页面上就只显示UNION SELECT产生的2和3了回显更干净。这一步也解释了为什么很多注入教程里喜欢用-1而不是1。3.3 第三步获取数据库名与版本信息拿到显示位之后就可以利用 MySQL 自带的信息函数了。先把第 2 列替换成database()第 3 列替换成version()看数据库名字和版本号?id-1 UNION SELECT 1,database(),version() --页面上第二块会显示当前数据库名第三块会显示 MySQL 版本号。我这边执行完得到的结果是security和5.5.53。这一步的意义比单纯拿个名字重要得多database()告诉我们当前所在的数据库是security后面查表时要在security库下找。version()告诉我们 MySQL 版本如果版本高于 5.0就可以用信息字典information_schema来查全库的表结构如果是 4.x 老版本那只能用暴力猜测表名的方式难度会大不少。在 MySQL 5.0 以上版本中information_schema是一个系统库里面藏着所有数据库的元数据信息。这是脱库的钥匙。3.4 第四步查询所有表名拿到数据库名之后就要看这个库里面有哪些表。我们用information_schema.tables来查?id-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schemasecurity --这里的group_concat()函数可以把多行结果拼成一行字符串非常实用。不加它的话如果有多张表页面上只会显示第一张表的表名因为每行结果只有一列能回显。我在本地执行的结果是emails,referers,uagents,users一共四张表重点肯定是users。为什么直觉上它最容易存用户名和密码而且前面报错信息里看到的SELECT * FROM users恰恰印证了这一点。3.5 第五步查询列名知道表名之后下一步查列名。用information_schema.columns?id-1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_schemasecurity AND table_nameusers --执行后页面上显示id,username,password很标准的三个字段id是用户编号username是用户名password是密码。这也正好对应ORDER BY那一步判断出来的 3 列结构。3.6 第六步脱库拿数据列名都齐了剩下就是直接查数据。把username和password都拼在显示位上用group_concat确保所有行都能看到?id-1 UNION SELECT 1,group_concat(username),group_concat(password) FROM security.users --页面上哗啦啦列出来一堆用户名和密码第一关到此就算通关了。整个过程中其实我们完全可以在第三步之后直接用select * from users一把梭把整张表的内容拉出来。但从教学角度看一步一步拆解表名、列名、数据会让你对information_schema的使用有更扎实的理解。后面在更复杂的场景里比如需要绕过某些过滤时这种拆解能力会派上大用场。4. 实战中的报错与排查实录4.1 为什么我用 -- 而不是 -- 或这是新手最容易卡住的地方之一。SQL 标准里--是注释符但它后面必须跟一个空格才能生效。你直接写?id1 --在 MySQL 里是有效的因为--后面有一个空格。但如果你在浏览器地址栏里输入--加空格空格会被 URL 编码成%20或者被某些浏览器吞掉导致注释不生效。#号在 MySQL 里也是注释符而且不需要加空格。但麻烦的是URL 里的#会被浏览器当成“页面锚点”处理请求根本不会把#后面的内容发到服务器。所以直接用#也不行除非把它写成%23。--之所以能通杀大多数场景是因为在 URL 解码后会变成空格变相满足了--后必须跟空格的要求。所以在手工注入时我几乎都用--。但也别死记这一个有些 Web 层过滤会对--做拦截到时候还得灵活换%23或--%20。4.2 union select 出来还是原页面内容出现这个现象通常有两个原因一是前面提到的第一条查询有结果。如果?id1本身有数据页面优先渲染的是第一条查询的结果你用UNION追加的数据根本没有在显示位展示。解决方法是把id改成不存在的值比如-1或者999。二是列数对不上。UNION SELECT 1,2,3如果跟前面查询的列数不一致数据库直接报错。这时候回到ORDER BY那一步重新确认列数。4.3 回显内容被截断group_concat默认有长度限制默认group_concat_max_len是 1024如果数据量非常大结果会被截断。我们这关只有十几个用户完全不用担心。但如果后续在数据量大的场景下脱库可以顺便加上?id-1 UNION SELECT 1,group_concat(username SEPARATOR 0x3c62723e),group_concat(password SEPARATOR 0x3c62723e) FROM security.users --设置group_concat_max_len的方式是?id-1 UNION SELECT 1,group_concat_max_len,3 --或者启动 MySQL 时加上参数但手工注入场景下通常只需要对值大一点即可。4.4 页面不显示任何内容但不出错这种情况往往是因为闭合方式判断错了。Less 1 是单引号闭合如果你用的是数字型注入的思路不带引号?id1 and 11这种测试就会显得“没有反应”因为整个 SQL 变成了SELECT * FROM users WHERE id1 and 11 LIMIT 0,1这里1 and 11被当成了字符串查不到数据页面自然显示不出来。解决办法是回到最开始的引号测试先把闭合方式试出来。5. 从 Less 1 延伸出去的注入思路5.1 字符型与数字型的判断差异Less 1 是典型的字符型注入输入的参数被单引号包起来了。在真实环境中你还会碰到数字型注入参数没有引号包裹直接拼进 SQL。判断方法其实很简单在参数后面加and 11和and 12如果页面表现不一样基本就是数字型如果毫无变化大概率是字符型还得再补一个引号试试。这背后的逻辑是数字型注入的参数直接参与运算and 11为真、and 12为假结果自然不同。字符型注入的参数被当成了字符串的一部分加and 11只会让整个字符串查询不到结果。5.2 拿到数据之后能做什么很多人打完靶场就结束了但真实场景里拿到数据之后还有很长的路要走。密码字段在真实系统中通常不是明文而是哈希值比如 MD5、SHA1 等。你还要根据哈希特征判断算法然后用在线字典或彩虹表去解。Less 1 里的密码是明文纯粹是为了教学方便。另外数据之外的东西往往更有价值比如数据库版本、当前用户权限、是否支持文件读写、能不能拿到 UDF 或into outfile写入一句话木马。这些在后续的 Less 关卡里都会陆续出现Less 1 只是起点。5.3 怎么把这一关的能力用到后续关卡sqli-labs 的关卡难度是递进的。Less 2 是数字型注入Less 3 和 Less 4 分别是有单引号括号和双引号括号包裹的注入。你会发现核心手法完全一样变化的只是“怎么把输入从引号的包裹里逃出来”。Less 5 之后开始进入报错注入、布尔盲注、时间盲注和无回显注入这些对 SQL 基础的要求会明显提高。我的建议是Less 1 这关不要只满足于“跑通”最好能把手动拼 SQL 的过程重复三到五遍直到不看笔记也能从ORDER BY一路写到group_concat脱库。肌肉记忆在这种基础关卡里特别重要后面遇到复杂的 WAF 绕过时你才能把注意力放在绕过策略上而不是卡在基础语法上。6. 总结Less 1 带给我的实战启发我自己在带新人或者写教学材料的时候反复强调一个观点Less 1 看上去简单但它教会你的是一整套 SQL 注入的思维方法——每一步都去确认、把自己放在数据库执行的角度去思考“这条 SQL 会变成什么样”。当你习惯了这种思维再去看后面的 Less 2、Less 3其实就是在处理“如何把注入载荷构造得能让后台 SQL 按你的意愿执行”这件事。最后再分享一个小经验动手敲 SQL 的时候最好自己开一个 MySQL 命令行把后台拼接完成的语句手动复制进去执行一遍。比如查到某个 payload 之后先在命令行里跑一次SELECT * FROM users WHERE id-1 UNION SELECT 1,2,3 --看看真实的输出长什么样再对比网页上的回显。这样做几次你对 SQL 语句的敏感度提升得比你想象中快很多。靶场存在的意义不是让你复制几段 payload而是让你真正理解每个参数背后的数据库逻辑。

相关新闻

LNTON烟火识别本地部署全指南:CUDA/cuDNN/Python环境避坑实战

LNTON烟火识别本地部署全指南:CUDA/cuDNN/Python环境避坑实战

简介:本资源是面向安防监控、智能视觉开发及边缘AI部署工程师的烟火与烟雾识别实用工具包,基于LNTON羚通算法实现高精度实时检测,适用于森林防火、工厂安全巡检、仓储物流等高风险场景的早期预警需求。压缩包共874个文件,含843张标…

2026/10/16 12:44:23 阅读更多 →
淘宝item_get接口调用全攻略:从签名机制到高频错误排查

淘宝item_get接口调用全攻略:从签名机制到高频错误排查

做电商开发的,没人能绕开商品详情这个接口。不管你是做ERP、做比价工具,还是做数据分析,只要有商品信息同步的需求,最后都会撞到同一个地方:淘宝开放平台的item_get。这个接口我前前后后用了几年,踩过坑也填…

2026/10/16 12:44:23 阅读更多 →
OpenCV图像去噪原理与工业级实战指南

OpenCV图像去噪原理与工业级实战指南

1. 为什么一张图的噪点处理,真能绕开PS?“从去除噪点的说起,有OpenCV要什么PS?”——这句话刚在技术群被转发时,我正帮某高校实验室处理一批显微成像数据。他们用的是一台老款CMOS传感器相机,拍出来的细胞切…

2026/10/16 12:43:19 阅读更多 →

最新新闻

跳表原理与实现:有序动态集合的工程优选方案

跳表原理与实现:有序动态集合的工程优选方案

1. 为什么跳表在工程实践中比红黑树更“顺手”?我第一次在某跨平台系统里看到跳表被用来替代红黑树做有序集合时,第一反应是:这玩意儿真能扛住高并发写入?毕竟教科书里红黑树是“标准答案”,AVL、B树、哈希表都各有定位…

2026/10/16 13:20:23 阅读更多 →
C#上位机与西门子PLC通信实战:S7协议伺服与IO控制全解析

C#上位机与西门子PLC通信实战:S7协议伺服与IO控制全解析

做上位机开发这些年,接触最多的就是和西门子PLC打交道。最近在做一个设备控制的模拟项目,需要在PC端通过C#开发和西门子PLC通信,既控制伺服电机完成定位,又要处理一堆数字量输入输出信号。这种需求在自动化设备里太典型了&#xf…

2026/10/16 13:20:23 阅读更多 →
像素压缩的奥秘:InternVL2.5-26B-MPO-AWQ 的 Pixel Shuffle 与动态分辨率机制拆解

像素压缩的奥秘:InternVL2.5-26B-MPO-AWQ 的 Pixel Shuffle 与动态分辨率机制拆解

【免费下载链接】InternVL2_5-26B-MPO-AWQ 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL2_5-26B-MPO-AWQ 点击查看 免费下载 InternVL2_5-26B-MPO-AWQ 是开源多模态大模型 InternVL2.5-26B-MPO 的 AWQ 4-bit 量化版本,它通过 Pixel Shuffle 像…

2026/10/16 13:20:23 阅读更多 →
CPU飙高≠服务器卡:从指标到实战的排查指南

CPU飙高≠服务器卡:从指标到实战的排查指南

CPU 飙高不等于服务器卡,这个话题我在运维一线见过太多误解了。很多同事一看到监控面板上 CPU 使用率冲上 90%,第一反应就是“服务器要挂了”,马上重启服务甚至扩容。但实际上,CPU 高和用户体感上的“卡”完全可能是两码事&#x…

2026/10/16 13:20:23 阅读更多 →
Langfuse全链路trace实践:从request_id日志到LLM应用可观测性

Langfuse全链路trace实践:从request_id日志到LLM应用可观测性

没有比"线上排查了半天,最后发现日志一切正常"更让人崩溃的时刻了。上个月我们一个问答类服务的用户连续反馈三次"回答明显跑偏",我拿着日志系统里搜出来的 request_id 逐条翻记录:请求进来了、响应回去了、状态码 200、…

2026/10/16 13:20:23 阅读更多 →
从手写数字识别深入理解交叉熵损失函数原理与实战

从手写数字识别深入理解交叉熵损失函数原理与实战

1. 从“猜数字”说起:损失函数到底在做什么 很多人第一次接触手写数字识别,注意力全在“网络结构怎么搭”“准确率能到多少”上,损失函数往往被当成一行代码随手带过。我刚开始学的时候也这样, loss CrossEntropyLoss() 一写&a…

2026/10/16 13:19:22 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →