CVE-2024-1561 (published 2024-04-16T00:15Z)

An issue was discovered in gradio-app/gradio, where the `/component_server` endpoint improperly allows the invocation of any method on a `Component` class with attacker-controlled arguments. Specifically, by exploiting the `move_resource_to_block_cache()` method of the `Block` class, an attacker can copy any file on the filesystem to a temporary directory and subsequently retrieve it. This vulnerability enables unauthorized local file read access, posing a significant risk especially when the application is exposed to the internet via `launch(share=True)`, thereby allowing remote attackers to read files on the host machine. Furthermore, gradio apps hosted on `huggingface.co` are also affected, potentially leading to the exposure of sensitive information such as API keys and credentials stored in environment variables.


Weakness information:
cwe_id: CWE-29
cwe_name: Path Traversal: '\..\filename'
description: The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize '\..\filename' (leading backslash dot dot) sequences that can resolve to a location that is outside of that directory.
extended_description: 
            This allows attackers to traverse the file system to access files or directories that are outside of the restricted directory.
            This is similar to CWE-25, except using "\" instead of "/". Sometimes a program checks for "..\" at the beginning of the input, so a "\..\" can bypass that check. It is also useful for bypassing path traversal protection schemes that only assume that the "/" separator is valid.
         
         

Fix information:
repo_url: https://github.com/gradio-app/gradio
commit_details: repo_url: https://github.com/gradio-app/gradio
author: aliabid94
author_date: 2023-12-27 14:16:56-08:00
msg: Component Server fix (#6884)

* changes

* add changeset

* Update gradio/routes.py

Co-authored-by: Aarni Koskela <akx@iki.fi>

* changes

* changes

---------

Co-authored-by: Ali Abid <ubuntu@ip-172-31-25-241.us-west-2.compute.internal>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Aarni Koskela <akx@iki.fi>
repository: repo_url: https://github.com/gradio-app/gradio
repo_name: gradio-app/gradio
description: Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
repo_language: Python
owner: gradio-app
file_changes: filename: ten-chefs-argue.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,5 @@
+---
+"gradio": minor
+---
+
+feat:Component Server fix

programming_language: Markdown
filename: file_explorer.py
change_type: ModificationType.MODIFY
diff: @@ -139,10 +139,8 @@ class FileExplorer(Component):
         return FileExplorerData(root=root)
 
     @server
-    def ls(self, value=None) -> list[dict[str, str]] | None:
+    def ls(self, _=None) -> list[dict[str, str]] | None:
         """
-        Parameters:
-            value: file path as a list of strings for each directory level relative to the root.
         Returns:
             tuple of list of files in directory, then list of folders in directory
         """

programming_language: Python
filename: routes.py
change_type: ModificationType.MODIFY
diff: @@ -699,7 +699,12 @@ class App(FastAPI):
                 block = state[component_id]
             else:
                 block = app.get_blocks().blocks[component_id]
-            fn = getattr(block, body.fn_name)
+            fn = getattr(block, body.fn_name, None)
+            if fn is None or not getattr(fn, "_is_server_fn", False):
+                raise HTTPException(
+                    status_code=status.HTTP_404_NOT_FOUND,
+                    detail="Function not found.",
+                )
             return fn(body.data)
 
         @app.get(

programming_language: Python
filename: test_routes.py
change_type: ModificationType.MODIFY
diff: @@ -831,3 +831,34 @@ class TestShowAPI:
         assert (
             interface.show_api is True
         ), "show_api should be True when IS_WASM is False"
+
+
+def test_component_server_endpoints(connect):
+    here = os.path.dirname(os.path.abspath(__file__))
+    with gr.Blocks() as demo:
+        file_explorer = gr.FileExplorer(root=here)
+
+    with closing(demo) as io:
+        app, _, _ = io.launch(prevent_thread_lock=True)
+        client = TestClient(app)
+        success_req = client.post(
+            "/component_server/",
+            json={
+                "session_hash": "123",
+                "component_id": file_explorer._id,
+                "fn_name": "ls",
+                "data": None,
+            },
+        )
+        assert success_req.status_code == 200
+        assert len(success_req.json()) > 0
+        fail_req = client.post(
+            "/component_server/",
+            json={
+                "session_hash": "123",
+                "component_id": file_explorer._id,
+                "fn_name": "preprocess",
+                "data": None,
+            },
+        )
+        assert fail_req.status_code == 404

programming_language: Python