SEC.PRIV.GRANT_FILE — An account can read and write files as the server
- Category: security
- Severity: high
- Level: 0
- Confidence: deterministic
- Downtime class: none — the finding is about a privilege, not about a statement
- Stability: stable
- Suites: audit
- Applies to: MySQL 8.4
What it does
LOAD_FILE() reads any file the MySQL server process can read. SELECT … INTO OUTFILE writes one.
Both run as the server's operating-system user, not as the account connecting.
So an injection that reaches an account holding FILE does not have to escalate anywhere. It can
read configuration, keys and other applications' data off the same host, and write a file somewhere
the host will later execute.
The honest limit: secure_file_priv
How far that actually reaches is decided by a server setting the grant knows nothing about:
secure_file_priv | What FILE can touch |
|---|---|
| a directory | only files inside it |
| the empty string | anything the server user can reach |
NULL | nothing — both operations are disabled |
This finding reports the privilege and points at the setting. It deliberately does not claim a reach it has not measured — the setting is a separate fact, and reporting it here would be a second alarm for a finding that belongs with the server settings.
What it looks like
-- Reads and writes files as the SERVER's operating-system user.
GRANT FILE ON *.* TO 'app'@'10.0.0.%';
What to do about it
-- An application account has no use for it.
REVOKE FILE ON *.* FROM 'app'@'10.0.0.%';
-- And on the server, confine what FILE can reach at all:
-- secure_file_priv = /var/lib/mysql-files (a directory)
-- secure_file_priv = NULL (disabled entirely)
A data-import job that genuinely uses LOAD DATA INFILE is the one case worth checking before
revoking — and it is usually better served by LOAD DATA LOCAL INFILE from the client, which needs
no server-side privilege at all.
The account SQLens connects as is never reported here.