Volver al blog

Caso de estudio ciberseguridad pentesting hacking ético

Intenté hackear una tienda web. Encontré una vulnerabilidad que podía afectar la confianza del negocio.

Por Armando Peña 8 min de lectura 2 de septiembre de 2026

man wearing red hoodie

Foto de  sebastiaan stam  en  Unsplash

Hay una diferencia bastante grande entre construir aplicaciones seguras y atacar aplicaciones.

Durante mucho tiempo mi relación con la seguridad web fue unilateral. Yo era el tipo que decía:

"Hay que validar esto."
"Ese punto de acceso necesita autenticación."
"Ponle una política de seguridad de contenido."
"No confíes en datos enviados por el cliente."

Pensaba como desarrollador.

Últimamente estoy intentando aprender el otro lado.
¿Qué pasa si en lugar de preguntarme cómo proteger una aplicación me pregunto: ¿cómo puedo romperla?

Tuve la oportunidad perfecta: una tienda web real, autorización para probarla y una regla sencilla.

A ver qué puedo romper.

Terminé encontrando una vulnerabilidad que, aunque no permitía tomar el control completo del sitio, podía afectar la confianza de los clientes y, dependiendo de cómo se utilizara, generar problemas tanto para el propietario como para quienes usaran la tienda.

Eso ya me parecía bastante más interesante.


🎯 El objetivo

La aplicación era una pequeña tienda web desplegada en Vercel.

A simple vista parecía normal:

  • Catálogo de productos
  • Páginas individuales
  • Carrito
  • Enlaces para compartir carritos
  • Botones de contacto

Mi primera impresión fue que estaba frente a una aplicación Next.js.
Me equivoqué.
Y ese pequeño error terminó siendo importante.


01 — Reconocimiento

Tenía acceso al código, pero decidí no empezar por ahí.
Primero quería responder una pregunta:

¿Qué puede descubrir un atacante que solamente conoce el dominio?

Empecé desde el exterior:

  • robots.txt
  • sitemap.xml
  • Direcciones y parámetros
  • Productos y carrito
  • Cabeceras HTTP

No había ningún /admin mágico.
El sitemap.xml tampoco regaló nada interesante.

Hallazgos interesantes: 0

Pero todavía no había empezado la parte divertida.


02 — ¿Dónde está el servidor?

Esperaba encontrar peticiones como:

GET /api/products
GET /api/products/123
POST /api/cart

No las había.
La aplicación prácticamente no tenía una API propia.

Volví a revisar la tecnología. Mi hipótesis inicial había sido Next.js. Resultó ser una SPA (React + Vite).

La arquitectura era simplemente:

┌───────────────┐
│   Navegador   │
└───────┬───────┘
        │
        ▼
┌───────────────────┐
│ Aplicación React  │
│ Productos         │
│ Carrito           │
│ Lógica            │
│ Navegación        │
└───────────────────┘

No había servidor propio ni base de datos.

Muchas vulnerabilidades tradicionales quedaron fuera de juego de inmediato:

Vulnerabilidad Estado
Inyección SQL
Salto de autenticación
Autorización de API
Inyección HTML del servidor

Si no podía atacar una API… podía atacar las suposiciones de la aplicación.


03 — 🧠 ¿Qué pasa si cambio el ID?

Una de las mejores técnicas de seguridad sigue siendo una de las más simples:
cambiar cosas que aparentemente no deberían cambiarse.

Encontré direcciones asociadas a productos mediante identificadores. Hice lo obvio:

/producto/1
     ↓
/producto/2
     ↓
/producto/3
     ↓
...

Y apareció el primer comportamiento inesperado.

🚨 Hallazgo 01 — Manipulación de identificadores

Era posible acceder, mediante la manipulación de identificadores, a productos que aparecían como agotados.

Visualmente:

┌─────────────────────────────┐
│          PRODUCTO           │
│          $XX.XX             │
│        ❌ AGOTADO            │
└─────────────────────────────┘

Pero modificando el identificador (/producto/[otro-id]), el producto podía aparecer dentro de determinados flujos de la aplicación.

⚠️ ¿Esto es un IDOR ("referencia directa insegura a objetos")?

Es tentador escribir “¡IDOR crítico!”, pero sería incorrecto.

Un IDOR clásico implica algo como:

GET /api/orders/123  →  123 → 124

y que el servidor entregue un recurso que debería proteger.
Aquí no había ese servidor. La aplicación era completamente del lado del cliente.

Por eso lo describo como:

Manipulación de identificadores y validación insuficiente de la lógica de negocio.

La diferencia no es académica. Clasificar correctamente una vulnerabilidad también forma parte del trabajo de seguridad.


04 — 🎒 ¿Y qué pasa con el carrito?

Si puedo manipular el producto, ¿puedo manipular también el carrito?

Sí.

Las direcciones utilizadas para compartir carritos podían modificarse para introducir productos que normalmente estaban agotados.

Producto agotado
       │
       ▼
Manipular identificador
       │
       ▼
Modificar dirección
       │
       ▼
Compartir carrito
       │
       ▼
Producto aparece

Ya no era solo una página mostrando información incorrecta.
La lógica de negocio estaba aceptando estados que el propietario probablemente no pretendía permitir.


05 — 👀 Revisemos las cabeceras

Volví a lo básico:

curl -I https://OBJETIVO

La aplicación no indicaba al navegador que impidiera cargar sus páginas dentro de un <iframe>.

Busqué:

  • X-Frame-Options → ❌
  • Content-Security-Policy: frame-ancestors → ❌

Así que hice lo que cualquier persona razonablemente curiosa haría 🤓☝️: intenté meter la tienda dentro de un iframe.


06 — 🥷 Clickjacking

La prueba de concepto fue deliberadamente sencilla:

<iframe src="https://OBJETIVO"></iframe>
┌─────────────────────────────────┐
│      PÁGINA CONTROLADA          │
│          POR MÍ                 │
│   ┌─────────────────────────┐   │
│   │    TIENDA OBJETIVO      │   │
│   └─────────────────────────┘   │
└─────────────────────────────────┘

Funcionaba.
La tienda podía ser cargada dentro de una página externa.
Esto es Clickjacking (secuestro de clics).


07 — 🧪 Construyendo la prueba de concepto

Tenía dos piezas:

Pieza Resultado
Manipulación de ID Producto agotado accesible
Carrito manipulable Estados no esperados
Aplicación embebible Clickjacking

La pregunta era: ¿qué pasa si las combino?

Construí una página externa que cargaba una dirección manipulada del carrito dentro de un iframe y colocaba elementos visuales propios encima. El objetivo era demostrar cómo una página controlada por un atacante podría intentar engañar al usuario usando la interfaz legítima de la tienda.

No obtuve:

  • ❌ Contraseñas
  • ❌ Ejecución remota de código
  • ❌ Acceso al servidor
  • ❌ Base de datos
  • ❌ Control de la aplicación

Pero sí demostré:

  • ✅ Manipulación de estados
  • ✅ Carrito manipulable
  • ✅ Aplicación cargable dentro de un iframe
  • ✅ Posibilidad de combinar ambos comportamientos

Y eso ya tenía consecuencias interesantes.


08 — 💀 ¿Por qué debería importarle esto al dueño de una tienda?

Aquí es donde un problema técnico se convierte en un problema de negocio.

Imagina que un cliente recibe un enlace aparentemente relacionado con una promoción. En lugar de llevarlo directamente a la tienda, el enlace conduce a una página controlada por un tercero. Esa página puede presentar contenido legítimo de la tienda dentro de su propia interfaz.

El cliente puede pensar:

“Estoy comprando en la tienda.”

cuando en realidad está interactuando con una página construida por otra persona.

Esto abre la puerta a escenarios de ingeniería social:

  • Confusión sobre precios o disponibilidad
  • Pérdida de confianza en la tienda
  • Campañas de engaño usando la identidad visual del negocio
  • Clientes que creen estar interactuando directamente con el propietario
  • Posibles reclamaciones si alguien resulta engañado

No demostré que el sitio permitiera realizar fraude automáticamente (sería irresponsable afirmarlo).
Lo que sí demostré fue más concreto:

La aplicación podía ser utilizada como componente dentro de un escenario de engaño controlado.

Para una tienda que depende de la confianza del cliente, eso merece atención.


09 — 🤖 ¿Y la inteligencia artificial?

Durante la investigación usé inteligencia artificial como segunda opinión.

Primero le pasé la aplicación para una evaluación inicial. Después utilicé análisis asistido sobre el código. Fue útil para identificar rápidamente:

  • React + Vite
  • Sin servidor propio
  • Sin base de datos
  • Sin autenticación
  • Dependencias con rangos de versiones

Pero la parte más interesante apareció en la revisión manual.

La IA podía ayudarme a preguntar:

“¿Qué problemas podría tener este código?”

Yo necesitaba preguntarme:

“¿Qué ocurre si utilizo esta funcionalidad de una manera que el desarrollador no esperaba?”

Ahí es donde la revisión humana sigue siendo fundamental.


10 — 🔧 De la vulnerabilidad al parche

Para el Clickjacking existen varias opciones:

X-Frame-Options: DENY

o mediante CSP:

Content-Security-Policy: frame-ancestors 'none'

La elección depende de si la aplicación necesita permitir que determinadas páginas sean embebidas.

Para el problema de la lógica de negocio, la aplicación debe dejar de confiar en información controlada por el cliente y validar correctamente la disponibilidad del producto dentro de cada flujo relevante.


11 — 🔁 La parte satisfactoria: volver a atacar

Una vulnerabilidad no está cerrada simplemente porque alguien diga “ya agregamos el encabezado”.

Hay que volver a intentar el ataque.

Antes:

¿Puede cargarse la aplicación dentro de un iframe? → SÍ ✅

Después:

¿Puede cargarse la aplicación dentro de un iframe? → NO ❌

Ese pequeño momento es una de las partes más satisfactorias de una auditoría.

Porque ya no estás diciendo “creo que está arreglado”.
Estás diciendo: “Intenté explotarlo otra vez y ya no funciona.”


12 — 🧠 Lo que realmente aprendí

Este caso fue pequeño.
No descubrí una ejecución remota de código.
No obtuve acceso a un servidor.
No robé una base de datos.

Y precisamente por eso me parece interesante documentarlo.

La arquitectura importa

No puedes atacar una aplicación sin entender primero qué tienes delante.

React + Vite  ≠  Frontend + API + Backend + Base de datos

La superficie de ataque cambia completamente.

La lógica de negocio es difícil de automatizar

Un escáner puede detectar cabeceras, TLS, versiones y configuración.
Es mucho más difícil que comprenda:

“¿Debería poder introducir este producto agotado en este carrito?”

Ahí hay que pensar.

Los ataques interesantes suelen empezar con preguntas pequeñas

  • ¿Qué pasa si cambio este ID?
  • ¿Qué pasa si cambio esta dirección?
  • ¿Qué pasa si cargo esta página dentro de un iframe?
  • ¿Qué pasa si combino ambas cosas?

Y de repente tienes una línea de investigación.


🥷 Epílogo

Mi experiencia profesional está mucho más relacionada con construir aplicaciones que con atacarlas.
Estoy empezando a explorar seriamente la seguridad ofensiva, el pentesting y el Red Teaming.

Creo que esta es una de las mejores formas de aprender:
atacar sistemas reales, con autorización, y documentar exactamente lo que ocurre.

No quiero limitarme a aprender una lista de vulnerabilidades de memoria.
Quiero aprender a mirar una aplicación y preguntarme:

¿Qué está dando por sentado este desarrollador?

Porque muchas veces ahí está la grieta.

Y quizá esa sea la mayor diferencia entre pensar como desarrollador y pensar como atacante.

El desarrollador pregunta:

“¿Cómo hago que esto funcione?”

El atacante pregunta:

“¿Qué pasa si hago que funcione de una manera que nadie esperaba?”

Y esa pregunta…
es peligrosamente divertida.

¿Te ayudó este artículo?

Si necesitas ayuda con tu presencia digital, cuéntanos tu idea.

Consultar por WhatsApp

Posts similares

person writing on white board
  • low-net
  • low-bandwidth
  • offline-first

Cómo transmitir una pizarra con 120 Kbps y una conexión inestable?

Quería que un dibujo cruzara la conexión de mi clase de inglés, alrededor de 120 kbps y con caídas constantes. Empecé comprimiendo una pizarra y descubrí que comprimir era la parte fácil: el problema era decidir qué información enviar y qué hacer cuando la conexión simplemente no está. Esta es la cadena de descubrimientos que llevó a LOW-NET, el laboratorio donde estoy midiendo esa pregunta.

Caso de estudio1940 palabras · 10 min de lectura

Armando Peña 1 0
man in black long sleeve shirt using computer
  • atribucion
  • utm
  • analytics

Como medir marketing cuando las redes no te dan datos? La atribución va en el enlace!

Las plataformas no te dicen de qué publicación salió tu cliente, y no tienen por qué. La atribución la pones tú, en el enlace, antes de publicar. Este es el sistema mínimo y honesto para saber qué canal funciona, de las visitas a las ventas: empieza por UTM y añade complejidad solo cuando hay un problema real.

Técnico1539 palabras · 8 min de lectura

Armando Peña 1 0