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. SQL injection error-based en la resolución de tenant de un portal de identidad
MediumVDP50 min

SQL injection error-based en la resolución de tenant de un portal de identidad

By @gorka

El login y el 'olvidé mi contraseña' resuelven tu organización por username antes de validar nada; un fallo ahí filtra, trozo a trozo, un secreto interno que ningún endpoint sirve.

203 views1 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· 1

w4tchw0lf1
@w4tchw0lf5 days ago

Objectives

1
Detectar que el username del login/reset alcanza una consulta SQL sin parametrizar.
2
Provocar el error de sintaxis y leer el mensaje crudo del motor más la sentencia completa.
3
Reflejar en el error el resultado de una subconsulta propia dentro de una función XPath.
4
Enumerar information_schema hasta localizar una tabla interna que ningún endpoint expone.
5
Superar la truncación del error (~32 caracteres) partiendo la extracción con substring().
6
Descartar el auth-bypass y el resto de decoys con pruebas acotadas.
7
Capturar la flag almacenada en vault_secrets.federation_root_secret.

Information

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

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

curlNavegador webBurp SuitePython (requests)

Prerequisites

  • HTTP y formularios web
  • SQL básico (SELECT/WHERE)
  • Uso de curl o un proxy HTTP

Tags

SQLiInformation Disclosure

Achievement you'll earn

Solve this lab to unlock this shareable achievement

BBLABS.ESLab Solved
MediumVDP
// achievement_unlocked

SQL injection error-based en la resolución de tenant de un portal de identidad

SQLiInformation Disclosure
Sep 2026
gorka
solved_by@gorkaMember since Mar 2026
bblabs.es// real hacking practice

Community writeups

Contenido del lab

Escenario

Ingressia es un SaaS de identidad y acceso para plantillas (workforce identity): decenas de organizaciones clientes ("tenants") delegan en Ingressia el login de sus empleados. Cada tenant tiene su propio directorio de identidades y su propia configuración SSO; antes de comprobar ninguna credencial, el portal necesita saber a qué tenant pertenece el username que el empleado teclea, para pintar el branding correcto y enrutar la petición al backend de auth de su organización.

Te contratan como pentester externo, sin ninguna credencial del sistema, para auditar el portal público de acceso: la pantalla de login y el flujo de "olvidé mi contraseña". Ambos flujos son pre-auth y aceptan el mismo dato de entrada: un username.

Tu misión, sin usar la cuenta de demostración que Ingressia te entrega para orientarte: demuestra que puedes alcanzar datos internos que ningún endpoint documentado sirve, y tráete la prueba — un secreto de federación guardado en un almacén interno — como flag.

Qué tienes

  • Cuenta demo: attacker@ingressia.local / password123 — te deja entrar y ver el dashboard y otras pantallas, pero la inyección corre pre-auth: no la necesitas para explotarla.
  • Dos formularios públicos que piden un username (login y "olvidé mi contraseña") — la superficie de ataque completa está a la vista, sin credenciales de por medio.

