📦 MySQL
What is it?
- Concept: MySQL (and its fork MariaDB) is the default relational database under most PHP web applications. It listens on
3306, though on single-host deployments it is usually bound to127.0.0.1and reached only through the application. For an attacker it is rarely the entry point and almost always the second stop — the place the application keeps its user table. - Impact: Access to the application’s schema means the full user list and their password hashes, plus whatever business data the application stores. Where the database user has
FILEprivilege or the server runs as root, it also becomes a file read/write primitive.
How it works
- Credentials for the application’s database account live in a config file in the webroot —
config.inc.php,wp-config.php,.env,settings.pydepending on the stack — in plaintext, because the application needs them at runtime. - Authentication is
user@host:'app'@'localhost'and'app'@'%'are distinct accounts with independent grants, which is why a credential that works locally may be refused remotely. - Password hashes for application users are stored in an application table (
users,zz_users,wp_users) using whatever algorithm the framework chose — bcrypt ($2y$) is the modern norm. - MySQL’s own account passwords are separate, stored in
mysql.user, and irrelevant to cracking application logins.
Security Quirks & Niche Facts
- The config file in the webroot is the pivot, and it is always plaintext. Any RCE that lands as the web user hands over the database on the next
cat. - Identify the hash before cracking, not after.
$2y$/$2a$is bcrypt (hashcat -m 3200),$1$is md5crypt (-m 500), a bare 32-hex string is raw MD5 (-m 0), 40-hex is SHA-1 (-m 100). Getting the mode wrong wastes the whole run. - bcrypt is slow but not a wall.
rockyou.txtagainst a handful of hashes finishes in minutes on a laptop, and business-application users pick business-application passwords. - Database users and system users share names far more often than they should — an application account cracked out of a user table is worth trying against
suand SSH immediately. mysql -p<password>inline works,mysql -p <password>does not — the latter reads the argument as the database name. Prefer-palone and type it at the prompt, which also keeps the password out ofpsand shell history.mysqlneeds a TTY for its password prompt. From a raw reverse shell it will fail confusingly; upgrade the shell first.SHOW GRANTSbefore assuming limits.FILEprivilege turns the connection intoLOAD_FILE()reads andINTO OUTFILEwrites;secure_file_privis what usually blocks it, and it is empty in careless deployments.- Remote
3306on an external scan is unusual and worth noting; the common case is localhost-only, requiring a foothold or a forwarded port to reach.
Notes
- Standard triage once connected:
show databases;→use <app_db>;→show tables;→ find the user table →select * from <users>;. - Dump hashes to a file with one hash per line and nothing else before feeding Hashcat — stray usernames or pipes in the file cause silent line-skips.
select user,host from mysql.user;shows which accounts are permitted from where, which explains a credential that works locally but not over a forwarded port.
Mitigation
- Fix: Store application database credentials outside the document root, restricted to the service user, and rotate them whenever a web-tier compromise is suspected.
- Grant each application a least-privilege account scoped to its own schema — no
FILE, noSUPER, no cross-database access. - Bind to
127.0.0.1unless remote access is genuinely required, and use TLS where it is. - Enforce password policy on application accounts; bcrypt only buys time against a weak password, it does not save it.
Related Usage
| File | Created |
|---|---|
| HTB Machine - Enigma | September 07, 2026 |
References: