BBLABSBBLABS
LaboratoriosAprender desde 0FuncionesPreciosTestimoniosDetrás de BBLABS
  1. Home
  2. Labs
  3. IDOR horizontal en la API de contenidos de un editor de notebooks colaborativos
Medium55 min

IDOR horizontal en la API de contenidos de un editor de notebooks colaborativos

By @gorka

La API de contenidos de notebooks localiza el workspace por el clusterId que manda el cliente sin comprobar que sea el tuyo: opera y lee el árbol de notebooks de otro miembro de la organización.

This hack was reported with no reward

360 views8 completedUpdated Nov 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· 8

espejoramirezhugo1
@espejoramirezhugo12 days ago
emperador2
@emperador19 days ago
kyzk33
@kyzk328 days ago
kaneki
@kaneki29 days ago
benjaminnocervigni
@benjaminnocervigni29 days ago
sharat chandra
@sharat chandraOct 2026
3nk1
@3nk1Oct 2026
r00thor
@r00thorSep 2026

Objectives

1
Descubrir en el propio editor qué parámetro identifica tu workspace ante la API de contenidos.
2
Encontrar el clusterId del owner de la organización en la superficie de colaboración del equipo.
3
Contrastar el 403 de la gestión de clusters frente al 200 de la API de contenidos con el mismo clusterId ajeno.
4
Confirmar con una escritura ajena (create) que la API de contenidos no comprueba propiedad del cluster.
5
Listar y leer el árbol de notebooks del cluster del owner mediante el mismo fallo de autorización.
6
Localizar el notebook de secretos de conexión del owner y extraer el root_api_token (la flag).
7
Descartar los 4 decoys del lab por razonamiento acotado, no por fuerza bruta ni adivinando UUIDs.

Information

Platform
HackerOne
Difficulty
Medium
Duration
55 min
Bounty
Sin recompensa
Completed
8
Creator
gorka@gorka
Updated
Nov 2026

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

curlNavegadorDevToolsPython requests

Prerequisites

  • HTTP y APIs REST
  • Uso básico de curl
  • Lectura de JSON

Tags

IDORBACAPI Abuse

Achievement you'll earn

Solve this lab to unlock this shareable achievement

Solve this lab to unlock this shareable achievement

Community writeups

Free · no account

The checklist I run on every new target

47 checks ordered by cost: first what can get you in trouble, then the cheap stuff, and finally the expensive stuff — which is where the big bounties are. I'll send it to your inbox right now.

Unsubscribe in one click, from any email.

Contenido del lab

Escenario

Kernelia es un SaaS de notebooks de datos colaborativos: los equipos de analítica escriben notebooks en SQL y Python contra sus almacenes de datos, y cada miembro de una organización tiene un workspace personal respaldado por un cluster de cómputo dedicado, identificado por un clusterId (UUID). Los notebooks y carpetas de cada workspace se gestionan a través de una API de contenidos que el propio editor consume para listar, leer y crear ficheros dentro del cluster sobre el que estás trabajando; y una página de colaboración de equipo donde cada miembro puede ver el workspace de los demás, para referenciarse y coordinar el trabajo.

Eres Sasha Morel, analista de baja privilegia recién incorporada al equipo de datos de Kernelia. Tienes tu propia cuenta, tu propio workspace y acceso legítimo a la plataforma como cualquier compañero — no hay nada roto en tu login. Tu misión como pentester: demostrar que, estando autenticada simplemente como una miembro más del equipo, puedes alcanzar el workspace de Dana Voss, la owner/administradora de la organización, y desde ahí extraer el secreto de conexión al almacén de producción que su notebook privado guarda.

Qué tienes

  • Cuenta demo: attacker@kernelia.local / password123 — Sasha Morel, analista y miembro real de la organización, con su propio cluster (morel-sandbox) y notebooks de ejemplo. Es tu punto de partida legítimo, no la vulnerabilidad.
  • El editor de notebooks de tu propio workspace y la página "Team Workspaces", donde el equipo colabora y ve el estado de cada workspace.

Guía paso a paso

  1. Recon + aprende el shape (baseline autorizado). Login con attacker@kernelia.local. Aterrizas en tu workspace; GET /api/auth/me te devuelve tu propio clusterId, y tu editor dispara GET /api/v1/notebooks/contents?clusterId=<tuyo>&path=/, que pinta tu árbol de notebooks. Operar sobre tu propio cluster funciona (estás autorizado) — pero acabas de aprender algo importante: la API de contenidos se direcciona por un clusterId que TÚ mandas como parámetro, no algo que el servidor deriva de tu sesión.

  2. Enumeración cross-org del clusterId del owner (el signpost crítico). La página "Team Workspaces" dispara GET /api/v1/org/workspaces: una funcionalidad de colaboración legítima que lista a cada miembro del equipo con su rol, el nombre de su workspace, su clusterId, el número de notebooks y su última actividad. La fila de Dana Voss (role: "owner") expone su clusterId en claro — sin adivinar ningún UUID: está ahí, pensado para que los compañeros se referencien unos a otros.

  3. El contraste diagnóstico y el IDOR. Antes de asumir que "todo está abierto", prueba a cambiar los ajustes del cluster de Dana: POST /api/v1/clusters/<clusterId de Dana>/settings → 403 not_cluster_owner — la propiedad del cluster SÍ se comprueba ahí. Repite la misma idea contra la API de contenidos con ese mismo clusterId ajeno: GET /api/v1/notebooks/contents?clusterId=<Dana>&path=/ → 200 con el árbol completo de Dana. Mismo identificador, misma sesión, dos comportamientos distintos — la API de contenidos es la anómala. Confírmalo escribiendo: POST /api/v1/notebooks/contents con {clusterId:<Dana>, path:"/poc-...", type:"directory", name:"..."} → 201, y la carpeta aparece de verdad al re-listar el cluster de Dana (o en el feed GET /api/v1/org/activity).

  4. Escalada a la flag. El árbol de Dana incluye un notebook llamado connection-secrets.ipynb, junto a otros de producción (prod-warehouse-etl.ipynb, org-billing.ipynb, carpeta ops/). El nombre no deja lugar a dudas. Léelo con el mismo fallo: GET /api/v1/notebooks/contents?clusterId=<Dana>&path=/connection-secrets.ipynb → su celda de configuración trae root_api_token = FLAG{...}, rodeado de campos de acompañamiento ficticios (service_connection_string, warehouse_password, signing_secret). Ese root_api_token es la flag.

Canales de feedback (no vuelas a ciegas)

  • Tu propio árbol de notebooks renderiza en 200 desde el paso 1 — confirmas que la API responde y que el clusterId es un parámetro tuyo, manipulable.
  • GET /api/v1/org/workspaces responde 200 con el clusterId de cada miembro en claro, incluido el de Dana — no hace falta enumerar nada.
  • El contraste 403 (cluster-settings sobre el cluster ajeno) frente a 200 (contenidos con el mismo clusterId ajeno) es observable en cada request, sin ambigüedad.
  • El create ajeno responde 201, y la carpeta recién creada aparece al re-listar el cluster de Dana y en GET /api/v1/org/activity — no hace falta confiar a ciegas en el código de estado.
  • La lectura de connection-secrets.ipynb responde 200 con la celda de configuración en texto plano, root_api_token incluido.

Decoys (descártalos por razonamiento acotado)

  • Decoy A — tu propio cluster (baseline autorizado). La API de contenidos funciona con normalidad sobre tu clusterId: es lo esperado, estás autorizada sobre tu propio objeto. Enseña el shape sin ser el bug. Lo interesante es que la MISMA llamada funcione también con un clusterId ajeno.
  • Decoy B — la consola de clusters SÍ enforca propiedad. POST /api/v1/clusters/:clusterId/settings y GET /api/v1/clusters/:clusterId llaman assertClusterAccess → 403 not_cluster_owner sobre el cluster de Dana en una sola prueba. Prueba que la autorización a nivel de objeto existe en la plataforma y funciona; aísla que el fallo real vive en la API de contenidos, no en "toda la app".
  • Decoy C — el endpoint de administración de la org es role-gated. GET /api/v1/org/admin/audit y POST /api/v1/org/admin/members exigen rol admin/owner → 403 requires_org_admin para tu cuenta member en una sola prueba. Prueba que el RBAC vertical funciona; el bug NO es de rol — es horizontal, entre pares autenticados, no necesitas ser admin.
  • Decoy D — el token placeholder de tu propio workspace. Tu demo-credentials.ipynb trae un demo_api_token de aspecto jugoso, pero es un placeholder: su md5 no coincide con la flag en cuanto lo compruebas. Te obliga a concluir que el secreto real vive en el cluster de Dana, no en el tuyo.

La falla del laboratorio

Clase: CWE-639 (Authorization Bypass Through User-Controlled Key), con CWE-284 (Improper Access Control), CWE-863 (Incorrect Authorization) y CWE-200/CWE-522 (Exposure of Sensitive Information / Insufficiently Protected Credentials, por el objeto alcanzado) como CWEs relacionadas. OWASP A01:2021 Broken Access Control.

La ruta de contenidos de notebooks (GET/POST /api/v1/notebooks/contents) sí exige sesión (requireAuth): cualquier miembro autenticado de la organización pasa el filtro. El defecto no es de autenticación — es que, antes de resolver y operar sobre el objeto identificado por el clusterId recibido en la query o el body, el handler nunca invoca assertClusterAccess(req.user, clusterId). Esa función existe en el codebase, está bien implementada (compara al propietario real del cluster contra el usuario de la sesión) y la usan correctamente las rutas hermanas de gestión de clusters — simplemente no se llama desde la ruta de contenidos. No hay ningún if invertido que depurar ni una comparación rota: es una llamada ausente en un único handler, mientras su hermano la hace bien. Por eso el defecto es invisible a un grep de patrones sospechosos: el código que sí existe es funcionalmente correcto.

Este es el motivo por el que "requiere login" no es sinónimo de "está autorizado". La autenticación responde a "¿quién eres?"; el control de rol (RBAC, aquí correctamente aplicado en las rutas de administración de la org) responde a "¿qué clase de acciones te permite tu rol?"; pero ninguna de las dos responde a la pregunta que de verdad importa en un sistema multi-tenant con identificadores de objeto suministrados por el cliente: "¿este objeto concreto es tuyo?". Esa tercera comprobación —autorización a nivel de objeto— es ortogonal a las otras dos, y es precisamente la que falta aquí. El resultado es un IDOR horizontal: no hace falta escalar de member a admin, basta con ser un compañero cualquiera y conocer (o descubrir legítimamente) el clusterId de otro.

Lo que habría prevenido el bug es enunciable en una línea de código: invocar assertClusterAccess(req.user, clusterId) antes de que la API de contenidos toque el almacenamiento — exactamente la misma llamada que ya protege la consola de gestión de clusters. Alternativamente, derivar el clusterId de la sesión del usuario en vez de aceptar el que manda el cliente eliminaría la superficie de ataque por completo para el caso de "mi propio workspace", aunque limitaría funcionalidades legítimas de colaboración que sí necesitan aceptar un clusterId ajeno de forma controlada (con su propio check de acceso).

Lección: que un endpoint "requiera sesión" no significa que esté autorizado a nivel de objeto — cada identificador de recurso que el cliente puede suministrar (un clusterId, un orderId, un documentId…) necesita su propio check de propiedad/acceso contra el usuario autenticado, y ese check hay que verificarlo comparando el comportamiento de rutas hermanas de la misma familia de API, no asumiendo que "si pide login, ya está protegido".

Laboratorio basado en la reproducción anonimizada (empresa ficticia Kernelia) de una técnica real de bug bounty sobre una plataforma de notebooks de datos cuya API de contenidos enrutaba operaciones de lectura y escritura por un identificador de cluster controlado por el cliente, sin comprobar la propiedad de ese cluster antes de operar.

The community

This isn't a platform with users.It's people breaking the same things you are.

Inside people ask, share what they found and solve together what would block anyone on their own. It's free to join: no account, no plan, no email required.

Join the BBLABS community on Discord
Join the Discord

Free and open. You don't need a BBLABS account to join.

BBLABS LogoBBLABS

Laboratorios de hacking basados en hackeos reales. Aprende practicando sobre entornos vulnerables, con la resolución explicada paso a paso.

Producto

  • Funcionalidades
  • Precios
  • Labs
  • Aprender desde 0
  • Blog
  • Detrás de BBLABS

Legal

  • Términos
  • Privacidad
  • Cookies
  • Entrar
  • Crear cuenta

Para cualquier duda, bug, soporte o sugerencia puedes escribir a team@bblabs.es

© 2026 BBLABS. Todos los derechos reservados.