Guía paso a paso

  1. Reconoce las dos superficies. El login (POST /api/auth/login) y el "olvidé mi contraseña" (POST /api/auth/password-reset) piden ambos un username antes de nada — es el paso de resolución de tenant. Mete un simple ' en ese campo (username=x', cualquier password) → 500 en vez del rechazo normal.
  2. Lee el error con calma. El cuerpo del 500 trae db_message (el texto crudo de MariaDB, tipo You have an error in your SQL syntax…) y statement (la sentencia completa: SELECT id, tenant_id FROM directory_identities WHERE username = 'x'' LIMIT 1). Conclusión: username se concatena crudo en la query, y la tabla detrás es directory_identities.
  3. Descarta el atajo evidente (Decoy A). Antes de perseguir la extracción, prueba si esto es un auth-bypass: username=' OR 1=1-- en el login. Resultado: 401, sin error, sin sesión. La comprobación de la contraseña es una segunda query, parametrizada + bcrypt.compare, totalmente separada de la resolución de tenant. Concluyes: esto es exfiltración, no bypass.
  4. Confirma la extracción in-band. Inyecta ' AND updatexml(1,concat(0x7e,(select version()),0x7e),1) AND '1'='1 en username. El 500 refleja algo como ~10.11.x-MariaDB~ en db_message — el error te devuelve el resultado exacto de tu subconsulta. Repite con database() para confirmar el schema (ingressia_directory).
  5. Enumera el schema. Con el mismo primitivo, pide (select group_concat(table_name) from information_schema.tables where table_schema=database()). Entre las tablas de negocio (tenants, sso_connections, auth_audit_log, reset_tokens…) aparece vault_secrets — que no está detrás de ningún endpoint que hayas visto. Pide sus columnas del mismo modo: secret_name, secret_value.
  6. Localiza la fila correcta. Enumera secret_name de vault_secrets: aparecen smtp_relay_password, webhook_signing_key, scim_bearer_token, session_signing_key y federation_root_secret — el nombre que destaca por describir un secreto raíz de federación entre Ingressia y sus tenants.
  7. Extrae con chunking. Pide secret_value where secret_name='federation_root_secret' — el db_message te lo devuelve cortado a ~32 caracteres (~FLAG{…~, sin cierre). La flag mide 38 caracteres. Parte la lectura en dos: substring(secret_value,1,31) y substring(secret_value,32,20), y concatena los dos fragmentos reflejados.
  8. Confirma el punto ciego / verifica. Reproduce el mismo ataque contra la segunda superficie (/api/auth/password-reset) para comprobar que no es una casualidad del login, y valida tu flag con bash flag/verify.sh (md5 de la preimagen).

Canales de feedback (no vuelas a ciegas)

  • El cuerpo del 500 (db_message + statement) en cada intento — el error crudo del motor y la sentencia SQL completa que se ejecutó.
  • El string reflejado entre ~…~ tras cada updatexml — confirma exactamente qué subconsulta se evaluó y con qué resultado.
  • La longitud visible del valor reflejado (~32 caracteres, cortado) — la señal explícita de que hace falta partir la extracción.

Decoys (descártalos por razonamiento acotado)

  • Decoy A — auth-bypass (username=' OR 1=1-- ): 401, sin error, sin login — el password-check va parametrizado con bcrypt.compare, aislado de la resolución de tenant.
  • Decoy B — typeahead de organizaciones (GET /api/directory/orgs?q='): 200 con results: [], ecoa q pero busca con WHERE name LIKE ? (bind) — el ' es texto literal, no delimitador.
  • Decoy C — ORDER BY con allowlist (GET /api/directory/reset-history?sort=(select 1)): 400 invalid sort field — el valor nunca llega a interpolarse.
  • Decoy D — campos hermanos parametrizados: un ' en password o en tenant (hint opcional del login) no dispara ningún error — solo username alcanza la query cruda.

La falla del laboratorio

Clase: CWE-89 (SQL Injection) + CWE-209 (Generation of Error Message Containing Sensitive Information) + CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor). OWASP A03:2021 (Injection) + A05:2021 (Security Misconfiguration).

La causa raíz vive en lib/directory.ts: resolveTenantByUsername() (login) y findAccountForReset() (reset) construyen la query de resolución de tenant concatenando username directamente en el string SQL y la ejecutan con pool.query(sql), sin values de bind. Es una función con un propósito legítimo — decidir a qué directorio de tenant pertenece un empleado antes de validar su contraseña — que casualmente interpola el identificador crudo. El propio password-check, en cambio, va por una segunda query parametrizada (WHERE tenant_id = ? AND username = ?) más bcrypt.compare, por eso la inyección nunca salta la autenticación: solo puede hacer que la consulta de resolución se comporte mal.

El segundo defecto amplifica al primero: middleware/verboseError.ts reenvía err.sqlMessage y la sentencia reconstruida (err.statement/err.sql) tal cual al cliente "para facilitar el diagnóstico de la federación entre Ingressia y el directorio del tenant". Sin ese error verboso, updatexml() seguiría rompiendo la query, pero el cliente solo vería un 500 genérico — el texto reflejado es lo que convierte cada fallo de sintaxis en un canal de exfiltración in-band, y también lo que le enseña al atacante la tabla directory_identities sin tener que adivinarla.

El usuario de base de datos de la aplicación, ingressia_app, tiene privilegios SELECT/INSERT/UPDATE únicamente sobre su propio schema (ingressia_directory), sin FILE, PROCESS ni superusuario. No existe ruta a LOAD_FILE/INTO OUTFILE ni a stacked queries: el techo de la explotación es exfiltración pura, lo que mantiene el laboratorio en Medium en vez de escalarlo a un RCE.

Lección: parametriza siempre los identificadores dinámicos, incluso en pasos que parecen "de infraestructura" (resolver un tenant, enrutar una petición) y corren antes del chequeo de negocio real; y nunca devuelvas al cliente el mensaje crudo del motor de base de datos ni la sentencia ejecutada — un handler de errores verboso convierte cualquier fallo de sintaxis en un oráculo de datos.

Laboratorio basado en la reproducción anonimizada (empresa ficticia Ingressia) de una técnica real de bug bounty sobre inyección SQL error-based en un flujo de 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