A user-defined function extends MySQL with native code loaded from a shared library in the plugin directory. The lib_mysqludf_sys library exposes sys_exec (run a command) and sys_eval (run a command and return its output), which give command execution as the MySQL service account straight from SQL.
The chain has three steps, each needing privilege:
- Write the library into the plugin directory with
INTO DUMPFILE(needsFILE, a permissivesecure_file_priv, and a writable@@plugin_dir). - Register the function, which needs
CREATE FUNCTION(part of the admin privileges):
CREATE FUNCTION sys_exec RETURNS INT SONAME 'lib_mysqludf_sys.so';
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';
- Call it:
SELECT sys_eval('id');
sys_eval('id') returns the command output as a string, so it reads back in-band; sys_exec returns only an exit status and is used for fire-and-forget actions such as adding a user or starting a reverse shell.
This route depends on stacked queries or a query context that allows CREATE FUNCTION, which the common PHP drivers do not provide, so in practice it is reached through an admin console, a multi-statement client, or an injection in a context that permits multiple statements. The payoff is execution as the database service account rather than the lower-privileged web user a web shell runs as.
Tools#
- sqlmap (
--os-shellautomates the OUTFILE and UDF routes) - lib_mysqludf_sys
References#
- MySQL Reference Manual: CREATE FUNCTION (UDF),
plugin_dir - OWASP Testing Guide: Testing for SQL Injection