
Analyse détaillée de CVE-2026-22038, une vulnérabilité de haute sévérité dans les blocs AutoGPT Stagehand qui journalise les clés API en texte clair, y compris la cause racine, l'impact et les mesures correctives.
Identifiant CVE : CVE-2026-22038
Produit : Plateforme AutoGPT (intégration Stagehand)
Versions concernées : Toutes les versions jusqu'à autogpt-platform-beta-v0.6.45 incluse
Corrigé dans : autogpt-platform-beta-v0.6.46
Type de vulnérabilité : CWE-532 — Insertion d'informations sensibles dans un fichier journal
Gravité : Élevée
Signalé par : Panuganti Siva Aditya (@sivaadityacoder)
Date du rapport : 19 décembre 2025
Avis GitHub : GHSA-rc89-6g7g-v5v7
AutoGPT est une plateforme open source qui permet aux utilisateurs de créer et d'exécuter des agents IA autonomes. Elle inclut une intégration Stagehand qui se connecte à l'automatisation de navigateur et à des fournisseurs de LLM comme OpenAI, Anthropic et Groq.
Lors d'une revue de code des blocs Stagehand, j'ai remarqué que les objets d'identifiants étaient passés directement dans les appels logger.info(). J'ai tracé chaque instruction de journalisation et constaté que était appelé en ligne — contournant explicitement la protection fournie par Pydantic.
.get_secret_value()SecretStrLe fichier vulnérable :
autogpt_platform/backend/backend/blocks/stagehand/blocks.py
Trois blocs sont concernés, chacun avec le même schéma :
StagehandObserveBlock (lignes 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 (lignes 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 (lignes 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()}"
)
Pour confirmer l'exploitabilité, j'ai exécuté chaque bloc Stagehand avec des identifiants valides et recherché dans les journaux d'application :
grep "secret:" /var/log/autogpt/application.log
La sortie a confirmé que de véritables clés API apparaissaient en clair :
[INFO] OBSERVE: Model credentials: ... secret: sk-proj-abc123xyz789...
[INFO] ACT: Model credentials: ... secret: sk-ant-api03-def456uvw...
[INFO] EXTRACT: Model credentials: ... secret: sk-1234567890abcdef...
La base de code d'AutoGPT utilise correctement le type SecretStr de Pydantic pour les clés API. Lorsqu'un objet SecretStr est inclus dans une f-string ou imprimé normalement, il s'affiche comme ********** — cette protection est intentionnelle.
La cause racine est que les développeurs ont appelé .get_secret_value() directement dans les instructions logger.info(). Cela dévoile explicitement le secret et transmet la valeur de chaîne brute au journaliseur, contournant entièrement le masquage intégré de SecretStr.
Le niveau de journalisation est INFO, ce qui signifie que ces instructions s'exécutent dans les environnements de production normaux — pas seulement lors de sessions de débogage locales. Chaque fois qu'un de ces blocs s'exécute avec des identifiants, le secret est écrit dans le fichier journal.
Scénario d'attaque :
secret:, sk-proj- ou 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"}]}'
Identifiants exposés : clés API Stagehand / Browserbase, clés API OpenAI, clés API Anthropic, clés API Groq, et tout identifiant de fournisseur LLM configuré dans les blocs Stagehand.
Le correctif dans autogpt-platform-beta-v0.6.46 supprime les appels .get_secret_value() des instructions logger.info().
Approche recommandée — supprimer complètement le secret du journal :
# Avant (vulnérable) :
logger.info(
f"OBSERVE: Model credentials: {model_credentials} for provider "
f"{model_credentials.provider} secret: {model_credentials.api_key.get_secret_value()}"
)
# Après (corrigé) :
logger.info(
f"OBSERVE: Model credentials for provider {model_credentials.provider} (API key redacted)"
)
Alternative — masquer, en conservant la structure du journal :
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())}"
)
L'approche de masquage n'expose que les 4 premiers et 4 derniers caractères — suffisant pour identifier quelle clé a été utilisée sans révéler le secret complet.
N'appelez jamais .get_secret_value() dans les instructions de journalisation. Si votre framework vous fournit un type de masquage de secret (SecretStr, SecretBytes, etc.), laissez-le faire son travail. Appeler la méthode de dévoilement dans un journaliseur annule tout l'intérêt.
INFO est un niveau de journalisation de production. Les dumps de style débogage des identifiants ne devraient jamais atteindre INFO. Si vous avez réellement besoin de confirmer quels identifiants sont actifs, journalisez uniquement des métadonnées non sensibles (nom du fournisseur, préfixe de la clé, 4 derniers caractères).
Les fichiers journaux sont une surface d'attaque. Traitez-les comme tout autre stockage de données sensibles — restreignez l'accès, faites-les pivoter et auditez leur contenu. Les outils d'agrégation (Splunk, ELK, Datadog) ont souvent un accès en lecture étendu, donc un secret dans une ligne de journal est effectivement un secret dans ces systèmes également.
Le correctif est toujours plus simple que le bug. Supprimer deux lignes de journalisation (ou les remplacer par des équivalents masqués) ferme complètement cette exposition. La dette de sécurité due à une « journalisation de débogage temporaire » laissée en production est courante et évitable grâce à la revue de code.
Les protections SecretStr sont opt-in, pas automatiques. Les développeurs doivent comprendre que la protection ne tient que tant que personne ne dévoile explicitement la valeur. La revue de code devrait signaler toute utilisation de .get_secret_value() en dehors du chemin d'authentification.
| Date | Événement |
|---|---|
| 19 décembre 2025 | Signalé aux mainteneurs d'AutoGPT via Huntr et l'avis de sécurité GitHub |
| 2025–2026 | Le mainteneur Nicholas Tindle a accusé réception du rapport |
| Avant avril 2026 | Corrigé dans autogpt-platform-beta-v0.6.46 |
| 25 avril 2026 | CVE-2026-22038 attribuée |