目录
一、绕过
1、过滤逗号
(1)盲注中的substr("string",1,3) 用from for 替换
substr("string" from 1 for 3)
(2)联合查询时 union select 1,2,3
用join和别名绕过
union select * from (select 1) a join (select 2) b join (select 3) c
(3)limit 1,1
用offset绕过
limit 2,1 = limit 1 offset 2
2、函数替换
(1)sleep() = benchmark()
(2)substr() , substring() , mid() 三个可相互替换
取子串函数还可以有 left() , right()
(3)ascii() = ord() ,在处理英文时是一样,中文是有不同
3、过滤引号
(1)宽字节注入,将 \ 吃掉
(2)十六进制
(3)编码
4、过滤空格
(1)/**/ = 空格
(2)内联注释 /*!select*/1,2;
(3)%0a 在url栏中等于空格
(4)反引号包表名 select * from`tables`;
(5)括号将子查询包起来
(6)浮点数 where id=8E0union 或 where id=8.0union
5、过滤注释符
直接闭合,不注释
6、过滤比较符
(1)greatest(n1,n2,n3....) //返回最大值
(2)least(n1,n2....) //返回最小值
(3) between and
7、过滤等号
like绕过
where username like 'admin';
二、PDO预编译
预编译:参数化查询,将sql语句参数化,不会将参数部分当作sql语句的一部分执行;语句是语句,参数是参数,将整个sql语句功能提前定死,消除歧义
1、模拟预编译
一个参数——PDO::ATTR_EMULATE_PREPARES,这个选项用来配置PDO是否使用模拟预编译,默认是true
查看日志:
2023-10-22T15:34:50.356115Z 11 Connect root@localhost on test using TCP/IP
2023-10-22T15:34:50.356353Z 11 Query SELECT password FROM test where username= 'root\' union select database()#'
2023-10-22T15:34:50.357303Z 11 Quit
本质是对符号进行了过滤,没有进行参数绑定和预编译
2、真正的预编译
查看日志:
(1)建立连接
(2)构建语法树 --(先准备语句,用 ? 占位)
(3)执行 --(将输入的值替换成 ? 执行)
三、预编译下的注入点
1、宽字节注入
只能针对模拟预编译
为什么真正的预编译不能吃掉\
因为在设置编码后,日志里的查询参数都被hex了
2023-10-26T01:20:47.891775Z 23 Prepare SELECT password FROM test where username= ?
2023-10-26T01:20:47.891842Z 23 Execute SELECT password FROM test where username= 0x31DF2720756E696F6E2073656C65637420646174616261736528293B23
2023-10-26T01:20:47.892337Z 23 Close stmt
2023-10-26T01:20:47.892379Z 23 Quit
2、没有参数绑定
没有参数绑定的预编译等于没有预编译
3、无法预编译的位置
order by,group by 后面不能参数化,不能进行预编译,预编译自动加上引号,而order by,group by 后面一般跟字段,如果加上引号语义就变了,就会查不到信息
总而言之就一个思路,不能加引号的位置就不能预编译。