CVE-2023-1177:MLflow中的LFI/RFI

LFI/RFI導致系統和云帳戶被接管

所有CVE在版本2.2.2中已經被修復

已發布了漏洞利用工具和掃描工具 

機器學習系統領域最流行的工具之一是MLflow(月下載量超過1300萬人次,且這個數字還在增長),它用于管理端到端機器學習生命周期。

Protect AI測試了MLflow的安全性,結果發現了一個混合型的本地文件包含/遠程文件包含(LFI/RFI)漏洞,可能導致整個系統或云提供商被人接管。強烈建議運行MLflow服務器的組織立即更新到最新版本,或者至少更新到2.2.2版本。版本2.2.1修復了CVE-2023-1177,版本2.2.2修復了CVE-2023-1176。我們在本博文中探討了該漏洞的影響、如何檢測它,以及我們發現這個嚴重漏洞的過程。如果你正在運行MLflow,請使用本博文中提供的免費工具,立即開始修補系統。使用傳統工具給系統打補丁可能是一個挑戰,因為許多自動化補丁管理系統并不枚舉或識別MLflow,就算枚舉或識別,可能也不會執行版本檢查。

立即升級到MLflow的最新版本非常重要,哪怕你的實例不在生產環境中,只在開發環境中使用。

影響

如果利用該漏洞,未經身份驗證的遠程攻擊者可以讀取啟動了MLflow服務器的用戶可以訪問的這臺服務器上的任何文件。

可以通過從MLflow服務器獲取私有SSH密鑰或云服務提供商憑據來獲得遠程代碼執行的機會。這讓攻擊者得以遠程登錄到服務器或云資源,并利用找到的憑據擁有的相應權限執行任意代碼。

漏洞細節

不需要用戶交互。

不需要事先了解環境。

MLflow的所有自定義配置都易受攻擊,包括開箱即用的安裝環境。

MLflow 2.1.1之前的所有版本都容易受到LFI的攻擊。

漏洞利用工具適用于從MLflow v1.12到 v2.1.1的所有版本。

MLflow 2.0之前的所有版本都容易受到LFI的攻擊,只需通過更簡單地利用:http://

MLflow維護者迅速響應了負責任披露的這個漏洞,在短短幾周內交付了修復程序。MLflow 2.1.1之后的版本不再容易受到攻擊。

漏洞檢測

若要檢查你的MLflow服務器是否容易受到攻擊,請使用我們的免費CVE-2023-117-scanner掃描工具(https://github.com/protectai/Snaike-MLflow)。

發現過程

我們先安裝了MLflow,啟動攔截代理BurpSuite以攔截所有MLflow API調用,運行用數據填充MLflow的實驗,然后啟動UI服務器作進一步探索。

# Download MLflow source to get access to their example runs

git clone https://github.com/mlflow/mlflow

# Create and enter new directory outside the mlflow/ directory

mkdir mlflowui

cd mlflowui

# Copy the example code from the MLflow source into this new directory

cp -r ../mlflow/examples/sklearn_elasticnet_wine .

# Setup a virtual environment for installing requirements

python3 -m venv venv

source venv/bin/activate

# Install mlflow in this virtual environment

pip install mlflow pandas

# Run the example experiment

mlflow run --env-manager=local sklearn_elasticnet_wine -P alpha=0.5

# Run the UI to see the experiment details

mlflow ui --host 127.0.0.1:8000

在創建實驗時,它給了我們指定存儲對象的目錄這一選項。這似乎是一個可配置的文件路徑,我們可以通過運行的示例實驗就可以看到:

圖1

這立即引起了我們的注意,因為這需要完美地實施過濾機制,以防止本地文件包含或任意文件覆蓋。然而,你無法從UI遠程運行MLflow實驗。由于當你通過UI創建實驗時,工件位置實際上沒有發生任何變化,因此這里沒有任何安全考慮。然后,我們通過點擊單趟實驗運行繼續探索。

圖2

點擊上圖所示的運行名稱,將我們帶到實驗運行細節,在這里我們可以看到實驗涉及的文件,并下載文件,如下圖所示。

在這里,我們在工件文件中看到了一個很大的“Register Model”(注冊模型)按鈕。我們很好奇。

圖3

圖4

它似乎不是什么特別值得關注的對象,因為它只是彈出一個模式,讓你選擇模型,然后將該模型的詳細信息保存為“Version 1”(版本1)。

圖5

但是底層到底發生了什么?為此我們檢查了BurpSuite。

圖6

我們發現了在UI中并沒有顯示的另一個協議和文件路徑輸入。這似乎很可疑。我們將它手動更改為用戶的私有SSH密鑰:file: /// Users/danmcinerney/. ssh/id_rsa。訪問該文件將允許你以啟動了MLflow服務器的用戶的身份遠程登錄到MLflow主機。

圖7

新的source在響應中有所體現,這通常表明服務器端出現了變化。我們很想知道這實現了什么操作,于是回過頭去查看已注冊的模型細節。實驗中沒有什么運行工件,模型細節或模型版本細節中也沒有值得關注的對象。這似乎是另一條死胡同,類似我們發現你可以將實驗工件路徑指向任意位置,但UI隨后不讓你任何操作。然而在檢查BurpSuite請求和響應日志后,我們發現了一些值得關注的異常。

攻擊者現在擁有訪問權

圖8

get-artifact API調用中的“500內部服務器錯誤”讓我們感到可疑。在安全測試的早期,get-artifact API調用值得注意,因為它是從工件存儲庫返回文件數據的調用。這是你從實驗運行下載模型的方法,我們發現它受到了一個防止本地文件包含漏洞的函數的保護,如下所示。

圖9

我們花了一些時間試圖繞過這個,但沒有成功。這個特殊的get-artifact調用的不同之處在于,它不是試圖從子文件夾獲取文件,而是直接訪問文件名。此外,它實際上不是同一個API調用。下面是記入文檔的get-artifact REST API調用:

圖10

下面是類似的model-version/get-artifact調用:

圖11

區別包括URL路徑、參數和值。這顯然不是同一個API調用。

我們注意到這個API調用不在說明文檔中。關鍵區別在于,它通過path URL參數直接查找文件名,而不是通過合法的get-artifact API調用中的相對文件路徑來查找。

這就意味著LFI防護機制并不存在,因為不需要執行目錄遍歷。只需要控制源文件夾的位置。在上面的幾個步驟中,當我們創建一個新的模型版本時,嘗試將API請求的source路徑位置修改為:file:///Users/danmcinerney/.ssh/id_rsa:

圖12

我們應該做的是將source位置更改為文件夾而不是更改為文件。我們糾正了這一點。

圖13

隨后我們重新發送了發現的這個未記入文檔的REST API調用,并將其指向id_rsa,這是新模型版本source位置中的文件以及提供遠程登錄服務器功能的私有SSH密鑰。

圖14

使用檢索到的SSH密鑰,我們就可以通過終端訪問運行MLflow服務器的主機。MLflow最常被配置為使用S3存儲桶作為工件存儲區。如果是這種情況,那么機器上另一個價值非常高的目標將是~/.aws/credentials文件,可想而知該文件存儲的是AWS憑據。

其他高價值目標可能包括包含明文密碼的Web服務器SQL配置文件或包含所有用戶密碼散列的/etc/shadow,這些用戶密碼散列可以通過hashcat之類的工具來破解。

漏洞利用工具

為了幫助保護你的系統,我們創建了一個簡單的工具來發現潛在漏洞,這個工具名為MLFIflow.py(https://github.com/protectai/Snaike-MLflow)。

安裝

git clone https://github.com/protectai/Snaike-MLflow

cd Snaike-MLflow/MLFIflow

python3 -m venv mlfiflow

source mlfiflow/bin/activate

pip install -r requirements.txt

使用

默認情況下,MLFIflow將嘗試從MLflow服務器讀取/etc/passwd,并使用找到的用戶名搜索SSH密鑰和云憑據文件:

python MLFIflow.py -s http://1.2.3.4:5000

若要指定待下載的文件的自定義單詞列表,使用-f標志:

python MLFIflow.py -s http://1.2.3.4:5000 -f /path/to/wordlist.txt