Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 

Repository files navigation

CVE-2026-22038 — AutoGPT Stagehand Blocks Log API Keys in Plaintext

CVE ID: CVE-2026-22038
Product: AutoGPT Platform (Stagehand integration)
Affected Versions: All versions up to and including autogpt-platform-beta-v0.6.45
Patched In: autogpt-platform-beta-v0.6.46
Vulnerability Type: CWE-532 — Insertion of Sensitive Information into Log File
Severity: High
Reported By: Panuganti Siva Aditya (@sivaadityacoder)
Report Date: December 19, 2025
GitHub Advisory: GHSA-rc89-6g7g-v5v7


My Approach

AutoGPT is an open-source platform that lets users build and run autonomous AI agents. It includes a Stagehand integration that connects to browser automation and LLM providers like OpenAI, Anthropic, and Groq.

During a code review of the Stagehand blocks, I noticed that credential objects were passed directly into logger.info() calls. I traced each log statement and found that .get_secret_value() was being called inline — explicitly bypassing the SecretStr protection Pydantic provides.

The vulnerable file:

autogpt_platform/backend/backend/blocks/stagehand/blocks.py

Three blocks are affected, each with the same pattern:

StagehandObserveBlock (Lines 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 (Lines 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 (Lines 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()}"
)

To confirm exploitability, I ran each Stagehand block with valid credentials and searched the application logs:

grep "secret:" /var/log/autogpt/application.log

The output confirmed that real API keys appeared in plaintext:

[INFO] OBSERVE: Model credentials: ... secret: sk-proj-abc123xyz789...
[INFO] ACT: Model credentials: ... secret: sk-ant-api03-def456uvw...
[INFO] EXTRACT: Model credentials: ... secret: sk-1234567890abcdef...

Root Cause

The AutoGPT codebase correctly uses Pydantic's SecretStr type for API keys. When a SecretStr object is included in an f-string or printed normally, it renders as ********** — that protection is intentional.

The root cause is that the developers called .get_secret_value() directly inside logger.info() statements. This explicitly unwraps the secret and passes the raw string value to the logger, bypassing SecretStr's built-in masking entirely.

The log level is INFO, meaning these statements execute in normal production environments — not just local debugging sessions. Every time one of these blocks runs with credentials, the secret is written to the log file.


Impact

  • Credential theft — plaintext API keys sitting in log files for anyone with log access
  • Financial damage — stolen keys get used to make expensive API calls at the victim's cost
  • Data access — the stolen credentials may grant access to the victim's data in those services
  • Quota exhaustion — an attacker can deliberately burn through rate limits to deny service
  • Long exposure window — log files are often kept for 30–90 days, so a key used once might stay exposed for months
  • Compliance issues — logging secrets violates PCI-DSS, SOC 2, and GDPR requirements

Attack scenario:

  1. An attacker gains read access to log files via a misconfigured aggregation system (Splunk, ELK, Datadog, CloudWatch), a compromised monitoring account, or excess internal access.
  2. They search logs for patterns like secret:, sk-proj-, or sk-ant-.
  3. They extract real API keys from the log output.
  4. They verify the key:
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"}]}'
  1. With a valid key they can make API calls, drain quotas, or access the victim's data.

Credentials exposed: Stagehand / Browserbase API keys, OpenAI API keys, Anthropic API keys, Groq API keys, and any LLM provider credentials configured in the Stagehand blocks.


Fix

The patch in autogpt-platform-beta-v0.6.46 removes .get_secret_value() calls from the logger.info() statements.

Recommended approach — remove the secret from the log entirely:

# Before (vulnerable):
logger.info(
    f"OBSERVE: Model credentials: {model_credentials} for provider "
    f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)

# After (fixed):
logger.info(
    f"OBSERVE: Model credentials for provider {model_credentials.provider} (API key redacted)"
)

Alternative — redact, keeping log structure intact:

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())}"
)

The redaction approach exposes only the first and last 4 characters — enough to identify which key was used without revealing the full secret.


Key Takeaways

  1. Never call .get_secret_value() in log statements. If your framework gives you a secret-masking type (SecretStr, SecretBytes, etc.), let it do its job. Calling the unwrap method inside a logger defeats the entire purpose.

  2. INFO is production-level logging. Debug-style dumps of credentials should never reach INFO. If you genuinely need to confirm which credentials are active, log only non-sensitive metadata (provider name, key prefix, last 4 chars).

  3. Log files are an attack surface. Treat them like any other sensitive data store — restrict access, rotate them, and audit what goes in. Aggregation tools (Splunk, ELK, Datadog) often have broad read access, so a secret in any log line is effectively a secret in those systems too.

  4. The fix is always simpler than the bug. Removing two log lines (or replacing them with redacted equivalents) fully closes this exposure. Security debt from "temporary debug logging" left in production is common and preventable with code review.

  5. SecretStr protections are opt-in, not automatic. Developers must understand that the protection only holds as long as no one explicitly unwraps the value. Code review should flag any use of .get_secret_value() outside of the authentication path.


Timeline

Date Event
December 19, 2025 Reported to AutoGPT maintainers via Huntr and GitHub Security Advisory
2025–2026 Maintainer Nicholas Tindle acknowledged the report
Before April 2026 Patched in autogpt-platform-beta-v0.6.46
April 25, 2026 CVE-2026-22038 assigned

References

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors