BBLABS v2BBLABSv2
>Home>Labs
>New labs

Latest 3 labs

Loading…

View all labs →
>Creators>Ranking
>Learn

Learn bug bounty

AcademyGuides, cheatsheets and glossaryVulnerabilitiesXSS, SQLi, IDOR, SSRF and moreHacker RoadmapYour step-by-step bug bounty pathBlogBug bounty guides and news
>Business>Pricing
ES
Log inLog in
>Home>Labs>New labs>Creators>Ranking>Learn>Business>Pricing
ES
Sign inCreate account
  1. Home
  2. Labs
  3. Enumeración de artefactos internos vía la API de listado de una media library
MediumVDP50 min

Enumeración de artefactos internos vía la API de listado de una media library

By @gorka

La API de listado de assets ecoa el filtro que le envías y el store de ficheros no autentica: enumera artefactos internos y extrae el secreto embebido en un bundle.

470 views5 completedUpdated Sep 2026
Log in to start

Learn to find this bug

A real HackerOne hack, reproduced for you to practice.

Create your account and practice real bugs that got paid. Download the environment, find it and learn the exact technique — your path to your first bounty.

650

hunters training

50

labs from real hacks

380

completions

$12,000

paid out for these bugs

40 flags captured this week
Create account
I already have an account

Access to all labs · no commitment · cancel anytime

Hackers who solved it· 5

w4tchw0lf1
@w4tchw0lf5 days ago
kaneki2
@kaneki10 days ago
alejandrorubiocam3
@alejandrorubiocam10 days ago
benjaminnocervigni
@benjaminnocervigni14 days ago
CR
@crash14 days ago

Objectives

1
Descubrir la API de listado de medios leyendo el XHR que dispara la galería pública.
2
Detectar que la respuesta ecoa el filtro que le mandas y qué parámetros acepta.
3
Enumerar artefactos internos cambiando u omitiendo el parámetro de visibilidad.
4
Contrastar el 401 de las rutas de escritura frente al 200 del listado y la descarga.
5
Descargar sin autenticación el bundle interno vigente desde su source_url.
6
Extraer el fichero de licencia embebido dentro del ZIP y localizar el secreto.
7
Descartar los 4 decoys del lab por razonamiento acotado, no por fuerza bruta.

Information

Platform
HackerOne
Difficulty
Medium
Duration
50 min
Bounty
VDP
Completed
5
Creator
gorka@gorka
Updated
Sep 2026

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

curlunzipNavegadorDevTools

Prerequisites

  • HTTP y APIs REST
  • Uso básico de curl
  • Nociones de ficheros ZIP

Tags

BACInformation DisclosureIDOR

Achievement you'll earn

Solve this lab to unlock this shareable achievement

BBLABS.ESLab Solved
MediumVDP
// achievement_unlocked

Enumeración de artefactos internos vía la API de listado de una media library

BACInformation DisclosureIDOR
Sep 2026
gorka
solved_by@gorkaMember since Mar 2026
bblabs.es// real hacking practice

Community writeups

Contenido del lab

Escenario

Assetria es una plataforma de gestión de activos digitales (DAM) para estudios de diseño web: sus clientes suben logos, imágenes de marca y bundles de build a una biblioteca central, y publican una galería pública para que sus propios equipos y sus partners consuman los assets aprobados. Por debajo de la galería vive una API de catálogo de medios (/api/v1/media) que la misma interfaz consume y que, según la documentación interna, también usan integraciones de terceros — de ahí que sea machine-readable y acepte parámetros de filtro explícitos en vez de estar cableada a una única vista.

Eres pentester contra el sitio público de Assetria. Tu misión: demostrar que la API de catálogo expone más superficie de la que la galería deja ver, y usar esa superficie para llegar hasta un artefacto interno del estudio (un bundle de build) que nunca debería ser público — y de ahí, extraer el service_license_key embebido dentro, un secreto de licencia de la organización.

Qué tienes

  • Cuenta demo: attacker@assetria.local / password123 — un colaborador de marca (contributor) de baja privilegia. Te sirve para entrar y ver tu perfil, pero no te da acceso al panel de administración (/admin/media, restringido a admin) y no es el punto de entrada de la vulnerabilidad.
  • La galería pública de Assetria, con buscador, paginación y vista de detalle por asset.
  • El bundle JavaScript público del front-end (galería + panel) — servido igual que cualquier SPA, sin ofuscar ni requerir sesión para descargarlo.

