Nuevos labs cada semana — Accede a todos desde 5€/mes

Nivel BásicoGratisbounty: $2.000

API keys expuestas en HTML público — $2.000 sin un solo bypass

Una plataforma de productividad entregaba un token de un CMS de terceros en window.CONFIG. Cualquiera con DevTools podía descargar templates de pago. Cómo cazar este patrón en cualquier app SPA.

Gorka El Bochi9 de mayo de 202610 min

Respuesta rápida

Hay bugs que no requieren fuzzing ni cadenas: solo leer lo que la app entrega al cliente. Una plataforma de productividad y notas inyectaba en su HTML un token de un CMS de terceros con permisos de lectura sobre todo el contenido de marketing — incluyendo URLs de descarga de templates de pago. Una sola petición HTTP autenticada, cero bypasses, $2.000 de bounty.


El objeto window.CONFIG

La aplicación afectada está construida en React (envuelta en Electron para escritorio). Como en la mayoría de SPAs, al cargar la página el servidor inyectaba un objeto de configuración global en el HTML:

javascript
window.CONFIG = {
  googleCaptchaSiteKey: "...",
  facebookPixelId: "...",
  contentful: {
    spaceId: "XXXXXXXXXXXXXX",
    ACCESS_TOKEN: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"  // esto no debería estar aquí
  },
  ...
}

Este objeto era completamente visible en el código fuente de la página. Sin autenticación, sin herramientas especiales. Ctrl+U y está ahí.

Entre las claves de terceros que viajaban en este objeto había una de Contentful — el CMS que la plataforma usaba para gestionar contenido de marketing, incluyendo el marketplace de templates.


Qué permite una API key de Contentful expuesta

Contentful expone una API REST. Con spaceId + ACCESS_TOKEN se puede consultar cualquier contenido del espacio sin más restricciones que el propio token:

css
window.CONFIG en el HTML público
    ↓
Extraer spaceId + ACCESS_TOKEN
    ↓
GET /spaces/{SPACE_ID}/environments/master/content_types?access_token=TOKEN
    ↓
Enumerar todos los content types del espacio
    ↓
Identificar content type "template" con campos price y url
    ↓
GET entries filtradas por fields.price >= 100
    ↓
Lista completa de templates de pago con URL directa de descarga

Las peticiones concretas

Paso 1 — Enumerar content types disponibles:

http
GET https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/content_types
    ?access_token={ACCESS_TOKEN}

La respuesta devuelve todos los tipos de contenido definidos. Entre ellos: person, category, template.

Paso 2 — Filtrar templates con precio ≥ $100:

http
GET https://cdn.contentful.com/spaces/{SPACE_ID}/environments/master/entries
    ?access_token={ACCESS_TOKEN}
    &content_type=template
    &fields.price[gte]=100

La respuesta incluía para cada template: título, categoría, precio y URL directa de descarga, independientemente de si el template era de pago.

Sin ningún paso adicional. Sin exploit. Solo leer la respuesta de la API con credenciales que la propia aplicación entregaba.


Root cause

El token de Contentful debería ser una clave de solo backend: el servidor la usa para construir el contenido que sirve al cliente, nunca debería llegar al cliente directamente. Al incluirla en window.CONFIG, cualquier usuario podía hacer las mismas consultas que el servidor, sin restricciones.

css
Build / Deploy
    ↓ ACCESS_TOKEN inyectado en window.CONFIG

Navegador / cliente
    ↓ HTML público accesible sin autenticación
    ↓ Token visible en cualquier DevTools

API Contentful
    ↓ Sin restricción de origen ni scope
    ↓ Acceso de lectura a todo el espacio

Dónde buscar este patrón

window.CONFIG, window.__INITIAL_STATE__, window.__NEXT_DATA__, window.APP_CONFIG — son los nombres más comunes. Cualquier app React, Vue o Next.js que hidrate estado en el cliente es candidata.

Lo que buscar dentro:

  • Claves de APIs de terceros: Contentful, Algolia, Sanity, Cloudinary, Firebase, Mapbox, Stripe (publishable vs secret key, ojo)...
  • Tokens con permisos de lectura amplios sobre recursos que no deberían ser públicos.
  • Parámetros internos que revelen estructura de datos, IDs, o endpoints no documentados.

El flujo es siempre el mismo: Ctrl+U o DevTools → buscar CONFIG, TOKEN, API_KEY, SECRET → evaluar qué permite hacer cada clave encontrada contra la API del servicio correspondiente.


Impacto

Descarga gratuita de cualquier template de pago del marketplace. En el caso concreto del reporte, templates con precios desde $100 hasta varios cientos. Acceso sin cuenta de pago, sin bypasses, con una sola petición HTTP autenticada con las credenciales que la propia app entregaba.

Reportado en mayo de 2024. Resuelto en menos de 24 horas. $2.000 de bounty.


Labs relacionados

Practica recon de tokens en código cliente y abuso de APIs de terceros mal restringidas: labs de Information Disclosure.

Practica esto en un lab

Information Disclosure

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados