Skip to content

Arbitrary file write and delete via unsanitized upload filename in the ChatDev workflow server #638

@geo-chen

Description

@geo-chen

The ChatDev workflow server saves uploaded files to a path built by joining a freshly created temporary directory with the client-supplied multipart filename, with no sanitization. A filename containing parent-directory segments escapes the temporary directory and writes attacker-controlled bytes to an arbitrary location on the host. The function's cleanup step then unlinks the same traversed path, so the upload also deletes an arbitrary file. The endpoint requires only a known session id, which is minted without authentication. Confirmed by calling the real save_upload_file with a traversal filename: the file was written outside the temp directory, and an existing victim file was deleted.

Details

server/services/attachment_service.py (save_upload_file, around lines 46 to 80):

filename = upload.filename or "upload.bin"
temp_dir = Path(tempfile.mkdtemp(prefix="mac_upload_"))
temp_path = temp_dir / filename            # filename not sanitized
with temp_path.open("wb") as buffer:
    ... shutil.copyfileobj(upload.file, buffer)
... register_file(...)                     # basenames it later (too late)
finally:
    temp_path.unlink()                     # follows the same traversal -> arbitrary delete

temp_dir / filename with filename = "../../../../tmp/CHATDEV_PWNED" resolves outside the mkdtemp directory. The later register_file uses the basename, but the write at temp_path.open("wb") already happened with the raw path. The finally block's unlink() resolves the same traversal, so any existing file the process can reach is deleted.

This is reached by POST /api/uploads/{session_id} (server/routes/uploads.py), which forwards the raw Starlette UploadFile (Starlette / python-multipart preserve / and .. in the filename). The route requires a known session via ensure_known_session(..., require_connection=False), but there is no authentication: a session id is minted by opening the workflow websocket or hitting the workflow-execute endpoints, with no credentials. The sibling download route validates session_id with a regex; the upload route validates neither the session id nor the filename.

Impact

A client that has minted a session id (no authentication) can write attacker-controlled files to, and delete arbitrary files at, any existing-directory path the server process can reach: overwrite shell rc / cron files / served static assets for code execution, or destroy host files via the cleanup unlink. Effective impact is arbitrary file write plus arbitrary file delete on the host.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions