Caça a CVEs no Cursor IDE: Análise Técnica Completa da Segurança do Editor de IA
Disclaimer: A pesquisa foi conduzida exclusivamente para fins educacionais. Todas as vulnerabilidades encontradas foram documentadas. Nenhum dado de terceiros foi comprometido. O autor não se responsabiliza pelo uso das técnicas descritas.
Introdução
Cursor é um IDE com IA baseado no VS Code, que processa milhões de linhas de código de desenvolvedores através de seus servidores. Ao considerar a segurança deste produto, surgiu a questão: quão confiável é o modelo de autorização do lado do servidor que fica entre um usuário gratuito e o Claude 4 Opus? Spoiler: não encontrei um bypass sério – o Cursor é realmente bem protegido. Mas no caminho, descobri 4 vulnerabilidades do tipo CVE, fiz engenharia reversa completa do API-surface, extraí esquemas protobuf de 1.1 milhão de linhas de JS minificado e encontrei um backdoor de desenvolvimento oculto nos servidores de produção. Eis o que resultou.
1. Reconhecimento: Descompilação do Cliente
O Cursor é baseado no VS Code (Electron). Todo o código do cliente está localizado em resources/app/out/vs/workbench/workbench.desktop.main.js, um arquivo com 1.144.696 linhas de JavaScript minificado. Este arquivo é uma mina de ouro: contém todas as definições protobuf, URLs de API, fluxo OAuth, Statsig feature gates e a lógica de roteamento de modelos.
Extração de Esquemas Protobuf:
O Cursor utiliza Connect-RPC (https://connectrpc.com/), um framework RPC moderno sobre HTTP/2. Todos os serviços são definidos diretamente no bundle JS. Para extrair os campos, utilizei uma cadeia de grep → sed → análise manual. O resultado foi o esquema proto completo para AgentRunRequest:
protobufmessage AgentRunRequest { ConversationState conversation_state = 1; Action action = 2; ModelDetails model_details = 3; // ← modelo para plan check McpTools mcp_tools = 4; string conversation_id = 5; McpFileSystemOptions mcp_file_system_options = 6; SkillOptions skill_options = 7; string custom_system_prompt = 8; RequestedModel requested_model = 9; // ← modelo para routing bool suggest_next_prompt = 10; string subagent_type_name = 11; bool exclude_workspace_context = 12; string harness = 13; repeated RequestedModel selected_subagent_models = 14; repeated ModelDetails selected_subagent_model_details = 15; string conversation_group_id = 16; repeated PreFetchedBlobs pre_fetched_blobs = 17; // string dev_raw_model_slug = 18; // ← CAMPO DO SERVIDOR, não está no cliente! }
A descoberta chave é que o campo 18 (devRawModelSlug) está ausente no código do cliente, mas o servidor o aceita e processa. Mais detalhes abaixo.
Extração de Tokens:
O Cursor armazena a autenticação em SQLite. O token JWT contém claims padrão, sem claims sobre assinatura ou plano. A verificação da assinatura é feita inteiramente no lado do servidor.
2. Arquitetura
O stack consiste no Cliente (Electron) enviando requisições via HTTP/2 + Connect-RPC para um API Gateway (api2.cursor.sh, api5.cursor.sh), que então encaminha para o Agent Service (roteamento de modelos, verificação de plano) e, finalmente, para os provedores de LLM (OpenAI, Anthropic, Google).
Protocolo Connect-RPC:
O Connect-RPC encapsula protobuf em HTTP/2. Cada frame contém um byte de flags, 4 bytes de comprimento e o payload protobuf. Flags 0x00 indicam um frame de dados, e 0x02 indicam um frame de trailer (JSON com erros/metadados).
Algoritmo de Checksum:
O Cursor utiliza um checksum customizado para validar requisições. O algoritmo é baseado em timestamp e é determinístico, o que o torna trivial de reproduzir. Ele foi revertido a partir do arquivo workbench.desktop.main.js.
Lista de Servidores:
Foram identificados diversos servidores, incluindo https://api2.cursor.sh (API principal), https://agent.api5.cursor.sh (específico do agent), e outros subdomínios relacionados a agentes e infraestrutura GCP.
3. Mapeamento Completo do API-Surface
Além dos serviços gRPC (Connect-RPC) como agent.v1.AgentService e aiserver.v1.DashboardService, foram identificados endpoints REST, como /auth/full_stripe_profile e /auth/start-subscription-now. Também foram extraídos mais de 40 Statsig Feature Gates, indicando funcionalidades como cc_override_agent_backend e user_is_professional.
4. CVE-1: Prototype Pollution (CWE-1321)
Severidade: Média (DoS, potencial escalada)
Endpoint: POST /auth/start-subscription-now
Impacto: Crash do servidor (HTTP 500), alteração do caminho de processamento.
Descrição: Endpoints JSON do Cursor parseiam o corpo da requisição sem sanitizar a propriedade __proto__. Ao enviar um payload com __proto__, o servidor retorna um 500 Internal Server Error em vez do esperado 400 Cannot upgrade free user. Isso confirma que a cadeia de protótipos é poluída antes da verificação da assinatura, causando uma exceção não tratada. Um comportamento similar foi observado no endpoint ActivatePromotion.
5. CVE-2: devRawModelSlug — Backdoor de Desenvolvimento Oculto (CWE-489)
Severidade: Média (Código de Debug Ativo em Produção)
Campo: AgentRunRequest.dev_raw_model_slug (campo proto 18)
Impacto: Existência de um mecanismo de desenvolvimento para bypass direto do roteamento de modelos em produção.
Descoberta: Ao escanear sistematicamente todos os campos proto (1-50) em AgentRunRequest, o campo 18 retornou uma resposta única: "devRawModelSlug is not available".
Análise: O campo 18 não está presente no código do cliente, mas o servidor o parseia e valida. A verificação ocorre antes da verificação do plano. Isso indica um mecanismo para especificar diretamente o modelo backend, contornando o roteamento padrão. Embora desativado em produção, a presença do código de processamento e a validação do campo sugerem um risco de segurança significativo se um feature flag for alterado ou se houver um erro de configuração.
6. CVE-3: Vazamento de Informações de Cabeçalho de Serviço Interno (CWE-200)
Endpoint: GET /agent.v1.AgentService/GetAllowedModelIntents
Impacto: Revelação da arquitetura do service mesh interno.
Descrição: Ao chamar o método GetAllowedModelIntents com um token Bearer comum, a resposta é um HTTP 401: "Invalid internal service header". Em vez de um genérico 401 Unauthorized ou 404 Not Found, essa mensagem revela que o endpoint existe, é tratado e utiliza autenticação service-to-service com um mecanismo de autorização separado para serviços internos.
7. CVE-4: Protobuf Field Injection & Wire Type Confusion
7.1 Duplicação de Campo 3 (Double Field Injection):
O Protobuf permite enviar o mesmo campo duas vezes. Embora a especificação indique que o último valor prevalece para campos singulares, a análise mostrou que o servidor do Cursor lida corretamente com isso, pegando o último valor para ambas as verificações (plan check e routing). No entanto, o servidor não rejeita o campo duplicado, o que é uma violação do princípio de parsing estrito.
7.2 Wire Type Confusion:
Enviar um model_id (geralmente string, wire type 2) como varint (wire type 0) resultou em um erro parse binary: illegal tag, indicando que o parser é suficientemente rigoroso e rejeita essa tentativa de confusão.
7.3 Subagent Field Injection:
Os campos 14 (selected_subagent_models) e 15 (selected_subagent_model_details) aceitam modelos premium sem verificação de plano. Embora a requisição seja processada, o modelo de resposta é idêntico ao de uma requisição normal com "default". Os campos 14/15 são ignorados no roteamento, mas não validados, aumentando a superfície de ataque para futuras alterações.
8. O Que Mais Foi Testado (Sem Resultado)
Diversos outros vetores foram testados, incluindo brute force de nomes de modelos, manipulação de claims JWT, injeção de callback OAuth, replay de webhook Stripe, confusão de Content-Type, brute force de cabeçalhos de serviço interno, override de cabeçalho Host, uso de custom_system_prompt e harness com subagent_type_name, overflow de inteiros em billing, gRPC Server Reflection e headers de feature flag. Na maioria dos casos, as proteções do lado do servidor, como verificação de plano, validação de assinatura e parsing rigoroso, impediram o sucesso dos ataques.
9. O Que o Cursor Faz Corretamente
É preciso reconhecer que o Cursor demonstra um modelo de segurança maduro. A verificação de plano é totalmente do lado do servidor, sem claims de assinatura no JWT. O parsing de protobuf é rigoroso, e a validação de assinatura de webhook Stripe e de nonce OAuth são implementadas corretamente. A verificação unificada de modelos e o uso de Connect-RPC em vez de gRPC bruto são escolhas positivas.
10. Conclusões e Recomendações
As vulnerabilidades encontradas incluem Prototype Pollution (CVE-2025-001), código de debug ativo em produção (CVE-2025-002), divulgação de informações (CVE-2025-003) e validação de entrada inadequada (CVE-2025-004). Recomendações para o Cursor incluem sanitizar __proto__ e constructor no parsing JSON, remover devRawModelSlug do esquema proto de produção ou ocultar o nome do campo em erros, unificar erros de autorização e validar todos os campos proto.
O Cursor é um dos produtos de IA mais conscientes em termos de segurança pesquisados. No entanto, a poluição de protótipo e a presença de um backdoor de desenvolvimento em produção são problemas reais que devem ser corrigidos. Para SaaS com autorização do lado do servidor, é crucial não armazenar privilégios no JWT, verificar o plano em cada requisição a partir do banco de dados, usar parsing proto rigoroso, não deixar campos de desenvolvimento em produção e não expor nomes internos através de erros, além de sanitizar __proto__ em JSON.
Ferramental:
Python 3 + httpx (cliente HTTP/2), serialização manual de protobuf, grep/sed para análise de JS minificado, SQLite3 para extração de tokens.
Resumo:
O Cursor é um dos produtos de IA mais seguros em termos de segurança pesquisados. A verificação de plano é totalmente do lado do servidor, o JWT não contém privilégios e o parsing de protobuf é rigoroso. No entanto, a poluição de protótipo e a presença de um backdoor de desenvolvimento em produção são problemas reais que precisam ser corrigidos.
