Skip to content

AutoAgent publishes an unauthenticated command-execution TCP server (tcp_server.py) on host 0.0.0.0, running attacker-supplied commands as root inside a container that mounts the host workspace #96

Description

@geo-chen

Summary

When AutoAgent runs an agent it starts a Docker sandbox and, inside it, a small TCP server (autoagent/environment/tcp_server.py) that reads a command from any connecting client and executes it through /bin/bash -c via subprocess.Popen(..., shell=True). There is no authentication. The server binds 0.0.0.0 inside the container, and the container is created with docker run -p {communication_port}:{communication_port}, which Docker publishes on the host's 0.0.0.0, so the command server is reachable from the network. The container runs --user root and bind-mounts the host workspace directory (-v {local_workplace}:{docker_workplace}). Consequently any unauthenticated client that can reach the published port (default 12347/12346) executes arbitrary commands as root inside the container and can read and write the mounted host workspace.

Details

Container creation (autoagent/environment/docker_env.py):

docker_command = [
    "docker", "run", "-d", "--name", self.container_name, "--user", "root",
    "-v", f"{self.local_workplace}:{self.docker_workplace}",          # host dir mounted into container
    "-w", f"{self.docker_workplace}", "-p", f"{self.communication_port}:{self.communication_port}", BASE_IMAGES,
    "/bin/bash", "-c",
    f"python3 {self.docker_workplace}/tcp_server.py --workplace {self.workplace_name} --conda_path {self.conda_path} --port {self.communication_port}"

-p {port}:{port} with no host IP publishes the port on 0.0.0.0 of the host (the project's own check_container_ports documents the resulting mapping as 0.0.0.0:12345->12345/tcp). The command server (autoagent/environment/tcp_server.py) reads a command and runs it with a shell, with no authentication and no allowlist:

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", args.port))
server.listen(1)
...
while True:
    conn, addr = server.accept()
    while True:
        command = receive_all(conn)            # raw bytes from the client
        if not command:
            break
        modified_command = f"/bin/bash -c 'source {args.conda_path}/etc/profile.d/conda.sh && conda activate autogpt && cd /{args.workplace} && {command}'"
        process = subprocess.Popen(modified_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)

receive_all simply reads until a recv returns fewer than 4096 bytes, so one send of the command string is executed. The default --port is 12347 (autoagent/cli.py). There is no token, signature, or origin check; the protocol is "connect, send command, receive output." Because the container is --user root and mounts the host workspace, the executed command runs as root and can read/write the host-side workspace files (and pivot from there).

(There is a second, related exposure: autoagent/server.py runs a FastAPI "MetaChain API" with uvicorn.run(app, host="0.0.0.0", port=8000) and no authentication, dynamically exposing every tool and agent as an endpoint that runs the agent; if started, it is likewise an unauthenticated, network-exposed agent-execution API.)

POC: available upon request

Impact

Any unauthenticated attacker who can reach the published communication port (same LAN, shared/cloud network, or a co-located host) executes arbitrary commands as root inside the AutoAgent sandbox container and reads/writes the bind-mounted host workspace directory, with no credentials and no user interaction. This is remote code execution with a path to host data (the mounted workspace) and lateral movement. Fix: do not publish the command port on 0.0.0.0 (bind the container port to 127.0.0.1 only, e.g. -p 127.0.0.1:{port}:{port}, and have tcp_server.py bind 127.0.0.1); require an authentication token on the command channel; avoid running the container as root; and avoid shell=True. Apply the same (auth + loopback bind) to autoagent/server.py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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