1. 为什么我建议每个做Web安全的人都从SQLi-LABS的Less1-7开始如果你刚接触Web安全或者已经做了一段时间的渗透测试但总觉得SQL注入这块知识是散的、不成体系那我真心建议你花两三天时间把SQLi-LABS的前7关老老实实过一遍。我自己当年就是从这7关入门的后来带新人的时候也一直用这套靶场做第一阶段的训练材料。原因很简单Less1到Less7这7个关卡几乎覆盖了SQL注入最核心的几种注入类型——联合查询注入、报错注入、布尔盲注、时间盲注、堆叠注入以及GET和POST两种传参方式的差异。把这7关吃透后面再遇到更复杂的注入场景你脑子里会有一套清晰的判断逻辑而不是靠运气去试。SQLi-LABS本身是一个开源的SQL注入练习靶场用PHPMySQL搭建每个关卡都是一个独立的页面模拟了不同过滤条件和传参方式下的注入场景。它的价值在于每一关都给你一个明确的注入点让你专注于理解注入原理和构造payload而不用花精力去找入口。这对于初学者来说非常友好因为找注入点本身是另一个需要大量经验积累的技能不适合和注入原理的学习混在一起。这篇文章我会按照我自己的学习路径来写从环境搭建开始然后逐关拆解Less1到Less7的注入思路、payload构造、原理分析以及我在实际操作中踩过的坑和总结的技巧。每一关我都会先讲清楚这一关的注入类型和传参方式然后给出完整的注入步骤最后解释为什么这样构造payload。如果你跟着走一遍应该能对SQL注入建立一个比较扎实的认知框架。2. 环境搭建与靶场部署的实操细节2.1 本地环境选型与部署方案搭建SQLi-LABS的环境有好几种方式我试过用集成环境比如PHPStudy、XAMPP直接跑也试过用Docker拉镜像还在虚拟机里手动配过ApachePHPMySQL。对于新手来说我最推荐的是用集成环境因为省去了配置PHP和MySQL的麻烦而且SQLi-LABS本身对PHP版本要求不高PHP5.x到PHP7.x都能跑。具体操作是这样的先装一个PHPStudy或者XAMPP启动Apache和MySQL服务然后把SQLi-LABS的源码解压到网站根目录下。以PHPStudy为例网站根目录通常是WWW文件夹你把解压出来的sqli-labs-master文件夹放进去然后在浏览器访问http://localhost/sqli-labs-master/就能看到靶场的首页。首页会有一个安装向导点击“Setup/reset Database for labs”按钮它会自动创建数据库和表并插入测试数据。这一步如果报错大概率是MySQL的root密码不对你需要去sql-connections文件夹下的db-creds.inc文件里修改数据库连接信息。注意SQLi-LABS的源码里数据库连接配置在sql-connections/db-creds.inc文件中默认用户名是root密码为空。如果你本地MySQL的root密码不是空的需要手动改这个文件否则安装会失败。用Docker的话更简单一条命令就能拉起来但我不建议新手一上来就用Docker因为你对文件结构和数据库配置没有直观感受后面遇到问题不好排查。等你把Less1-7都过了一遍再换成Docker做快速复现会更高效。2.2 数据库与表结构的前置认知在开始注入之前你需要对SQLi-LABS的数据库结构有一个基本了解。靶场默认创建了一个名为security的数据库里面主要有四张表users、emails、uagents、referers。其中users表是注入的核心目标表它有三个字段id、username、password。每一关的注入点基本上都是围绕id这个字段展开的。为什么我要强调这个因为你在做联合查询注入的时候需要知道当前查询返回了几个字段才能构造正确的UNION SELECT语句。而知道目标表的结构能帮你更快地判断注入成功后应该查什么数据。比如你知道users表里有username和password字段那么注入成功后就可以直接查这两个字段的值而不需要先去猜表名和字段名。另外SQLi-LABS的每一关都会在页面顶部显示当前的SQL查询语句在Less1-7中大部分关卡都会显示这对于理解注入原理非常有帮助。你可以直观地看到你的输入是如何被拼接到SQL语句中的以及为什么某些payload能生效而某些不能。3. Less1-4联合查询注入的完整拆解3.1 Less1最简单的GET型字符注入Less1是整个靶场的第一关也是最基础的一关。它的传参方式是GETURL形如?id1注入类型是字符型注入也就是说id参数的值被单引号包裹后拼接到SQL语句中。你可以通过输入?id1来判断是否存在注入如果页面报错说明单引号被带入了SQL语句存在字符型注入。这一关的完整注入流程是这样的首先判断字段数用?id1 order by 3 --如果页面正常显示说明字段数大于等于3用?id1 order by 4 --如果页面报错说明字段数是3。然后确定回显位置用?id-1 union select 1,2,3 --注意这里把id设为-1是为了让前面的查询返回空结果这样页面就会显示union查询的结果。你会看到页面上显示了2和3的位置说明这两个位置可以用来回显数据。接下来就是查数据库信息了。先查当前数据库名和版本?id-1 union select 1,database(),version() --。然后查表名?id-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schemadatabase() --。再查users表的字段名?id-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_nameusers --。最后查数据?id-1 union select 1,group_concat(username),group_concat(password) from users --。提示--中的在URL中会被解析为空格这是注释掉后面SQL语句的常用方式。你也可以用#但在URL中#会被浏览器当作锚点所以需要编码为%23。这一关的难点不在于payload本身而在于理解为什么id-1能让union查询的结果显示出来。因为原查询SELECT * FROM users WHERE id-1返回空集而union查询的结果会追加到原查询结果后面当原查询为空时页面就只显示union查询的结果。这个逻辑在后面所有联合查询注入中都是一样的。3.2 Less2数字型注入的差异与判断Less2和Less1的区别在于Less2是数字型注入id参数没有引号包裹直接拼接到SQL语句中。你可以通过输入?id1来测试如果页面报错但错误信息和你输入的单引号有关说明单引号没有被当作字符串边界而是被当作SQL语法的一部分这就是数字型注入的特征。数字型注入的payload构造比字符型简单因为不需要考虑闭合引号的问题。判断字段数用?id1 order by 3 --确定回显位置用?id-1 union select 1,2,3 --后面的查询步骤和Less1完全一样只是不需要在id值后面加单引号。我在实际测试中经常遇到的一个问题是怎么快速判断一个注入点是字符型还是数字型我的经验是先输入一个单引号如果页面报错再输入and 11和and 12如果and 11页面正常而and 12页面异常说明是数字型注入如果两个都异常说明是字符型注入且单引号没有被正确闭合。这个方法在实战中非常高效不需要反复试payload。3.3 Less3带括号的字符型注入Less3的注入类型是字符型注入但id参数被括号和单引号一起包裹形如WHERE id($id)。这一关的测试方法是输入?id1如果页面报错再输入?id1) --如果页面正常显示说明闭合成功。这一关的payload构造需要在单引号后面加一个右括号来闭合原查询的括号然后用--注释掉后面的内容。比如判断字段数用?id1) order by 3 --确定回显位置用?id-1) union select 1,2,3 --。后面的查询步骤和Less1一样。这一关的价值在于让你意识到注入点的闭合方式可能不止单引号一种还可能有括号、双引号、甚至更复杂的组合。在实际渗透测试中你需要根据报错信息或者页面行为来判断闭合方式而不是死记硬背payload。3.4 Less4双引号加括号的闭合技巧Less4和Less3类似但闭合方式是双引号加括号形如WHERE id($id)。测试方法是输入?id1如果页面报错再输入?id1) --如果页面正常说明闭合成功。这一关的payload构造和Less3一样只是把单引号换成双引号。判断字段数用?id1) order by 3 --确定回显位置用?id-1) union select 1,2,3 --。我在带新人的时候发现很多人在Less3和Less4卡住不是因为payload不会构造而是因为不知道什么时候该加括号、什么时候该加双引号。我的建议是先输入一个单引号或双引号观察报错信息。如果报错信息里显示了完整的SQL语句你就能直接看到闭合方式如果没有显示就尝试在引号后面加右括号再尝试加注释符逐步逼近正确的闭合方式。4. Less5-7盲注与堆叠注入的进阶实战4.1 Less5布尔盲注的判断逻辑与脚本化Less5是布尔盲注关卡页面不会回显任何查询结果也不会显示报错信息只会根据SQL查询是否返回结果来显示不同的页面内容。具体来说如果查询有结果页面显示“You are in...”如果查询无结果页面显示空或者不同的提示。这一关的注入思路和前面完全不同因为你无法通过回显位置来获取数据只能通过构造真/假条件根据页面返回的不同状态来逐字符推断数据。比如你可以用?id1 and length(database())8 --来判断当前数据库名的长度是否为8如果页面显示“You are in...”说明条件为真数据库名长度就是8。布尔盲注的核心是构造一个可以逐字符比较的条件。比如要猜数据库名的第一个字符可以用?id1 and ascii(substr(database(),1,1))100 --通过二分法不断缩小范围最终确定字符的ASCII码值。这个过程如果手动做效率极低所以实际测试中都是用脚本自动化。我用Python写过一个简单的布尔盲注脚本核心逻辑就是遍历每个字符位置用二分法猜ASCII码值。脚本的关键是要能准确判断页面返回的真假状态通常是通过检查页面中是否包含某个特定的字符串比如“You are in...”来实现。这个脚本的框架在后面的时间盲注中也能复用只需要把判断条件从页面内容改成响应时间。注意布尔盲注的payload中substr()函数的起始位置从1开始不是从0开始。这一点和很多编程语言不同容易搞错。4.2 Less6双引号闭合的盲注变体Less6和Less5的区别在于闭合方式Less6是双引号闭合形如WHERE id$id。测试方法是输入?id1如果页面显示异常再输入?id1 and 11 --和?id1 and 12 --如果前者正常后者异常说明存在布尔盲注。这一关的payload构造和Less5一样只是把单引号换成双引号。比如判断数据库名长度用?id1 and length(database())8 --猜字符用?id1 and ascii(substr(database(),1,1))100 --。我在实际测试中遇到过一个坑有些靶场环境对双引号的处理和单引号不同比如PHP的magic_quotes_gpc配置可能会影响双引号的转义。虽然SQLi-LABS默认不会开启这个配置但如果你在别的环境里测试需要留意这一点。4.3 Less7文件写入与堆叠注入的实战应用Less7是文件写入关卡它的注入类型是字符型注入但页面不会回显查询结果而是提供了一个“导出文件”的功能。这一关的核心是利用SQL注入的INTO OUTFILE语句将查询结果写入服务器上的文件。这一关的注入思路是这样的首先判断闭合方式输入?id1如果页面报错再输入?id1 --如果页面正常说明闭合成功。然后构造payload用?id-1 union select 1,2,3 into outfile /var/www/html/sqli-labs-master/Less-7/test.php --将查询结果写入指定文件。如果写入成功你就可以通过浏览器访问这个文件看到写入的内容。这一关的难点在于文件路径的确定和文件写入权限的配置。你需要知道Web服务器的根目录在哪里以及MySQL的secure_file_priv配置是否允许写入文件。如果secure_file_priv设置为NULL则不允许写入如果设置为某个目录则只能写入该目录下的文件如果为空则允许写入任意目录。提示在MySQL 5.7及以上版本中secure_file_priv默认可能不是空的你需要手动修改MySQL配置文件my.ini或my.cnf在[mysqld]节下添加secure_file_priv然后重启MySQL服务。我在实际操作中踩过的坑是文件路径写错了导致写入失败但页面没有任何提示。后来我通过查看MySQL的错误日志才发现是路径问题。所以建议你在做这一关的时候先确认Web服务器的根目录路径然后确保MySQL有写入权限。5. 常见问题排查与避坑经验汇总5.1 注入点判断的常见误区很多新手在判断注入点时习惯直接输入and 11和and 12但如果注入点是字符型且没有正确闭合引号这两个payload都会报错导致误判为不存在注入。正确的做法是先输入一个单引号或双引号观察页面是否报错然后再尝试闭合引号并加注释符最后再用and 11和and 12来验证。另一个常见误区是忽略URL编码。在GET请求中特殊字符如空格、单引号、双引号、井号等都需要进行URL编码否则浏览器或服务器可能会截断或错误解析。比如空格要编码为%20或单引号要编码为%27双引号要编码为%22井号要编码为%23。5.2 联合查询注入的字段数判断技巧判断字段数时用order by是最常用的方法但有时候order by会被过滤。这时候可以用union select逐步增加字段数直到页面正常显示。比如先试union select 1如果报错再试union select 1,2以此类推。这个方法比order by慢但在order by被过滤时是有效的备选方案。另外确定回显位置时把id设为-1是为了让原查询返回空结果这样union查询的结果才能显示在页面上。如果id设为一个存在的值原查询会返回结果union查询的结果可能会被覆盖或者不显示。这个细节很多人容易忽略。5.3 盲注效率提升的实用技巧布尔盲注和时间盲注如果手动做效率极低一个字符可能要试几十次。我的建议是第一尽量用二分法而不是线性遍历这样每个字符最多试7次ASCII码范围0-127第二用脚本自动化Python的requests库配合二分法逻辑几分钟就能跑完一个数据库名第三如果目标支持报错注入优先用报错注入因为报错注入可以直接回显数据效率比盲注高得多。时间盲注还有一个坑网络延迟会影响判断准确性。如果服务器响应本身就不稳定你设定的延迟阈值可能被误判。我的做法是先测一下正常请求的响应时间然后设定一个明显大于正常响应时间的阈值比如正常200ms阈值设为3秒这样可以减少误判。5.4 文件写入失败的排查思路Less7的文件写入失败通常有以下几个原因一是secure_file_priv配置限制了写入目录二是MySQL用户没有文件写入权限三是文件路径不存在或没有写入权限四是Web服务器和MySQL不在同一台机器上导致路径不匹配。排查的时候先查secure_file_priv的值再确认MySQL用户的权限然后检查文件路径是否存在最后确认Web服务器和MySQL的部署关系。我在实际测试中总结了一个快速排查表你可以对照着检查问题现象可能原因排查方法页面报错但无具体信息闭合方式不对尝试单引号、双引号、括号组合union查询无回显字段数不对或回显位置不对用order by判断字段数用-1让原查询为空盲注判断不稳定网络延迟或页面内容变化增大延迟阈值检查页面特征字符串文件写入失败secure_file_priv限制或权限不足查配置、查权限、查路径6. 从Less1-7延伸出的SQL注入知识体系把这7关过完你其实已经接触到了SQL注入的大部分核心概念。但我想强调的是Less1-7只是一个起点它们帮你建立了注入的基本思维框架但真实的渗透测试场景远比靶场复杂。比如真实环境中可能有WAF过滤、参数编码、二次注入、Cookie注入、HTTP头注入等变种这些在Less1-7中都没有涉及。我的建议是先把Less1-7的每一种注入类型都手动复现一遍理解每一步的原理然后再去挑战Less8以后的关卡。Less8是布尔盲注的变体Less9是时间盲注Less10是时间盲注的变体Less11开始是POST型注入。这些关卡会逐步增加难度帮你把知识体系补全。另外我强烈建议你在学习SQL注入的同时同步学习MySQL的基础语法特别是information_schema数据库的使用、substr()、ascii()、group_concat()等函数的用法。这些是构造payload的基础如果只是死记硬背payload而不理解背后的语法遇到变种场景就会束手无策。最后分享一个我自己的学习习惯每学完一个注入类型我都会在自己的笔记里画一张流程图把从判断注入点到获取数据的每一步都写清楚包括每一步的payload和原理。这样过一段时间再回头看能快速回忆起整个思路也能发现之前理解不到位的地方。SQL注入的知识点其实不多但组合方式非常灵活建立自己的知识框架比记住多少个payload更重要。
阅读完成 · 觉得有帮助?