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

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

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.

Gratis · sin cuenta

La checklist que repaso en cada objetivo nuevo

47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.

Te das de baja en un clic, desde cualquier correo.

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

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.