CVE-2023-36827 (published 2023-07-05T22:15Z)

Fides is an open-source privacy engineering platform for managing the fulfillment of data privacy requests in a runtime environment, and the enforcement of privacy regulations in code. A path traversal (directory traversal) vulnerability affects fides versions lower than version `2.15.1`, allowing remote attackers to access arbitrary files on the fides webserver container's filesystem. The vulnerability is patched in fides `2.15.1`.\n\nIf the Fides webserver API is not directly accessible to attackers and is instead deployed behind a reverse proxy as recommended in Ethyca's security best practice documentation, and the reverse proxy is an AWS application load balancer, the vulnerability can't be exploited by these attackers. An AWS application load balancer will reject this attack with a 400 error. Additionally, any secrets supplied to the container using environment variables rather than a `fides.toml` configuration file are not affected by this vulnerability.\n

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/ethyca/fides
commit_details: repo_url: https://github.com/ethyca/fides
author: Sean Preston
author_date: 2023-07-05 15:53:48+01:00
msg: Merge pull request from GHSA-r25m-cr6v-p9hq

* fix for vuln

* Apply suggestions from code review

Use correct spelling of malicious.

Co-authored-by: Adam Sachs <adam@ethyca.com>

* Update src/fides/api/main.py

Verbiage

Co-authored-by: Thomas <thomas.lapiana+github@pm.me>

---------

Co-authored-by: Adam Sachs <adam@ethyca.com>
Co-authored-by: Thomas <thomas.lapiana+github@pm.me>
Co-authored-by: Dave Quinlan <83430497+daveqnet@users.noreply.github.com>
repository: repo_url: https://github.com/ethyca/fides
repo_name: ethyca/fides
description: The Privacy Engineering & Compliance Framework
repo_language: Python
owner: ethyca
file_changes: filename: common_exceptions.py
change_type: ModificationType.MODIFY
diff: @@ -223,6 +223,10 @@ class SSHTunnelConfigNotFoundException(Exception):
     """Exception for when Fides is configured to use an SSH tunnel without config provided."""
 
 
+class MalisciousUrlException(Exception):
+    """Fides has detected a potentially maliscious URL."""
+
+
 class AuthenticationError(HTTPException):
     """To be raised when attempting to fetch an access token using
     invalid credentials.

programming_language: Python
filename: main.py
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,7 @@
 """
 Contains the code that sets up the API.
 """
+import os
 import sys
 from datetime import datetime, timezone
 from logging import WARNING
@@ -11,6 +12,7 @@ from fastapi.responses import FileResponse
 from fideslog.sdk.python.event import AnalyticsEvent
 from loguru import logger
 from starlette.background import BackgroundTask
+from urllib.parse import unquote
 from uvicorn import Config, Server
 
 import fides
@@ -20,6 +22,7 @@ from fides.api.app_setup import (
     log_startup,
     run_database_startup,
 )
+from fides.api.common_exceptions import MalisciousUrlException
 from fides.api.middleware import handle_audit_log_resource
 from fides.api.schemas.analytics import Event, ExtraData
 
@@ -151,6 +154,17 @@ def read_index() -> Response:
     return get_admin_index_as_response()
 
 
+def sanitise_url_path(path: str) -> str:
+    """Returns a URL path that does not contain any ../ or //"""
+    path = unquote(path)
+    path = os.path.normpath(path)
+    for token in path.split("/"):
+        if ".." in token:
+            logger.warning(f"Potentially dangerous use of URL hierarchy in path: {path}")
+            raise MalisciousUrlException()
+    return path
+
+
 @app.get("/{catchall:path}", response_class=Response, tags=["Default"])
 def read_other_paths(request: Request) -> Response:
     """
@@ -158,6 +172,12 @@ def read_other_paths(request: Request) -> Response:
     """
     # check first if requested file exists (for frontend assets)
     path = request.path_params["catchall"]
+    logger.debug(f"Catch all path detected: {path}")
+    try:
+        path = sanitise_url_path(path)
+    except MalisciousUrlException:
+        # if a maliscious URL is detected, route the user to the index
+        return get_admin_index_as_response()
 
     # search for matching route in package (i.e. /dataset)
     ui_file = match_route(get_ui_file_map(), path)

programming_language: Python
filename: test_api_router.py
change_type: ModificationType.MODIFY
diff: @@ -45,3 +45,24 @@ class TestApiRouter:
             f"{V1_URL_PREFIX}/route/does/not/exist/", headers=auth_header
         )
         assert resp_4.status_code == HTTP_404_NOT_FOUND
+
+    def test_malicious_url(
+        self,
+        api_client: TestClient,
+        url,
+    ) -> None:
+        malicious_paths = [
+            "../../../../../../../../../etc/passwd",
+            "..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd",
+            "%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd",
+            "%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd",
+            "..%c0%2f..%c0%2f..%c0%2f..%c0%2f..%c0%2f..%c0%2f..%c0%2f..%c0%2f..%c0%2f/etc/passwd",
+            ".../...//.../...//.../...//.../...//.../...//.../...//.../...//.../...//.../...//etc/passwd",
+            "...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2f...%2f...%2f%2fetc/passwd",
+            "%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//%2e%2e%2e/%2e%2e%2e//etc/passwd",
+            "%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2f%2e%2e%2e%2f%2e%2e%2e%2f%2fetc/passwd",
+        ]
+        for path in malicious_paths:
+            resp = api_client.get(f"{url}/{path}")
+            assert resp.status_code == 200
+            assert resp.text == "<h1>Privacy is a Human Right!</h1>"

programming_language: Lua