Guía paso a paso

  1. Recon del surface: encuentra el XHR que alimenta la galería. Navega la galería pública y abre la pestaña de red. Verás que dispara GET /api/v1/media?type=image&visibility=public&page=1&per_page=20. La respuesta es JSON: items[] (cada uno con id, type, visibility, title, filename, source_url, size, created_at), un bloque pagination, y un bloque filter que ecoa exactamente lo que mandaste: "filter":{"type":"image","visibility":"public","q":null}. Auto-descubrimiento puro: no adivinas ni la ruta ni los nombres de los parámetros, los lees directamente del tráfico que la propia galería genera.

  2. Prueba a cambiar o quitar el filtro de visibilidad. El bloque filter que acabas de ver ecoando tu petición es una invitación a experimentar: repite la llamada con visibility=internal, o quítala del todo. El filter de la respuesta cambia exactamente con lo que mandaste — confirmación de que el servidor aplica tu valor tal cual, sin comprobar si tienes permiso para pedirlo. Aparecen de inmediato items nuevos que la galería nunca mostró: "visibility":"internal", "type":"application", con title del estilo "Studio Brand Kit — internal build 2025" y filename con el patrón studio-brandkit-build-<año>-<sufijo>.zip. El total de pagination sube respecto a la vista pública, y cada item trae su propia source_url ya resuelta.

  3. Contrasta con las rutas de escritura del mismo catálogo. Antes de asumir que "toda la API es así de abierta", prueba a crear o borrar un asset sin sesión (POST/DELETE sobre la ruta de administración del catálogo): responde 401 sin cookie. El listado y la lectura, en cambio, no te pidieron nada. La asimetría es la pista: la restricción existe de verdad en esta API — solo que no cubre lectura ni descarga, únicamente escritura.

  4. Descarga sin autenticarte el artefacto interno más reciente. Con la source_url del paso 2, pide el fichero directamente (sin cookie, sin header Authorization): 200, content-type: application/zip. El store de ficheros que sirve estos assets no comprueba nada antes de entregar los bytes por su clave.

  5. Descomprime y localiza el secreto — confirma que es el build vigente. Dentro del ZIP hay un fichero LICENSE.key con nombre inequívoco. Contiene varios campos de acompañamiento y uno decisivo: service_license_key. En el build 2025 (el más reciente por created_at y por el nombre del title) ese campo trae la flag en claro. Si grepeas un build más viejo primero, el campo sale vacío o marcado EXPIRED — señal de que hay que quedarse con el vigente.

Canales de feedback (no vuelas a ciegas)

  • El XHR de la galería responde 200 con JSON legible/grep-able; el bloque filter ecoa exactamente lo que mandaste, invitando a experimentar con otros valores.
  • Cambiar o quitar el filtro de visibilidad hace aparecer de inmediato items nuevos y sube el total de la paginación — no hace falta adivinar si "hay algo más", la respuesta te lo muestra.
  • El contraste 401 (crear/borrar sin sesión) frente a 200 (listar/leer/descargar sin sesión) es observable en cada request, sin ambigüedad.
  • La descarga del artefacto responde 200 application/zip sin cookie; el fichero de licencia dentro del ZIP tiene un nombre que no deja lugar a dudas sobre qué grepear.
  • Los builds antiguos traen el campo del secreto vacío o EXPIRED — un confirmador explícito de "este no es, prueba el vigente", no un callejón silencioso.

Decoys (descártalos por razonamiento acotado)

  • Bundles públicos de marketing. Ya visibles en la galería (type:"application", visibility:"public"), descargan 200 igual que el resto. Pero dentro solo hay un README.txt de prensa y un par de imágenes — sin LICENSE.key. Refutable en una prueba: unzip + grep no encuentra el campo.
  • Una imagen de campaña sin publicar. Aparece con la misma etiqueta de visibilidad "escondida" que los artefactos de verdad, y el mismo fallo la deja descargar sin auth — exposición real, pero es un .png: no hay ZIP que abrir ni secreto que extraer. Refuerza el fallo sin ser el objetivo.
  • El login con la cuenta demo. Entrar con attacker@assetria.local no abre ningún camino nuevo: es una cuenta contributor, no tiene acceso al panel de administración, y la descarga del artefacto interno funciona exactamente igual con o sin sesión. Refutable en una prueba: pedir el artefacto sin cookie ya da 200.
  • Builds antiguos del brand kit. Descargan 200 igual que el vigente, pero su LICENSE.key trae el campo del secreto vacío o marcado EXPIRED (clave rotada). Fuerza a no conformarte con el primer ZIP que encuentres, sino a comparar fechas y quedarte con el build 2025.

La falla del laboratorio

Clase: CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor), con CWE-862 (Missing Authorization), CWE-306 (Missing Authentication for Critical Function) y CWE-602 (Client-Side Enforcement of Server-Side Security) como CWEs relacionadas. OWASP A01:2021 (Broken Access Control) + A05:2021 (Security Misconfiguration).

