Overview
This note documents what an attacker can see and abuse from an interactive mysql>
prompt, using the Altered box as a training ground. It moves from enumeration, through
file read/write, to remote code execution via user-defined functions (UDF).
Context for Altered: the application database account holds the FILE privilege (which
is why INTO OUTFILE succeeds), and the mysql process runs as its own dedicated user
rather than root. That makes it an ideal environment for practising UDF and general_log
tricks.
| Primitive | Requires | Impact | Fix |
|---|---|---|---|
LOAD_FILE | FILE | Arbitrary file read (secrets, keys) | Remove FILE, narrow secure_file_priv |
INTO OUTFILE / INTO DUMPFILE | FILE + write access to target | Webshell / binary drop | Remove FILE, web root not writable by mysql |
UDF sys_exec | FILE + INSERT on mysql.func + write to plugin dir | Remote code execution (root if mysqld runs as root) | Read-only plugin dir, non-root mysqld, AppArmor |
general_log write | SUPER | Write that bypasses secure_file_priv | No SUPER for the application account |
Dump mysql.user | SELECT on mysql.* | Hash theft, crack or reuse | Least privilege, strong passwords |
The escalation ladder from a database foothold is: read data -> read system files -> write system files -> execute code -> shell (potentially root). Each rung requires a different privilege, which is why privilege enumeration comes first.
Reconnaissance
The first 60 seconds in mysql> establish three things: who you are, what you can do,
and where you can write.
| |
Establish whether you hold FILE, the value of secure_file_priv, and the location of
plugin_dir. These three facts determine which of the techniques below will work.
Initial Access
File read with LOAD_FILE()
LOAD_FILE() reads any file readable by the mysql user, within the bounds of
secure_file_priv.
| |
This works because of the FILE privilege plus a file readable by the MySQL process.
A database foothold is frequently an arbitrary file read across the whole host, not just
over its own data, which makes it excellent for collecting secrets and pivoting.
Fix: revoke FILE from the application account and set secure_file_priv to a narrow
directory.
File write: INTO OUTFILE vs INTO DUMPFILE
This distinction is critical and frequently confused.
INTO OUTFILE | INTO DUMPFILE | |
|---|---|---|
| Format | Adds tabs and newlines, escapes characters | Raw write, byte for byte |
| Rows | Many | Exactly one |
| Use | Text files, simple webshells | Binaries (for example a .so for UDF), files that require exact bytes |
| |
If you try to write a .so through OUTFILE, escaping damages the ELF and the UDF will
not load. For binaries, always use DUMPFILE.
UDF to command execution
This is the key technique: moving from a database account to system command execution as the MySQL process user. If MySQL runs as root, this is root RCE.
MySQL allows loading user-defined functions from .so libraries in the plugin_dir
directory. The public lib_mysqludf_sys library exposes sys_exec() and sys_eval(),
which call system(). Given FILE (to write the .so) plus INSERT on mysql.func
(to register the function), you get command execution.
Requirements:
FILEprivilege plus write access to@@plugin_dirsecure_file_privdoes not block writes to the plugin directory (''is ideal)- a
.soof the correct architecture (x86_64)
| |
| |
| |
sqlmap ... --os-shell (or --os-pwn) does exactly this under the hood. Doing it manually
once is worthwhile for understanding.
On Altered this gives a shell as the mysql user rather than root, but it is still lateral
movement plus an understanding of a technique that on many boxes yields root directly.
Fix: no FILE for the application account; a plugin directory that is not writable by the
mysql user; MySQL not running as root; AppArmor or SELinux confining mysqld.
general_log to webshell (bypassing secure_file_priv)
When secure_file_priv blocks INTO OUTFILE but you hold SUPER or
SYSTEM_VARIABLES_ADMIN, you can write a file through the query log, because the log path
is not subject to secure_file_priv.
| |
The log now contains your query (with the PHP code) and sits in the web root as a .php
file. The same arbitrary-write primitive has multiple realisations; when one path is
blocked, look for another.
Fix: do not grant SUPER to the application; monitor changes to general_log.
Lateral Movement
Harvesting and cracking hashes
| |
Identification and hashcat modes:
mysql_native_passwordproduces a*HEX40hash (SHA1(SHA1(pass))), hashcat-m 300caching_sha2_password(default since MySQL 8) is much harder and slower
| |
On Altered, the application hashes in uhc.users are bcrypt $2y$ (not mysql.user),
so hashcat -m 3200. They are deliberately useless (players rotate them), but they are
good practice for recognising formats and the cost of bcrypt. Test the password from
.env (DB_PASSWORD) against system and SSH accounts; password reuse is common.
Session and information_schema recon
| |
information_schema is a map of the entire database; PROCESSLIST on production can
reveal other services’ queries with secrets in them.
Privilege Escalation
Persistence primitives, documented so they can be detected:
A new database user with full rights, a quiet database backdoor:
1 2CREATE USER 'svc'@'%' IDENTIFIED BY 'x'; GRANT ALL ON *.* TO 'svc'@'%' WITH GRANT OPTION;A trigger or
EVENTthat runs onINSERTor on a scheduleA UDF left registered in
mysql.func, for persistent RCE
Blue-team detection: audit mysql.user and mysql.func, alert on
CREATE FUNCTION / CREATE USER / GRANT, monitor changes to general_log and
plugin_dir, and check the integrity of .so files in the plugin directory.
Lessons Learned
- Privilege enumeration first:
FILE,secure_file_privandplugin_dirdecide which techniques are available. LOAD_FILEturns a database account into an arbitrary file read across the host.- Use
DUMPFILEfor binaries,OUTFILEfor text;OUTFILEcorrupts ELF files. - UDF is the bridge from “read and write files” to “execute code”.
- The
general_logtrick is an alternative write primitive whensecure_file_privblocksOUTFILE. - Passwords found in
.envor configuration files should always be tested against SSH and other services.
Command Reference
| |
| |