
Análisis detallado de CVE-2026-22038, una vulnerabilidad de alta gravedad en los bloques AutoGPT Stagehand que registra claves API en texto plano, incluyendo la causa raíz, el impacto y la remediación.
ID CVE: CVE-2026-22038
Producto: Plataforma AutoGPT (integración Stagehand)
Versiones afectadas: Todas las versiones hasta autogpt-platform-beta-v0.6.45 inclusive
Corregido en: autogpt-platform-beta-v0.6.46
Tipo de vulnerabilidad: CWE-532 — Inserción de Información Sensible en Archivos de Registro
Gravedad: Alta
Reportado por: Panuganti Siva Aditya (@sivaadityacoder)
Fecha del informe: 19 de diciembre de 2025
Aviso de GitHub: GHSA-rc89-6g7g-v5v7
AutoGPT es una plataforma de código abierto que permite a los usuarios construir y ejecutar agentes de IA autónomos. Incluye una integración Stagehand que se conecta a la automatización de navegadores y proveedores de LLM como OpenAI, Anthropic y Groq.
Durante una revisión de código de los bloques Stagehand, noté que los objetos de credenciales se pasaban directamente a las llamadas logger.info(). Rastreé cada declaración de registro y descubrí que se llamaba a en línea — omitiendo explícitamente la protección que proporciona Pydantic.
.get_secret_value()SecretStrEl archivo vulnerable:
autogpt_platform/backend/backend/blocks/stagehand/blocks.py
Tres bloques están afectados, cada uno con el mismo patrón:
StagehandObserveBlock (Líneas 185–188)
logger.info(f"OBSERVE: Stagehand credentials: {stagehand_credentials}")
logger.info(
f"OBSERVE: Model credentials: {model_credentials} for provider "
f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)
StagehandActBlock (Líneas 285–288)
logger.info(f"ACT: Stagehand credentials: {stagehand_credentials}")
logger.info(
f"ACT: Model credentials: {model_credentials} for provider "
f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)
StagehandExtractBlock (Líneas 373–376)
logger.info(f"EXTRACT: Stagehand credentials: {stagehand_credentials}")
logger.info(
f"EXTRACT: Model credentials: {model_credentials} for provider "
f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)
Para confirmar la explotabilidad, ejecuté cada bloque Stagehand con credenciales válidas y busqué en los registros de la aplicación:
grep "secret:" /var/log/autogpt/application.log
La salida confirmó que las claves API reales aparecían en texto plano:
[INFO] OBSERVE: Model credentials: ... secret: sk-proj-abc123xyz789...
[INFO] ACT: Model credentials: ... secret: sk-ant-api03-def456uvw...
[INFO] EXTRACT: Model credentials: ... secret: sk-1234567890abcdef...
El código de AutoGPT utiliza correctamente el tipo SecretStr de Pydantic para las claves API. Cuando un objeto SecretStr se incluye en una f-string o se imprime normalmente, se muestra como ********** — esa protección es intencional.
La causa raíz es que los desarrolladores llamaron a .get_secret_value() directamente dentro de las declaraciones logger.info(). Esto desempaqueta explícitamente el secreto y pasa el valor de cadena sin procesar al registrador, omitiendo por completo el enmascaramiento integrado de SecretStr.
El nivel de registro es INFO, lo que significa que estas declaraciones se ejecutan en entornos de producción normales — no solo en sesiones de depuración locales. Cada vez que uno de estos bloques se ejecuta con credenciales, el secreto se escribe en el archivo de registro.
Escenario de ataque:
secret:, sk-proj- o sk-ant-.curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer sk-proj-abc123xyz789..." \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}'
Credenciales expuestas: claves API de Stagehand / Browserbase, claves API de OpenAI, claves API de Anthropic, claves API de Groq y cualquier credencial de proveedor de LLM configurada en los bloques Stagehand.
El parche en autogpt-platform-beta-v0.6.46 elimina las llamadas a .get_secret_value() de las declaraciones logger.info().
Enfoque recomendado — eliminar el secreto del registro por completo:
# Antes (vulnerable):
logger.info(
f"OBSERVE: Model credentials: {model_credentials} for provider "
f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)
# Después (corregido):
logger.info(
f"OBSERVE: Model credentials for provider {model_credentials.provider} (API key redacted)"
)
Alternativa — redactar, manteniendo la estructura del registro intacta:
def redact_secret(value: str) -> str:
if len(value) <= 8:
return "***"
return f"{value[:4]}...{value[-4:]}"
logger.info(
f"OBSERVE: Model credentials for provider {model_credentials.provider} "
f"secret: {redact_secret(model_credentials.api_key.get_secret_value())}"
)
El enfoque de redacción expone solo los primeros y últimos 4 caracteres — suficiente para identificar qué clave se utilizó sin revelar el secreto completo.
Nunca llames a .get_secret_value() en declaraciones de registro. Si tu framework te proporciona un tipo de enmascaramiento de secretos (SecretStr, SecretBytes, etc.), deja que haga su trabajo. Llamar al método de desempaquetado dentro de un registrador anula todo el propósito.
INFO es registro a nivel de producción. Los volcados de credenciales estilo depuración nunca deberían llegar a INFO. Si realmente necesitas confirmar qué credenciales están activas, registra solo metadatos no sensibles (nombre del proveedor, prefijo de la clave, últimos 4 caracteres).
Los archivos de registro son una superficie de ataque. Trátalos como cualquier otro almacén de datos sensibles — restringe el acceso, rótalos y audita lo que entra. Las herramientas de agregación (Splunk, ELK, Datadog) a menudo tienen acceso de lectura amplio, por lo que un secreto en cualquier línea de registro es efectivamente un secreto también en esos sistemas.
La corrección siempre es más simple que el error. Eliminar dos líneas de registro (o reemplazarlas con equivalentes redactados) cierra por completo esta exposición. La deuda de seguridad por "registro de depuración temporal" dejado en producción es común y prevenible con revisión de código.
Las protecciones de SecretStr son opcionales, no automáticas. Los desarrolladores deben entender que la protección solo se mantiene mientras nadie desempaquete explícitamente el valor. La revisión de código debería marcar cualquier uso de .get_secret_value() fuera de la ruta de autenticación.
| Fecha | Evento |
|---|---|
| 19 de diciembre de 2025 | Reportado a los mantenedores de AutoGPT mediante Huntr y el Aviso de Seguridad de GitHub |
| 2025–2026 | El mantenedor Nicholas Tindle reconoció el informe |
| Antes de abril de 2026 | Corregido en autogpt-platform-beta-v0.6.46 |
| 25 de abril de 2026 | Asignado CVE-2026-22038 |