El catálogo de medios de Assetria trata visibility y type como filtros que el cliente puede suministrar libremente, y los aplica al pie de la letra (WHERE visibility = <valor recibido>, o sin cláusula alguna si se omite) sin derivar, del rol o de la sesión de quien pregunta, qué visibilidad tiene derecho a pedir. La restricción "un usuario anónimo solo debería ver public" no vive en el servidor: vive en la galería, que sencillamente elige mandar visibility=public en su propia llamada. Cualquier otro consumidor de la misma API — o el mismo atacante con curl — puede pedir lo contrario. No es un if invertido ni una comprobación rota: es la ausencia de una regla de autorización sobre un parámetro que el código de listado, por lo demás, procesa correctamente.

Esa ausencia se combina con una segunda: el store de ficheros que sirve los blobs por su clave de almacenamiento no exige ninguna credencial antes de devolver el contenido. Aunque el listado hubiera estado bien protegido, cualquiera que conociera (o enumerara) una clave de almacenamiento podría descargar el fichero igualmente. Juntas, ambas ausencias convierten un catálogo pensado para distinguir "público" de "interno" en una fuga total: el atacante enumera lo que no debería ver y lo descarga sin credencial alguna.

La escalada final añade una tercera capa, más de diseño que de código: el equipo de build embebe un secreto de licencia real (service_license_key) dentro de un artefacto binario pensado para uso interno, confiando en que ese artefacto nunca sea alcanzable desde fuera. Cuando la exposición de arriba lo hace alcanzable, el secreto sale con él — sin que el atacante necesite ninguna credencial en ningún punto de la cadena.

Lo que habría prevenido el bug es enunciable en una frase: derivar la visibilidad permitida del rol del solicitante en vez de aceptar la del cliente (anon ⇒ solo public, sin excepción), y exigir autenticación/autorización en el store de ficheros igual que se exige en las rutas de escritura del mismo catálogo. Adicionalmente, ningún artefacto con secretos reales debería residir en un store alcanzable sin control de acceso, con independencia de si su URL es "adivinable" o no.

Lección: que una API "solo" acepte parámetros de filtro razonables (tipo, visibilidad, paginación) no implica que esté autorizando correctamente quién puede pedir cada valor — filtrar y autorizar son preguntas distintas, y basta con responder bien a la primera para dar una falsa sensación de control sobre la segunda. Cuando la UI restringe algo "por defecto" mandando un valor concreto, comprueba siempre si esa restricción también vive en el servidor o si solo vive en la elección del cliente.

Laboratorio basado en la reproducción anonimizada (empresa ficticia Assetria) de una técnica real de bug bounty sobre una media library cuya API de listado indexaba artefactos internos que la interfaz nunca enlazaba, servidos por un store de ficheros sin autenticación.

Contact

Practice, learn and hack

Bug bounty practice platform with labs based on real hacks. Learn ethical hacking in safe environments.

contact→

Follow us

YouTube
@0xGorka
X
@gorkaelbochi
LinkedIn
gorka-el-bochi-morillo
Instagram
@_.gorkaaa.b
Email
team@bblabs.es

Access every lab from €7.99/mo

New labs every week. Cancel anytime.

Create account

BBLabs is the bug bounty labs platform where you learn bug bounty with real vulnerabilities extracted from paid hacks on HackerOne, YesWeHack and Bugcrowd. Here you practice web hacking —XSS, SQLi, IDOR, SSRF, CSRF and more— in downloadable environments, capture the flag, read the writeup and apply the technique on active bug bounty programs.

BBLabs is the alternative to HackTheBox, TryHackMe and PentesterLab for those who want to practice bug bounty with real hacks instead of artificial CTFs. From €7.99/mo, no commitment.

→ Learn bug bounty from scratch→ How to do bug bounty step by step→ Real bug bounty reports→ BBLabs for companies and academiesLabsAcademyVulnerabilitiesToolsHacker rankingXSS labsIDOR labsSSRF labsCSRF labsHackTheBox alternativeHack4u alternativeTryHackMe alternativePortSwigger alternativePentesterLab alternativeBug Bounty Labs comparisonHackerOne to practiceOffSec / OSCP alternativeINE / eWPT alternativeHTB Academy alternativeDVWA alternativeJuice Shop alternativeVulnHub alternativePentesterAcademy alternativeRoot-Me alternativeHackTheBox vs TryHackMeBest bug bounty platforms 2026BlogSpoilersWhat is bug bounty?How much do you earn in bug bounty?OWASP Top 10 explainedBest sites to practice web hackingHow to become an ethical hacker from scratchBurp Suite tutorial (Spanish)OSCP guide and prepGoogle Dorks for bug bountyHow much an ethical hacker earns in SpainBug bounty tools 2026Best cybersecurity certifications 2026Burp Suite tutorialsqlmap tutorialffuf web fuzzingnuclei tutorialHTTP Request SmugglingWAF bypassPrompt injection (LLM)Google Dorks
Made withand code
TermsPrivacyCookiesComparisonES

© 2026 BBLABS v2 — All rights reserved