Durante um engajamento autorizado de pentest (WEB + API) contra screening.globo.com, solicitado pelo CSIRT Globo, identificamos que o banco de dados Firestore de produção do projeto estava completamente aberto: leitura, escrita e deleção sem nenhuma credencial. Qualquer pessoa na internet, sem autenticação, conseguia ler a base inteira de usuários (PII completa) e também criar, sobrescrever ou apagar registros. A causa raiz foi uma única Security Rule permissiva (allow read, write: if true) replicada em produção e em desenvolvimento. A falha já foi corrigida pela Globo, que autorizou a publicação deste write-up.
Este post reconstrói o raciocínio do teste do zero: como cheguei do reconhecimento inicial até a confirmação de escrita/deleção irrestrita, os erros de configuração por trás de cada etapa, e o que fica de lição pra quem faz — ou defende contra — esse tipo de ataque.
Contexto e escopo
screening.globo.comO engajamento seguiu duas fases: reconhecimento e mapeamento de superfície, e execução + validação, com cada requisição registrada em uma Replay Collection nomeada — nada de achado "confirmado de cabeça"; toda evidência foi reproduzida e revisada antes de virar finding oficial.
Cadeia de ataque — visão geral
Antes de detalhar cada passo, vale ter uma visão macro da cadeia completa. Cada etapa alimenta a seguinte — e em nenhum momento foi necessária qualquer credencial:
Bundle JS público em s.glbimg.com entrega apiKey, projectId e appId — para dois ambientes (PRD e DEV).
GET anônimo no Firestore REST API retorna 200 OK com 135 KB+ de PII — coleção inteira de usuários exposta.
PATCH cria documento sintético, DELETE remove — ambos sem autenticação. Integridade comprometida.
Ambiente DEV (gglobo-screening-hdg-dev) com mesma regra permissiva e dados aparentemente reais.
Passo 1 — O bundle JS que entregou o mapa todo
Antes de qualquer teste na API, o reconhecimento passivo em s.glbimg.com (CDN estático que serve os assets da aplicação) já revelava mais do que deveria: o bundle JavaScript público carregado pelo frontend continha, em texto plano, o objeto de configuração do Firebase — apiKey, projectId, storageBucket e appId, para dois ambientes: produção (gglobo-screening-hdg-prd) e desenvolvimento (gglobo-screening-hdg-dev).
Isso, isoladamente, não é uma vulnerabilidade — o próprio Google documenta que a API Key do Firebase identifica o projeto, não autentica o usuário; ela pode aparecer no client-side sem problema se as Security Rules estiverem corretas. Mas é exatamente esse "se" que transforma um bundle público em reconhecimento completo: com projectId e apiKey em mãos, um atacante já tem tudo que precisa para formular requisições diretas contra a API REST do Firestore — sem precisar de nenhum scan adicional.
Exposição de configuração de cliente não é o achado em si — é o multiplicador do achado seguinte.
Passo 2 — Testando se a porta realmente estava destrancada
Com o projectId de produção em mãos, o próximo passo é o mais óbvio possível: uma requisição GET direta contra o endpoint REST do Firestore, sem nenhum header de autenticação.
GET /v1/projects/gglobo-screening-hdg-prd/databases/(default)/documents/users/ HTTP/1.1
Host: firestore.googleapis.com
Sem Authorization, sem cookie, sem qualquer credencial de usuário. A resposta: 200 OK, mais de 135 KB de corpo, contendo a coleção inteira de usuários — cada documento com PII completa (email, nome, sobrenome, empresa, cargo, país, território e role na plataforma).
Um 200 OK anônimo desse tamanho é a assinatura de uma Security Rule do tipo:
allow read, write: if true;
— a regra menos restritiva que o Firestore permite, equivalente a deixar o banco inteiro público na internet. Não existe meio-termo aqui: ou a regra exige autenticação, ou não exige nada, e o comportamento observado confirmou a segunda opção.
Reproduzi a leitura mais de uma vez (incluindo por ID de documento específico) para eliminar a hipótese de resposta em cache ou comportamento intermitente — o resultado foi consistente em todas as tentativas, o que é o que se espera antes de elevar isso de suspeita para finding crítico.
Passo 3 — De "consigo ler" para "consigo apagar"
Confirmar leitura sem autenticação já seria crítico por si só (exposição de PII de todos os clientes cadastrados). Mas a pergunta natural do próximo passo é: as Security Rules também liberam escrita?
Testar isso contra dados reais está fora de cogitação — a resposta certa é criar um documento sintético, validar o comportamento, e limpar tudo depois. Foi exatamente esse o fluxo:
1. PATCH — Criar um documento de teste, sem autenticação
PATCH /v1/projects/gglobo-screening-hdg-prd/databases/(default)/documents/users/pentest-bbounty-1788044488-DELETE-ME-2
Host: firestore.googleapis.com
Content-Type: application/json
{
"fields": {
"first_name": { "stringValue": "PENTEST-ORCHESTRATOR" },
"test_marker": { "stringValue": "pentest-bbounty-2026-08-29-delete-immediately" }
}
}
Resposta: 200 OK, documento criado com createTime e updateTime retornados pela própria API — sem exigir nenhum token.
PENTEST-ORCHESTRATOR na coleção users/ de produção. Resposta 200 OK confirma escrita irrestrita.2. DELETE — Remover o mesmo documento, também sem autenticação
DELETE /v1/projects/gglobo-screening-hdg-prd/databases/(default)/documents/users/pentest-bbounty-1788044488-DELETE-ME
Host: firestore.googleapis.com
Resposta: 200 OK, corpo vazio ({}) — deleção confirmada.
{}) com status 200 confirma a deleção.3. GET — Confirmar que o documento realmente não existe mais
GET /v1/projects/gglobo-screening-hdg-prd/databases/(default)/documents/users/pentest-bbounty-1788044488-DELETE-ME
Host: firestore.googleapis.com
Resposta: 404 Not Found. Prova de deleção efetiva, sem resíduo em produção.
Esse ciclo completo — criar, confirmar, apagar, confirmar de novo — é o que separa uma suposição de uma evidência reproduzível. Nenhum dado real de cliente foi tocado em nenhum momento; o único artefato escrito em produção durante todo o teste foi esse documento sintético, e ele saiu de lá limpo.
O resultado eleva o problema de uma falha de confidencialidade (F01, leitura) para uma falha de integridade completa (F02): qualquer agente externo, sem credencial nenhuma, pode sobrescrever ou apagar permanentemente registros reais de clientes.
Passo 4 — O ambiente DEV tinha o mesmo buraco (e dados que pareciam reais)
Lembra do segundo projectId encontrado no bundle JS? gglobo-screening-hdg-dev apresentava exatamente a mesma configuração de regras — leitura aberta, sem autenticação — e os dados retornados aparentavam ser reais e recentemente atualizados (não uma massa sintética de testes esquecida).
Ambientes de desenvolvimento tendem a herdar (ou nunca corrigir) as mesmas regras permissivas do projeto que os originou, e times de segurança que auditam só produção deixam esse espelho destrancado.
O que mais apareceu no caminho (achados secundários)
Nenhum deles crítico isoladamente, mas valem registro:
- Headers de segurança ausentes em
screening.globo.com— faltavamX-Frame-Options,Strict-Transport-Security,Referrer-PolicyePermissions-Policy. - Múltiplos subdomínios do ecossistema Globo (
s3.glbimg.com,p.glbimg.com,s2-screening.glbimg.com,tags.globo.com) semContent-Security-Policy— relevante especialmente nos hosts que servem JavaScript, onde a ausência de CSP agrava qualquer cenário futuro de comprometimento de supply chain. - Um parâmetro suspeito (
platform) emhorizon-track.globo.com/configs, sinalizado por padrões associados a File Inclusion — classificado como informativo, não validado. O scanner identifica padrão de nome, não comportamento; ficou como recomendação de teste manual (LFI/RFI, path traversal) para uma fase 2.
Resumo dos achados
| ID | Descrição | Severidade | Impacto |
|---|---|---|---|
| F01 | Leitura irrestrita da coleção users/ — PII completa exposta |
CRITICAL | Confidencialidade |
| F02 | Escrita e deleção irrestrita em produção — sem autenticação | CRITICAL | Integridade |
| F03 | Ambiente DEV com mesma regra permissiva e dados reais | HIGH | Confidencialidade |
| F04 | Headers de segurança ausentes (HSTS, CSP, X-Frame-Options) | INFO | Hardening |
| F05 | Parâmetro platform com pattern de File Inclusion (não validado) |
INFO | Investigação |
Por que isso importa
Não é só "achamos um bug". A combinação dos três achados principais forma uma cadeia completa de exploração sem nenhuma barreira técnica:
- O bundle JS público entrega
projectIdeapiKey— reconhecimento zero-esforço. - A Security Rule
allow read, write: if trueremove qualquer necessidade de autenticação. - O resultado é acesso não autenticado total — leitura, escrita e deleção — sobre PII real de clientes, em produção.
Do ponto de vista de conformidade, isso toca diretamente LGPD (Art. 46/48) e os capítulos V8 (Authorization) e V4 (API and Web Service) do ASVS. Do ponto de vista prático: a correção é cirúrgica — trocar uma linha de Security Rule — e pode ser aplicada sem downtime. É o tipo de achado onde o esforço de exploração é mínimo e o esforço de correção também é, o que só reforça o quanto vale auditar Security Rules do Firebase/Firestore como item recorrente de checklist, não como afterthought.
Remediação
A Globo aplicou as correções e autorizou a publicação. Todas as ações abaixo foram executadas:
Corrigir as Security Rules em produção e desenvolvimento, com a mesma urgência nos dois ambientes
allow read: if request.auth != null && request.auth.uid != null;
allow write: if request.auth.uid == resource.data.uid;
Auditar todas as coleções do projeto — o problema raramente fica isolado em uma única collection
Remover a configuração do projeto DEV do bundle de produção (ambientes diferentes não deveriam compartilhar a mesma base de código de frontend)
Auditar logs de acesso do Firestore em busca de acessos anônimos anteriores à correção
Adicionar os headers de segurança faltantes via CDN/proxy reverso, de forma centralizada para todo o ecossistema *.glbimg.com e *.globo.com
Lições para quem faz (ou defende contra) esse tipo de teste
Bundle JS é superfície de reconhecimento
Toda config de client-side exposta merece ser tratada como ponto de partida de um teste, não como ruído.
200 OK anônimo é sinal, não coincidência
Vale sempre testar o "óbvio" antes de assumir que já está coberto.
Provar escrita exige disciplina de limpeza
Documento sintético, ciclo completo de criação-confirmação-remoção-confirmação — sem isso, é alegação, não evidência.
Ambiente DEV ≠ risco reduzido
Se ele espelha a mesma infraestrutura e carrega dado real, ele é produção com outro nome.
Nem todo alerta de scanner é finding
O parâmetro platform ficou como informativo justamente por não ter sido validado manualmente — rotular certo o nível de confiança de um achado é parte do trabalho.
Este engajamento foi conduzido de forma autorizada, com evidências registradas e validadas antes da criação de qualquer finding oficial. A Globo corrigiu a vulnerabilidade e autorizou a publicação deste conteúdo.