Category: Security Research

Beyond the Vault: What Banking Sites Quietly Share Before You Ever Log In

Beyond the Vault: What Banking Sites Quietly Share Before You Ever Log In

Banks present themselves as the most careful custodians of personal and financial data. Customers expect that trust to extend to every digital interaction including public websites and online application flows. Yet Jscrambler’s Security Research Team found that many banking experiences transmit sensitive information to third-party advertising, analytics, and personalization platforms. In many cases, the data leaves the browser before the user has made a consent choice. In others, it continues to flow even after users have rejected tracking technologies.

banking page third-parties

Following on from our previous pixel research, Jscrambler conducted a runtime analysis of the tracking and personalization scripts running on financial services websites across Europe and the USA. Our previous pixel research analysed retail, hospitality, and healthcare websites. This new research focuses on retail and investment banks, payment infrastructure providers, and online customer journey flows including opening accounts, applying for credit, and simulating loan approval processes. These are among the most regulated and sensitive pages on the web, and the behaviors we observed go well beyond what ordinary user journey tracking requires.

The findings were consistent enough to be described as patterns rather than one-off errors. Across the fourteen financial-services cases we documented in this research, tracking fired without a valid consent choice at nine separate companies, whether before the banner was actioned, after the user rejected all cookies, or despite an essential-cookies-only selection. The data then reached approximately a dozen distinct third parties.

Tracking pixels and personalization tags were once simple tools for confirming an ad impression or counting a visit. On a banking website, the same tags now sit alongside mortgage calculators, account-opening forms, and loan applications, and they behave the same way as they do on a retailer’s checkout page. They collect contact details, hash them, attach them to persistent identifiers, and transmit product and financial intent to platforms the bank does not control. Most institutions are unlikely to realize how much of this is happening by default.

From Anonymous Visit to Named Banking Customer

The most direct concern is a pattern that Jscrambler Security Research repeatedly observed across banks in different countries: re-identifiable contact data leaving banking pages and entering advertising platforms.

As we documented in our previous research, TikTok and Meta normalize an email address or phone number, convert it into a SHA-256-hashed identifier, and send it out. The hashing is deterministic, so the same input always produces the same output. A hash built from a known email or phone number can therefore be traced back to that person by anyone who holds the original value. It does not anonymise the user, it merely relabels them.

banking sites examples

A representative case was the online mortgage flow of a Spanish bank. The user was shown a cookie banner offering the usual choices to accept, configure, or reject, with text explaining that the site used both first- and third-party cookies for analytics and advertising based on a profile built from the user’s browsing habits. After the user accepted, TikTok received the user’s hashed email and hashed phone number through a request to its pixel endpoint (analytics.tiktok.com/api/v2/pixel/inter). The request originated from an embedded iframe serving the mortgage page, and the payload carried the hashed identifiers alongside the:

  • Full page URL
  • Referrer
  • Anonymous ID
  • Session ID
  • Pageview ID

The bank publishes a cookie policy that enumerates the cookies and vendors it sets, but TikTok does not appear in it, and TikTok is absent from the bank’s privacy policy as well. A customer reading either document would have no way to know that accepting cookies would send their hashed contact details to TikTok.

The recipient of sensitive data was not always a social media platform. During the online account and card application flow of a second Spanish bank, requests to Google Ads, Google Consent Mode, and Google Analytics carried the user’s hashed phone number directly in the request URL, in parameters named telefono_hasheado and ep.telefono_hasheado. The data travelled to Google’s advertising and measurement infrastructure from inside a live banking application, on a page where the customer was entering personal details.

In the most serious version of this pattern, the data was not hashed at all. During the online account-opening flow of a Portuguese bank, a request was sent to Evergage, now part of Salesforce Interaction Studio, including the user’s email address in the request URL. The value was Base64-encoded, which is simple encoding, not encryption, so it was trivially decodable back to the raw address.

Later steps of the same flow sent additional personal data to the same platform, including the customer’s name, age, Portuguese tax number (NIF), and a Salesforce Marketing Cloud contact key. Several of these events carried an empty consents array. Sending raw, recoverable personal data and a government identifier from an account-opening flow to a marketing personalization platform poses a materially different risk from a hashed advertising signal, and it sits at the top of any reasonable severity scale.

Two further cases sent a hashed email to Google after the user had actively rejected cookies. Because the consent story is the sharper issue there, we return to them below.

The Financial Intelligence Problem

Identity is only one part of the exposure. The other is financial intent: the signals that reveal what customers are looking for, the decisions they are considering, and where they are in a financial journey. This creates a dual risk: customers may unknowingly expose sensitive financial information, while banks may unintentionally reveal valuable intelligence about customer demand, product interest, and conversion activity to third parties.  On a retail site, this type of exposure might reveal:

  • Product names
  • Prices
  • Cart values

On a banking site, the implications are far more significant. The data can reveal borrowing behaviour, financial needs, product preferences, and progress through high-value application journeys.

In the personal credit flow of a Portuguese consumer-credit provider, Google Analytics received the full loan application URL, which encoded the customer’s specific choices: a requested amount of €2,500, a 12-month term, an indication that insurance was selected, and a flag for finance charges. Google Analytics also received event names from the application, including steps labeled as the personal data and banking details stages of the credit flow. This exposed not only that a person was applying for a loan, but the amount requested, repayment period, additional product selections, and progression through the application funnel.

On the personal credit simulator of a second Portuguese bank, a Google Analytics event transmitted the full set of simulation values after a user completed a credit simulation:

  • Desired loan amount
  • Repayment period
  • Monthly payment
  • Annual Percentage Rate (TAEG)
  • Nominal interest rate (TAN)
  • Total amount payable (MTIC)
  • Product family and subfamily

No email, phone number, or hashed identifier appeared in that particular request, but the financial profile it described was remarkably detailed. Aggregated across many sessions, this is a continuous feed of demand, pricing sensitivity, borrowing preferences, and conversion behaviour for a bank’s lending products, flowing into a third-party platform that uses signals collected across its advertising ecosystem to refine audience models and optimize advertising performance, including for other financial services organizations.

This is the financial services equivalent of the commerce data problem we described in our previous research. Organizations may believe they are simply measuring user behavior through standard analytics. In practice, they are exporting a detailed, ongoing view of customer financial intent and product economics to third parties, often with little visibility into the scope or sensitivity of data being shared.

The timing of these requests matters as much as their content. Consent only protects the user if the tracking waits for it, and during this analysis, we found three distinct ways that it did not:

  • Tags that fired before the banner was actioned.
  • Tags that kept firing after the user rejected everything.
  • Consent choices that were never carried into the part of the site doing the tracking.

consent

The first pattern was tracking that ran before any choice was made. On the wealth-management site of a major US bank, a “Yext” tracking pixel sent a pageview to yext-pixel.com while the cookie banner was still on screen. The page source showed analytics enabled by default (window.yextAnalyticsEnabled = true), and the function that was supposed to gate analytics on consent simply set the same value to “true” again, so the pixel was effectively always on. The same pre-consent firing appeared elsewhere. On the site of a major US investment bank, a Parse.ly content analytics pageview (p1.parsely.com/px/) and an Adobe Experience Platform request went out before consent.

This before-consent firing was something we saw in both the US and Europe alike, from the US wealth-management and investment-banking cases, to a European bank whose fingerprinting script, covered below, also loaded before the banner was answered.

On a UK payments provider’s site, the conversational marketing platform “Drift” received visitor and page data, including an anonymous ID, country, hostname, locale, and session metadata, ahead of the banner, alongside an early Google Analytics request. On a US payments-infrastructure provider’s site, a Google DoubleClick advertising request (adservice.google.com/ddm/fls/z/) loaded while the cookie banner was still displayed.

The second pattern is harder to defend because the user explicitly said no. On the digital account-opening flow of a third Spanish bank, Google Ads and DoubleClick received the user’s hashed email in the request URL, via the match_id and u5 parameters, even though cookies had been rejected. The request itself carried pscdl=denied, confirming the rejected state. This is one of the two cases flagged earlier: a hashed contact identifier sent to an advertising and conversion endpoint from an account-opening page after the user declined.

A Portuguese bank’s account-opening flow went further still. With cookies rejected, Google Analytics received a SHA-256 hash of the email the user had entered during registration, again alongside the consent-denied signals (gcs=G100, npa=1, pscdl=denied). On the same flow, LinkedIn captured the journey step by step:

  • Page visits
  • Form-submission clicks
  • Clicks on the account-opening call to action
  • Birth-year selection from the date picker

LinkedIn even recorded the user clicking “RECUSAR TODOS”, the reject-all button itself. The consent rejection was logged as a tracking event at the same moment it was being ignored.

The Portuguese consumer credit flow described earlier follows the same pattern. There, the user rejected all cookies, the Google Analytics request correctly carried gcs=G100, npa=1, and pscdl=denied, and Google Analytics still received the loan application URL and event names. Recording the consent state and then ignoring it in practice is arguably worse than not asking, because it produces a documented record of a choice the implementation did not honor.

The third pattern is subtler. On another Portuguese bank’s personal credit simulator, the user accepted only essential cookies. Clicking through to the next step opened an iframe hosted on a separate subdomain, and that iframe did not inherit the consent choice made on the main site. Once it loaded, Pinterest, LinkedIn, TikTok, Meta, and Google/DoubleClick all fired from inside it, receiving the credit simulation context:

  • Loan purpose
  • Loan amount of €27,000
  • Repayment term of 48 months
  • Total cost of €32,141.92
  • Monthly payment of €655.99

LinkedIn also captured the click on the data-processing consent label inside the form. A consent choice that is not carried across a site boundary is not really a consent choice at all.

Fingerprinting Beyond Marketing

Not every concerning behavior comes from an advertising or analytics tag. Our research also identified browser fingerprinting and device intelligence collection occurring through first-party scripts, including activity observed on the online banking site of a Dutch bank.

A first-party script collected detailed browser and device characteristics and transmitted the information back to the bank through tracking image requests (/qsl/trans.gif), while storing a browserInfo cookie. The collected data included:

  • User agent
  • Platform
  • Screen and window dimensions
  • Language settings
  • Installed plugins
  • Font and browser fingerprint attributes
  • Input field metadata

The script also probed localhost, issuing image requests to 127.0.0.1 on specific ports and measuring timing differences to infer whether particular local services were running. The observed ports included 7070 and 5938, associated with AnyDesk and TeamViewer, respectively. This behavior is consistent with fraud or remote-access detection rather than advertising, and there can be a legitimate security rationale for it. It is still highly invasive browser fingerprinting and local-port probing performed on the user’s machine, and it deserves the same scrutiny, disclosure, and consent treatment as any other runtime data collection. We are confirming the precise scope of this behavior under controlled testing.

The other direction was third-party adtech. On the site of a Portuguese bank, a fingerprinting script loaded before any cookie consent and sent a full device fingerprint to an advertising endpoint (tracking.adsafety.net), covering canvas, WebGL, fonts, audio, plugins, screen properties, timezone, hardware details, and more. Here there was no fraud-prevention rationale. A marketing fingerprint was being assembled before the user had agreed to anything.

Data Privacy and Regulatory Implications

The behaviours documented here create significant privacy and compliance risks under  GDPR and equivalent US privacy regimes. The consent failures specifically implicate the ePrivacy Directive, which requires prior consent before storing or reading information on a user’s device. The recurring triggers are consent failures, where tags fire before a choice or after a rejection, and the transmission of personal data, including a government identifier in the Portuguese account-opening case, to third parties that the reviewed policies did not clearly name as recipients. Where data, such as an email address appears in a request URL, it can persist well beyond the request itself, in browser histories, server logs, and intermediary systems, widening the exposure.

The financial sector also carries obligations that a general retailer does not. In the European Union, operational resilience and third-party risk rules under DORA arguably treat unmanaged third-party scripts in customer-facing flows as part of the institution’s risk surface.

The payments framework is tightening as well: PSD2 already governs how payment data is handled, and its successors, the Third Payment Services Directive and the Payment Services Regulation, reached final compromise texts in April 2026 and are moving toward adoption. In the United States, the Gramm-Leach-Bliley Act and its Safeguards Rule require financial institutions to protect customer information, and state laws such as California’s CCPA and CPRA give consumers rights over how their data is shared. Loan amounts, repayment terms, and account-opening details are exactly the kind of customer information these regimes expect banks to control end to end.

This problem rarely stays on one website. Many of the groups in scope do not operate a single brand. The large traditional banks increasingly run separate digital or neobank brands alongside their flagship sites, often sharing tag management, analytics, and personalization infrastructure across the group. A single permissive default, an unconsented pixel, a hashed identifier in a URL, or a personalization platform receiving raw email addresses does not stay contained to one domain. It tends to replicate across every consumer-facing property the group operates. A configuration problem on one site is rarely just one site’s problem.

data configuration

There is a competitive cost too, and it’s worse in finance than in retail. These tags are no longer just passive measurement tools. They are active collection systems that feed customer identity, financial intent, and product economics into the same global platforms that every other institution, including direct rivals, uses. Across this analysis, that data reached at least a dozen distinct third parties, including Google, Meta, and TikTok, as well as LinkedIn, Pinterest, Adobe, and Salesforce. Better aggregate data improves targeting, and better targeting tends to favor the players with the largest budgets.

Whose Responsibility Is It?

When the initial pixel research was published, Dark Reading wrote an article about it and asked Meta and TikTok to respond. Their answers are worth revisiting now, because our banking findings test the assumptions behind them. Meta characterized the report as self-promotional and said it misrepresented how the Meta Pixel works while ignoring the company’s privacy controls and its policy of prohibiting the sharing of sensitive data. TikTok took a narrower line, stating that advertisers decide what events and parameters they send, and that any data it receives is limited to what partners intentionally configure and send.

Both responses place the responsibility squarely on the website operator. That framing only holds if the collection is something the operator deliberately switched on, and much of what we observed is not. Features such as automatic advanced matching are enabled by default and are designed to capture and hash contact details with no explicit action from the site owner. A bank that drops in a standard pixel does not intentionally configure it to send a customer’s hashed email and phone number from a mortgage page, as we saw in the Spanish case above. In these situations, default platform behavior becomes a significant factor in what data is collected and transmitted.

Meta’s response also highlights a broader tension. A policy that prohibits the sharing of sensitive data is difficult to reconcile with a pixel that, left at its defaults, captures and transmits exactly that kind of data from account-opening and lending flows. If the policy genuinely prohibited it, the default would not collect it.

So the position remains the same as in our previous pixel research: responsibility is shared.  Financial institutions do have a responsibility to review the third-party technologies they deploy on sensitive customer journeys, understand what data is collected, and govern those integrations appropriately.

However, responsibility cannot rest solely with the website operator when the tools are designed to collect as much data as possible by default, relying on terms of service, implementation guidance, and limited guardrails to keep that collection defensible. Telling an institution that it should have configured a pixel differently, while shipping a pixel that defaults to excessive data collection, does not fully address the underlying issue. It shifts the burden of controlling complex browser-based data flows to the organizations and customers with the least visibility into how those flows operate.

Recommendations to mitigate runtime data risks

Managing this requires more than reading a vendor’s documentation. It means auditing what the scripts actually do at runtime, in real application flows, and watching for scope creep, where a tag begins collecting more than was intended.

Monitor runtime behavior: gain visibility into what each script accesses, what it collects, and how it transmits that data, with particular attention to account-opening, mortgage, loan, and simulation flows rather than just the homepage.

Enforce runtime controls: proactively restrict third-party access to sensitive fields and block unauthorized exfiltration, including data placed in request URLs.

Honor the consent choice in practice: ensure tags do not load before a choice is made, verify that a rejection actually stops data leaving the browser rather than merely recording a denied signal, and confirm that the choice is carried into iframes and subdomains rather than lost at the boundary.

Disable advanced matching and automatic collection where it is not justified: turn off automatic event capture and advanced matching features that send hashed or raw contact data unless that sharing is documented, disclosed, and lawful.

Treat first-party scripts with the same rigor: fingerprinting and port-probing logic, even when built for fraud prevention, should be inventoried, disclosed, and consent-aware.

It is the responsibility of every institution to ensure that the scripts running on its sites are configured to do only what they are permitted to do, and nothing more. On a banking site, the gap between those two things is not a measurement nuance. It is sensitive customer data leaving the organization through a channel most users never see.

The next post in this series turns from what banks are exposing to what that data is worth on the other side: how advertising platforms, data brokers, and competitors can use these signals to improve targeting, audience intelligence, and commercial decision-making. And what it means for the institutions providing them.

From Blobs to Blockchain: Inside the New Takedown-Resistant Skimmer Tricks

Article originally posted in May 2024.


Online tracking technologies include scripts or code embedded within websites or mobile applications to collect user data for analysis. Such technologies offer insight into user behavior, yet their use in healthcare raises concerns about the privacy and security of protected health information.

This article considers:

  • What is the HIPAA Privacy Rule?

  • What is the Background to the HIPAA Guidance?

  • What are the Key Milestones in the HIPAA Guidance?

    • Initial OCR Guidance (December 2022)

    • Joint OCR-FTC Letter (July 2023)

    • American Hospital Association Lawsuit (November 2023)

    • Updated OCR Guidance (March 2024)

  • Key HIPAA Acronyms Explained

  • How Did We Get Here?

  • Jscrambler Client-Side Protection Platform

What is the HIPAA Privacy Rule?

Health Insurance Portability and Accountability Act came into being in August 1996. Part of the Act required the Secretary of the U.S. Department of Health and Human Services (HHS) to publicize standards for the electronic exchange, privacy, and security of health information.

After drafting proposals and a series of public consultations, privacy regulations around individually identifiable health information (IIHI) were published in final form in August 2002. 

Summarizing for brevity, the HIPAA Privacy Rule applies to “covered entities”, namely health plans, healthcare clearinghouses, and any healthcare provider, who transmits health information in electronic form under HIPPA (Health Insurance Portability and Accountability Act). 

The Privacy Rule protects all individually identifiable health information held or transmitted by a covered entity or its business associate, in any form or media. This is generally information about an individual’s past, present, or future health, the provision of or payment for healthcare. This information is known as protected health information (PHI) within the scope of the HIPAA Privacy Rule.

What is the Background to the HIPAA Guidance?

The US Department of Health and Human Services (HHS) is made up of a family of agencies, one of which is the Office for Civil Rights (OCR). Among other things, this agency ensures that individuals receiving services from HHS-conducted or -funded programs can trust the privacy and security of their health information.

In recent years, the OCR has issued and re-issued guidance around the use of online tracking technologies in the context of HIPAA. It has worked together with the Federal Trade Commission, the consumer-protection regulator to issue warning letters and see the legality of its guidance challenged.

Initial OCR Guidance (December 2022)

In December 2022, the OCR issued initial guidance on online tracking technologies under HIPAA Rules. In essence, the guidance confirmed that using third-party tracking technologies on websites, web, and mobile apps without a business associate agreement (BAA) in place was a HIPAA violation if the tracking technology collected and transmitted IIHI. Even with a BAA in pace, the use of such technologies may still violate HIPAA Rules.

The impetus for the December 2022 guidance was research conducted with the top 100 hospitals in the US. It showed that one-third had tracking codes on their websites, which transmitted user data to Meta (Facebook), Google, and others. In seven cases, the code had been added to password-protected patient portals. This made it likely that many more hospitals outside the sample had also transferred sensitive data without a BAA in place and without obtaining patient consent.

Joint OCR-FTC Letter (July 2023)

In July 2023, the OCR and Federal Trade Commission (FTC) sent a joint letter to around 130 hospitals and telehealth providers, warning them about the privacy and security risks of online tracking technologies. For example, if the information disclosed reveals health conditions, diagnoses, medications, medical treatments, frequency of visits to healthcare professionals, and so on. It referred to recent enforcement actions against digital healthcare platforms.

American Hospital Association Lawsuit (November 2023)

In November 2023, the American Hospital Association filed a lawsuit against the HHS and OCR over its December 2022 guidance on website tracking technologies. 

Such technologies are critical for analytics software, video technologies, translation and accessibility services, digital maps, etc on websites and apps. 

The American Hospital Association alleged “gross overreach by federal bureaucracy” and that the HHS rule “exceeds the government’s statutory and constitutional authority” and “harms the very people it purports to protect.”

Updated OCR Guidance (March 2024)

In March 2024, the OCR issued updated guidance on online tracking technologies under HIPAA Rules. This included additional examples of when tracking code can and cannot be used, tips for complying with HIPAA, and enforcement priorities.

In short, the guidance remains that regulated entities are not permitted to use tracking technologies in a manner that would result in impermissible disclosures of PHI or any other violations of the HIPAA Rules. For example, disclosures of PHI to tracking technology vendors for marketing purposes, without individuals’ HIPAA-compliant authorizations, would constitute impermissible disclosures.

The OCR provides further clarification on different types of tracking technologies and how they work. It explains the difference between tracking on webpages, where login may or may not be required, and within mobile apps. 

For some web pages, the nature of the visit determines whether HIPAA applies. The guidance gives examples of a student researching a paper about oncology services and a patient seeking a second opinion about a brain tumor.

For the former, collecting and transmitting the student’s IP address and other personally identifiable information to third parties without a BAA is not a HIPAA violation, as no PHI is involved. For the latter, transmitting the same data without a BAA would be a HIPAA violation, as the information would be classified as PHI.

How Did We Get Here?

The Internet was designed for sharing and collaboration not necessarily telemedicine, banking, and shopping. How web applications are built has also changed over time. The business intelligence has moved from web servers, owned and managed by companies, into the consumer web browser, powered by JavaScript, distributed APIs, and microservices.

As a result, any JavaScript running on a web page can access all data entered into form fields on that page. With no separation between different parts of the application, this makes healthcare data susceptible to HIPAA violations and/or client-side attacks. This is where criminals exploit vulnerabilities in a website’s code or infrastructure to harvest data. This goes by many names, including e-skimming, digital skimming, data skimming, and formjacking.

Jscrambler Client-Side Protection Platform

The Jscrambler Client-Side Protection Platform safeguards first-party JavaScript through state-of-the-art obfuscation and exclusive runtime protection.

Its fine-grained JavaScript behavioral analysis also mitigates threats and risks posed by third-party tags. It also complies with the new PCI DSS v4 standard for card data security. With Jscrambler, businesses adopt a unified, future-proof client-side security policy all while achieving compliance with emerging security standards.

Trusted by digital leaders from several industries, including healthcare, financial, and entertainment, Jscrambler gives businesses the freedom to innovate securely.

Feel free to connect with our client-side security experts to try our solutions to prevent digital skimming attacks.

Payment Iframes Now Proven Susceptible to Silent Skimming

Attackers are constantly finding new ways to evade defenders’ protections. Whether it’s through obfuscating their code, compromising plugins, poisoning content delivery networks (CDNs), or through supply chain compromises, these attacks continually plague e-commerce websites.

Now, there’s a new twist: the latest Magecart-style campaign, analyzed by our researchers, was discovered to be stashing its payload inside blockchain smart contracts and using novel ways to bypass Content Security Policy (CSP). Jscrambler’s security research team has been tracking this attack since February 2025. Below, we detail some of the latest methods used by attackers, along with guidance on how to protect your business.

Discovery

Jscrambler tracks numerous skimming campaigns across the Internet as part of our ongoing effort to keep the Magecart classification in Webpage Integrity current. Identifying and monitoring this campaign all began with this post on X:


threatcat-malicious-code-blockchain

A few months later, a related post appeared:


magecart-attacks-jscrambler-blog

Clicking on the link, the search string used by the researcher stood out to us:

"URL['revo'+'keObj'+'ectU'+'RL']"

This was a simple obfuscation of revokeObjectURL(), a JavaScript function that the attackers were chopping up in this manner to try to hide their tracks. We searched for sites containing this specific string and quickly found dozens of them. Upon closer examination, they all contained web skimmers designed to siphon off cardholder data. Nothing new has been observed so far; however, as Jscrambler’s research team continued to examine the attack code, they discovered a couple of new techniques that had not been observed before.

First, one of the pieces of the attack was stored in a blockchain smart contract, and second, it employed some interesting techniques using “blobs” that allowed the attack code to evade most CSP configurations currently in use.

How does it work?


As with most campaigns these days, the attack operates in several stages and appears to be a toolkit, allowing attackers to tailor the attack to the specific website they’ve compromised. The first stage, the initial loader, is placed on the compromised website by the attacker. While we don’t know exactly how the attackers carried out the initial compromise, it’s often due to unpatched vulnerabilities or misconfigurations, such as default/weak passwords on the website or server.

Attackers use multiple stages for several reasons. First, they don’t know how long they’ll have access to the compromised host, and may not have time to tailor an attack script before they lose access. By linking out to a third-party website over which they have complete control, they can gain a foothold and tailor their malware later. Second, adding a small loader script rather than a full attack script makes it less likely that someone casually reviewing the code will detect that something unusual is going on. The loader is often made to appear innocuous, for example, by referencing well-known services like Google Tag Manager.

Let’s delve into one of the infections we’ve seen now.

Stage 1: Loader

We noticed several variations to the first-stage loader, depending on exactly what technology stack the victim website was using. As we examined these different loaders, we discovered some interesting elements to the first stage of this attack for some victims:

First, the tiny bootstrap snippet is lightly obfuscated. In some instances with a twist – instead of using the usual MIME type of “text/javascript”, which tells the web browser that the snippet is JavaScript code and to execute it, this loader instead uses a fake MIME type (rocketlazyloadscript), which is later rewritten into active JavaScript on the client-side by a legitimate WordPress plugin, WP Rocket. 

This technique of changing the MIME type is used to attempt to conceal JavaScript code from static analysis tools that scan websites for it. It relies on a legitimate behaviour in the WP Rocket plugin, which is meant to optimise page load times by delaying scripts from loading until after a page has been rendered in the web browser.


stage-1-loader-jscrambler-research

Second, this code unusually relies on JavaScript “blobs” (Binary Large OBject) – ephemeral, in-memory JavaScript objects that exist only as long as the page is loaded. Usually, these objects are legitimately used for generating dynamic content, such as loading images or video without writing them to disk, but in this case, the attackers are using them to keep the attack code out of the webpage’s DOM. To remove the blobs from the DOM, the attackers use URL.createObjectURL() to generate the blobs. Once this loader is executed, the attackers then call URL.revokeObjectURL(), which wipes any reference to the loader from the DOM.

Another aspect of blobs is that browsers typically do not display them when you open the JavaScript debugger; all you see is the ID, not the code it represents. While there are, of course, ways to view the code, it’s another barrier that attackers used here to make it a little harder to analyze, and also make it that little bit harder for anyone reviewing the code to figure out what’s going on, too.

We haven’t seen blobs used as an evasion technique in Magecart attacks before, and most Content Security Policies aren’t configured to prevent blobs from being loaded like this.


Stage 2: Smart Contract

This is another fascinating and novel aspect of the campaign: the attackers utilize blockchain smart contracts as a storage layer for their malware. The term “Etherhiding” has been used to describe storing malware on the blockchain; however, this is the first time we’ve seen a blockchain smart contract utilized in a Magecart attack.


This approach gives attackers an advantage – rather than hosting the skimmer on infrastructure that defenders could report and take down, instead, the second-stage queries a Binance Smart Chain (BSC) testnet contract to retrieve its payload. Because the blockchain is decentralised and immutable, defenders can’t simply “take down” the source like they would with a compromised server. As long as the contract exists on the testnet, the malicious code remains publicly accessible and resilient against traditional disruption. It also means that attackers can update the smart contract at any time, and this new code will be loaded the next time a victim visits a compromised website.

From-Blobs-to-Blockchain-Inside-the-New-Takedown-Resistant Skimmer-Tricks

To hide the attack code in a smart contract, the data is encoded, and the loader decodes it and stashes it locally, in localStorage, and appends _gtm to the key to make it look at first glance like it’s a Google Tag Manager-related key. Finally, this stage reassembles the data into another blob for execution.

attack-code-smart-contract

As mentioned, this is a new development in web skimming. By hiding behind the persistence of a smart contract, attackers gain a level of censorship resistance and infrastructure redundancy that has not been seen before. Updating the skimmer is as simple as sending a new transaction to the contract, and there’s almost nothing that defenders can do to erase the code once it’s on-chain. This means that client-side security becomes even more critical because the delivery vector itself may be untouchable.

Stage 3: Websocket to communicate with the attacker’s C2 infrastructure

The final stage of the attack is, again, a modern approach, but is more in keeping with what we usually see in this type of attack. The third-stage code connects to a command-and-control (C2) server via WebSockets. This provides attackers with a live communication channel through each victim’s web browser. Once the smart contract payload is decoded and executed, the script opens a connection to domains like babymarket.io, webawast.xyz, kefersuc.xyz or kezopersuc.xyz, where it registers the infected session with a unique identifier.

The attackers insert their skimming attack here – either by hiding the original payment iframe and replacing it with their own, or by inserting their own iframe that mimics the PSP for merchants utilizing a full redirect to the PSP. Again, though, the attackers go beyond the run-of-the-mill attacks, as in some cases they even display what appear to be fake 3DS and SMS prompts here too.


fake-3ds-form
Fake 3DS Form

Interestingly, we observed the use of both silent skimming attacks and double-entry attacks in this campaign, which depended on the technology stack and PSP used by the compromised merchant website. For double-entry skimming attacks, the original payment iframe would be shown after the first “failed” transaction, where the attackers captured the payment details. Still, in the silent skimming attacks we saw both payment and customer cookie data being transmitted to the attacker’s C2 infrastructure, immediately followed by a transaction attempt on the card, suggesting that the attackers were proxying the legitimate transaction.

fake-payment-form-example-skimming-attack-bbvaFake Payment Form

legitimate-payment-form-BBVA-jscrambler-blogLegitimate Payment Form

In this instance, the skimmer expects to find Stripe-related elements to replace, but we found several other payment service providers mentioned in other skimmer instances:

  • Bancard Payments

  • Bykea

  • CreditGUARD

  • Mercado Pago

  • Midtrans

  • Mollie

  • Monetico

  • Montonio

  • Payfast

  • Paypal

  • PayPlug 

  • PayU

  • PhonePe

  • Pikpay

  • Razorpay

  • RedSys

  • Viva.com

  • Wompi

Using WebSockets instead of traditional HTTP beacons gives the attackers two significant advantages. First, it enables bi-directional communication, allowing the C2 to push fresh JavaScript payloads in real-time, rather than relying on static scripts. In fact, we discovered at least 37 different payload configurations being sent, depending on the origin of the request, the path, and other identifiers being sent from each site. Second, using web sockets means the traffic appears more like legitimate session traffic, which makes it easier to blend in with chat widgets, analytics, and other modern web features.

In an attempt to obfuscate what their attack script is doing, the attackers also include references to some legitimate URLs from several well-known PSPs within their script, again so that a casual reviewer might assume that this script is somehow involved in the legitimate payment flow.

attempt-to-obfuscate-attack-scripts

An interesting benefit of attackers using a smart contract in the previous stage is that, if the attacker’s C2 servers are disrupted, they only need to update the smart contract to point to their new C2 systems, allowing the attack to continue.
 

Smart Contract Updates


During our research, we also discovered infrastructure for managing smart contact updates. One was in use by this campaign and showed several updates, and another that appeared to be related, pointing to the same wallet, but unused at present.

original-smart-contract-data-management-pageOriginal Smart Contract Data Management Page


translated-smart-contract-data-management-pageTranslated Smart Contract Data Management Page

The page source code showed the mechanisms for updating the smart contracts. It relies on having the MetaMask extension installed on the web browser, which holds the attacker’s private keys, and without which it’s not possible to update the smart contract. However, by following the wallet address embedded in the page source code, we were able to discover additional C2 hosts used by the attackers, listed at the end of this post.

Impact

Jscrambler’s security researchers discovered over 110 websites compromised to date, with the number of compromised sites increasing, and it is likely an underestimate, as many signs of compromise might only be visible to logged-in customers of these sites. 

What can be done?


Once again, we see that using CSP and SRI alone is unlikely to prevent this type of attack on its own. Most of the victim websites had some CSP headers, but they were ineffective in preventing these attacks. Where entities use client-side protection technology, such as Jscrambler’s Webpage Integrity, this type of attack would be detected by the out-of-the-box configuration. This is because Webpage Integrity monitors scripts’ behaviors, and so the moment a blob or data source is accessed, it is automatically detected.

Compare this to what’s necessary on an ongoing basis for a CSP/SRI configuration:

  • Ensure that your Content Security Policy (CSP) is as restrictive as possible, and explicitly block blob: and data: sources in script-src.

  • Ensure that your CSPs’ connect-src parameter only includes required, known domains to detect rogue WebSocket connections.

  • Actively monitor and investigate CSP violations, and avoid CSP wildcards wherever possible, especially when referencing CDNs.

  • Ensure that security-impacting HTTP headers are being monitored, that alerts are received, and that these alerts are investigated promptly.

We also noticed that many of the affected merchants in this campaign were utilizing a full redirect to their PSP, but the attackers subverted that mechanism. PCI DSS doesn’t include any controls to monitor these full redirect mechanisms, which we’re now seeing attackers exploiting. Merchants, especially those using SAQ A, would be wise to proactively monitor their payment pages, even where a full redirect is used, to ensure that attacks like those described here are detected early.

Conclusion

This attack combines modern and new techniques to create a multi-faceted and hard-to-detect attack. Attackers continually find new ways to keep their exploit code available online, and the use of Blockchain smart contracts is a novel new approach that makes the attack code essentially impossible to take down.


Equally, attackers continue to obfuscate their code, such as making it appear to be part of Google Tag Manager or Stripe, in this case, and they continue to exploit legitimate browser features to make their attacks harder to detect.

Jscrambler also continues to observe that CSP and SRI are misconfigured or too lax on breached websites, and these technologies are simply insufficient on their own to prevent modern attacks. This is especially true when attackers exploit features like blob: URLs to circumvent more common detection mechanisms. Defenders need to use a layered approach, blending in client-side security to gain insights into the behaviour of all scripts across their websites.

Indicators of Compromise (IOCs):
Smart Contract

Contract

Creation Date

Contract Creator

0xb9a76d21df3c71209c42e601402709da650def7f

Jul-12-2025 10:01:48 PM UTC

0xD016cA1e8Ec9FA90AB11498e98D5c0a7C6C85A04

0x41cab44cacded0af59e08d187efd8a3cbf2bd18a

Oct-15-2024 02:25:36 PM UTC

0x5178a932D5b312801e02c43FD50399a88028b9D0

0x0967296defa0fd586c9ede5730380e2b059fab95

Nov-08-2024 09:48:57 AM UTC

0x5178a932D5b312801e02c43FD50399a88028b9D0

0xdde0e8f536abc0df8082b5df880e03d98180751a

Feb-13-2025 09:50:35 AM UTC

0x62036ed878271b0c2E820c1AbF533d91DfF4448f

0x3596a5d8fdd13763482de91a4ca74b7dbcbd98f9

Jan-12-2025 09:19:23 AM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0xa3a94b59178a4b32a753be258892cc9c5b57c40e

Aug-13-2025 01:31:53 PM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0x885685dd99553c0c441ee37ba6ac93d21549b755

Aug-13-2025 01:32:14 PM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0x0301ad41dbcbbeec4c03375ad136d69770fc6a81

Aug-13-2025 01:58:53 PM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0x26b15226887d8afe94671f0551eea3ab16873be6

Aug-13-2025 01:59:14 PM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0xc9233895f25b1135bd0bd352b4b97a7c7210f33b

Apr-02-2025 03:49:55 PM UTC

0xdec39Acd463C68d8CF4086bcA538FEB44e21AA5B

0xb5f454d3102f90c876e42bf8d077a16d24ba9d67

Aug-13-2025 11:30:28 PM UTC

0xb6B9D77b0723eaf356434C24EC5F79E25FC2a917

0x94d2c03f67790fbdeb875938433db0175b340717

May-22-2025 09:34:23 AM UTC

0x8d27f28819082AdB36850B1dcafB4F032154D9FA

0x1c13cf158da35a385391db798752119a301d85b4

Sep-02-2024 01:41:28 PM UTC

0x5178a932D5b312801e02c43FD50399a88028b9D0

0xf62e8c14b894a8be0cc9501844f2c4b46140565c

Jan-09-2025 07:17:05 AM UTC

0xc2F695613de0885dA3bdd18E8c317B9fAf7d4eba

0xc19f7400850203f14014236dfab18b0821bfbf08

Jan-09-2025 09:53:11 AM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0xdcd2aa8c6b34f940c073640ed8681d0505fbb1cb

Jan-09-2025 11:29:14 AM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0xc1b04be318fd252364cd247c2d544ecab3ebb6ef

Feb-11-2025 02:44:55 PM UTC

0xf62E8c14b894a8be0cC9501844f2C4B46140565C

0xe8aa199d3fbd8570b7b56b4e1bc9e384a58b2af6

Sep-12-2025 07:29:22 AM UTC

0xB32E5C1470b7CAc7e5D7f86C351B1e1765190936

0xfcb633fccea898c23e4a89a8f83f23941e75e034

Oct-03-2025 09:08:18 AM UTC

0xd14404519f9FD4113206f766c2992fD8AE7ECc10

0x574c15b6706317011fc7c9632b4b974c6d61a108

Oct-05-2025 02:11:45 PM UTC

0xd14404519f9FD4113206f766c2992fD8AE7ECc10

Indicators of Compromise (IOCs):
Domains

Domain

Registered On

kezopersuc[.]xyz

2025-03-28

kefersuc[.]xyz

2025-03-07

keritysuc[.]xyz

2025-03-07

suckerity[.]xyz

2024-11-06

webawast[.]xyz

2025-09-08

babymarket[.]io

2025-05-08

wordpress-login[.]com

2025-02-11

neshion[.]com

2024-11-08

gagichls[.]top

2025-08-05

test.gagichls[.]top

2025-08-05

wsocket[.]store

2025-04-02

woscket[.]store

2025-09-12

elementatorprof[.]online

2025-04-30

asd123qwe2[.]online

2024-12-07

inspectlet[.]observer

2025-08-21

websocket[.]click

2025-03-06

insightanalytics[.]pro

2025-05-22

wordpress-commerce[.]com

2025-08-13

cdn[.]cdnjscookies[.]top

2025-01-12

cn[.]ls1ks[.]xyz

2024-02-07

cdn[.]iconstaff[.]top

2024-02-28

gigacgetski[.]top

2025-03-08

wooadminpro[.]com

2025-09-16

References

The Hidden Cost of Convenience: Is Public Wi-Fi Putting Your Private Data at Risk?

Recently, Turaco Labs detailed a silent skimming attack that used a proxy to silently skim payment card data from Stripe’s iframe solution. Theirs is a great analysis of the attack, and I’d encourage you to read it.

The tl;dr is that the attackers:

  1. Compromised the merchant’s website.

  2. Replaced the call to load stripe.js from stripe.com to instead load stripe.js from the attacker’s domain.

  3. Hosted a version of stripe.js on their domain that created the iframes in the consumer browser as normal, but also loaded a modified version of Stripe’s code inside the iframe to capture the payment card data, which posted the data to the attacker’s API instead of stripe.com.

  4. When the attacker’s API received the payment card data, it silently took a copy and then posted the data to Stripe’s API, forging the headers so it looked to have come from an iframe created by Stripe’s original JavaScript.

Although this class of attack has been threat modelled and talked about in PCI-circles this is the first time it’s been reported in the wild, and it lays bare the conceit that just using an iframe to collect payment data isolates the merchant’s website from attacks.

The benefit of using an iframe from an e-commerce payment service provider / payment gateway / payment processor (referred to herein as an ECPG) is that it mostly isolates the merchant’s web server and allows the merchant to use SAQ A to validate their compliance with PCI DSS. We say “mostly isolates” because there are always attacks against an iframe.

Watch the on-demand webinar: Iframe Integrity: Empowering PSPs to Secure Payment Iframes Against Skimming Attacks with Zero Hassle

Double-entry skimming attacks

The attacker can compromise the merchant’s server or any third-party JavaScript present on the page to put up a fake payment page, which accepts the consumer’s cardholder data, only for it to steal the data and display “an error has occurred” message. The consumer is then invited to enter their data again (hence double-entry), but this time into the real payment fields from the ECPG, and the transaction happens normally. Double-entry attacks are generally detected because they interfere with the merchant’s desired payment flow.

Silent skimming attacks

In a silent skimming attack, the attacker’s aim is to silently take a copy of the cardholder data without interfering with the payment flow. Typically, this is done by tampering with the ECPG’s iframe or by overlaying a fake iframe above the real one, but then posting the data to the payment provider’s API endpoint. 

These types of skimming attacks can be detected and prevented by the ECPG — although our experience is that not all do a very good job.

This proxy attack is a new form of silent skimming. And although technically we agree with Turaco Lab’s observation that:

“This is not a bug in Stripe; it’s the consequence of local code tampering on the merchant’s site, plus a high-fidelity relay that keeps the rest of the contract intact.”


Our view is that preventing this attack is Stripe’s responsibility.

Sure, there isn’t a bug in Stripe’s code.

However, Stripe could have deployed measures such as code hardening and anti-tampering techniques to the code that created the iframe, as well as to their code that runs inside the iframe. They could have threat-modeled this type of attack and implemented some defences to ensure that cardholder data only came from their code, with an attestation that it was loaded from their domains by their JavaScript. Security techniques, along with code hardening and anti-tampering, could have defeated this type of attack.

The Emperor’s New Clothes Problem with PCI DSS SAQ A

Given that there are attacker techniques that can silently skim cardholder data from ECPG iframe solutions, you’d assume that a merchant’s compliance with PCI DSS would be designed to prevent such attacks. That’s not a safe assumption.

Merchants who use an iframe solution from an ECPG can validate their compliance using SAQ A, as all “account data functions completely outsourced to PCI DSS validated and compliant third parties”. 

However, for e-commerce merchants, the PCI SSC implicitly acknowledges that the security of the merchant’s web server that generates the page that includes the ECPG’s iframes could affect the security of cardholder data. So SAQ A contains some minimal controls/requirements relating to the merchant web server, such as default account management, patching, access controls, and vulnerability scanning. The assumption is that the attack surface is minimal and the ECPG’s solution sufficiently isolates the merchant’s web server.

When PCI DSS 4.0 was released, SAQ A also included two new requirements applicable to e-commerce merchants, designed to counter these types of skimming attacks: requirements 6.4.3 and 11.6.1

However, before these requirements became mandatory, for some reason best known to the PCI SSC, they were removed from SAQ A and replaced with a new eligibility criterion that the merchant had to fulfil to remain eligible to use SAQ A (or, following FAQ 1331, base their QSA assessment on just the requirements contained in SAQ A):

“The merchant has confirmed that their site is not susceptible to attacks from scripts that could affect the merchant’s e-commerce system(s).”

Further guidance was provided in FAQ 1588 as to how a merchant would be able to provide such a confirmation. These are:

“Using techniques such as, but not limited to, those detailed in PCI DSS Requirements 6.4.3 and 11.6.1 to protect the merchant’s webpage from scripts targeting account data. These techniques may be deployed by the merchant or a third party.

Or

Obtaining confirmation from the merchant’s PCI DSS compliant TPSP/payment processor providing the embedded payment page/form(s) that, when implemented according to the TPSP’s/payment processor’s instructions, the TPSP’s/payment processor’s solution includes techniques that protect the merchant’s payment page from script attacks.”


Most merchants take the second approach and rely on the confirmation (or the implied confirmation) from their ECPG. However, in the case of this attack against Stripe’s iframe implementation and, in our experience, many others’, this confirmation is hollow.

  • Some ECPGs suggest their compliance with PCI DSS meets this criterion, but PCI DSS doesn’t include any requirements that would support this assertion.

  • Many ECPGs have no protection against iframe tampering and overlay attacks, even though there are some simple things they can do.

  • We are aware of only a limited number of ECPGs that have implemented techniques such as code hardening and tamper detection, which would protect against this proxy attack.

We’re constantly surprised that merchants are receiving such confirmation of protection and how they, in turn, are able to meet the SAQ A eligibility criteria when their site is unfortunately susceptible to attacks from scripts that could affect the merchant’s e-commerce system.

It is fair to observe that our surprise is amplified because our core products, Code Integrity, Iframe Integrity, and Webpage Integrity, would stop these attacks. However, the JavaScript and engineering principles in our products are not secrets; ECPGs could conduct their own threat modeling and develop their own defences and techniques: they just haven’t.


Fixing the Problem

Small e-commerce merchants without their own cybersecurity teams should be able to accept payment card transactions safely: they should be able to buy solutions that allow them to safely accept card-based payment transactions online.

Using an iframe from a validated PCI DSS compliant ECPG should be the answer. However, currently it isn’t the answer because there’s no assurance that the ECPG’s solutions are robust against known types of criminal attacks, let alone new ones that may have been threat-modeled but not yet seen in the wild.

What’s required is that these ECPG iframe solutions should be separately and independently evaluated to validate that they are not susceptible to known and threat-modeled script attacks. 

Such a validation should test for code hardening, tamper resistance, and incorporate some form of attestation-and-monitoring between the ECPG’s code running in the parent page, in the iframe, and the ECPG’s back end. As this proxy attack demonstrated, it’s no use just relying on the server checking origins and referers.

Today, customer-present merchants can select validated payment solutions via approved P2PE solutions (and for mobile acceptance, MPOC solutions), which minimise the risk of a compromise of cardholder data.

There has to be the same for e-commerce:  a PCI SSC validation and listing program for e-commerce payment gateway solutions so merchants could attest that their payment processing was wholly outsourced, and that their site wasn’t susceptible to script attacks.

This program could take advantage of the current PCI Secure Software Standard. 

All it would take is for the brands to clarify that an ECPG solution consisting of the:

  • JavaScript in the parent page that creates the payment page

  • JavaScript in the iframe, which is the payment page

  • Back-end code for the API that accepts the payment card data 

Together are “Payment Software” which, according to brand rules, must comply with the Secure Software Standard, be validated by an independent PCI SSC Secure Software Standard accredited assessor, and be listed on the PCI SSC’s website.

Ideally, the eligibility criteria for SAQ A for e-commerce would be that the software provided by the ECPG that the merchant embeds in their website is validated to the Secure Software Standard and is listed on the PCI SSC website as such. 

Until that time most merchants who think they qualify for SAQ A should really take a look at SAQ A-EP because until the SSC and the industry catches up with how criminals attack iframes, no merchant using the majority of ECPG’s iframe solutions can honestly attest that their site is not susceptible to attacks from scripts that could affect the merchant’s e-commerce system(s). 

Is this really a problem?

Ideally, there would be no compromises of payment card data when such compromised data can still be used to make a fraudulent transaction. Eliminating all compromises and fraud isn’t the goal — it’s reducing the volume to one that maintains the integrity of the payment ecosystem.

So, if a few small SAQ A eligible merchants have compromises where criminals obtain small volumes of payment card data, is this a problem? Should the ecosystem worry about the conceit that an iframe and SAQ A adequately protect cardholder data?

It won’t be a surprise that our view is that the industry should care about this, for two reasons:

  1. Once perfected, these are systemic attacks; it isn’t one small merchant that’s affected, but multiple merchants using the same ECPG or system.

  2. Large level 1 and level 2 merchants continue to utilize the provisions of FAQ 1331 to limit their QSA assessment to just the requirements in SAQ A. Some such merchants deploy technical solutions like Jscrambler’s Webpage Integrity or our competitors’ solutions to minimize the susceptibility of their e-commerce systems to script attacks. But many do not, relying on implied assurances from their ECPG or an optimistic assumption.

Until there’s an independent evaluation and validation program for ECPG solutions that matches what is available with P2PE for face-to-face solutions and MPoC for mobile, the online retailer isn’t being helped by the ecosystem. It’s time that changed.

The npm chalk and debug attack proves again: the web’s trust model is broken

On September 8th, 2025, Aikido reported a major supply chain attack affecting dozens of npm packages, including the hugely popular chalk and debug. In total, the compromised packages represent 2.6 billion weekly downloads across the ecosystem.


The malicious updates, injected after the attacker compromised the npm account of a well-known maintainer, targeted MetaMask wallets, attempting to intercept and hijack crypto transactions.

As shocking as the scale of this incident is, it highlights once again a problem we already know too well: the web’s trust model is broken.

The web rose to dominance because it is simple, portable, and composable. In software, composability means building larger systems by combining smaller, reusable components — very often written and maintained by someone else, such as maintainers, open-source contributors, or third-party vendors.

On the web, this idea is pushed to the extreme: modern apps may depend on hundreds of external libraries, each one a building block you didn’t have to write yourself. This is why we can develop applications in record time — but it also comes with a price. Every new dependency you add is a potential new attack vector.

In this post, we’ll unpack what this incident tells us about the web’s trust model, why the Client-Side remains the ultimate target, and how organizations can move from crossing their fingers to actually defending against these inevitable compromises.

The Web Trust Model is Broken


The strength of the web has always been its composability. Instead of reinventing the wheel, developers can stitch together countless libraries and frameworks to deliver applications faster than ever. But composability comes at a price: in most modern applications, the majority of the code is not written by you. It comes from strangers — maintainers you’ve never met, teams you don’t control, and code paths you probably never review in detail.

In practice, many developers place blind trust in third-party packages. They focus on securing the code they authored while assuming dependencies “just work.” From an application security perspective, this mindset is dangerous. A vulnerability or backdoor can hide anywhere in the codebase, and in today’s web, it’s far more likely to be in a dependency than in your own logic.

This isn’t theory; history has shown us what happens when that trust is misplaced:

  • In 2018, the event-stream compromise introduced malicious code through a maintainer handover, planting a backdoor deep inside countless projects.

  • In 2021, attackers hijacked the UAParser.js npm account, publishing versions that installed password stealers and cryptominers on developer machines.

  • In 2022, the maintainer of colors and faker deliberately sabotaged his own packages, breaking thousands of projects overnight and exposing the fragility of relying on community goodwill.

  • And most recently, the XZ Utils compromise (2024) showed us something even more unsettling: that attackers can patiently embed themselves as trusted maintainers for years, only to eventually deliver a backdoored release. It’s an extreme case, but it proves that even the people we trust most can become part of the attack surface.

The chalk and debug incident is just the latest reminder that blind trust is no security strategy. Supply-chain compromises are no longer rare exceptions — they are part of the threat landscape. If your defenses assume that upstream code will always remain trustworthy, you’re already playing a losing game.

Highlights From the Attack


As a company specializing in JavaScript security, we were eager to analyze the malicious payload in detail.

From deobfuscating the code, we found Aikido findings to be accurate: the payload was designed to run in the browser, intercept network and wallet calls, actively tampered with MetaMask wallet transactions, and replace legitimate recipient addresses with attacker-controlled ones.

We respectfully disagree with JD Staerk’s characterization of the code as ‘heavily obfuscated’.  Our analysis found the obfuscation was superficial. Yes — the code looks messy at first glance, but it is not resilient to reverse engineering and analysis. With modest automation and manual inspection, the core objects and behaviors can be exposed quickly. It appears the attacker aimed to slow down casual reviewers, not to withstand determined reverse engineering.

Example (Figure 1) — you can see how an obfuscated control object is trivially mapped back to readable identifiers:

Obfuscated-left-recovered-representation-rightFigure 1 — Obfuscated (left) vs. recovered representation (right)

For a full walkthrough of the malicious code’s execution flow, we recommend JD Staerk’s post, which provides a detailed step-by-step analysis. A few implementation details are especially noteworthy because they show the attacker’s intent and the reasons the attack was discovered quickly.

1) XHR monkey-patching (response rewriting)

The payload replaces XMLHttpRequest.prototype.send so it can inspect responses, parse JSON responses when appropriate, run a replaceAddress step, and then overwrite xhr.responseText/xhr.response with a tampered version.

This technique is broad and noisy: it will attempt to replace any string that looks like a wallet address in any response. Because it operates on a wide surface area (any XHR response), it increases the chance of detection and false positives, which likely contributed to the rapid discovery of the compromise.

Monkey-patched-replaces-wallet-addressFigure 2 – Monkey-patched XHR.prototype.send that replaces wallet address


2) Transaction Interception (wallet API hooking)

For active wallet manipulations, the payload wraps wallet RPC methods. The intercept wrapper inspects arguments, identifies methods like eth_sendTransaction or Solana signing methods, rewrites transaction objects via a txRewriter, and then forwards the call to the original method:

Transaction-interception-in-memory-tamperingFigure 3 — Transaction interception and in-memory tampering


Because the payload hooks into the wallet signing flow, the attacker can change the transaction recipient just before it reaches the wallet for user confirmation — the precise moment where human attention is required, and therefore the ideal point to hijack funds if users don’t inspect the destination carefully.

3) Levenshtein-based address selection

Rather than blindly choosing any attacker address, the code uses a Levenshtein distance function to pick the attacker address that is visually most similar to the original recipient. This reduces the chance a human will notice the swap — whether skimming a UI or reviewing on a hardware wallet — a clever trick to fool the eye.

levenshtein-distance-algorithm

Figure 4 – Using levenshtein distance algorithm to pick the most visually similar  attacker address

4) The curious fetch error – exposing the attack

Ironically, one of the first public signals was a build error:

ReferenceError: fetch is not defined


The attacker used fetch calls assuming the runtime provided it. On older Node.js environments (where fetch is not global), this caused CI/CD builds to fail — a lucky fail for defenders. In modern Node versions where fetch exists, that same call would have silently executed, making detection harder. This reveals a tradeoff: assumptions that make code run broadly can also make it stealthier.

It makes us wonder: the threat actor used several clever tricks, but didn’t think to include a fetch polyfill.

Was the Attack Successful?

From a technical perspective, the attack seems to have been successful. We observed on-chain transfers to attacker-controlled addresses within the first 24 hours after the compromise. The following is a snapshot of those addresses and the balances transferred (net worths are shown as of publication):

From a technical perspective, the attack was successful. We were able to confirm that the following wallets and amounts were moved to some of the attacker’s wallets, all within one day.

Wallet

Portfolio Net Worth *

0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976

$923.15

0xa4134741a64F882c751110D3E207C51d38f6c756

$1.32

0x93Ff376B931B92aF91241aAf257d708B62D62F4C

$0.44

Total

$924.91

* (Net worths captured at time of publishing; on-chain activity may continue — link these addresses to Blockscan for live updates.)



Viewed economically, the haul is surprisingly small — under $1,000 in on-chain receipts. That amount is very modest compared with what a functioning, widely-deployed clipper could have achieved, and far below what access to compromised, high-usage packages would fetch on illicit markets. In other words: this was not a dry-run — the attacker clearly intended to profit — but they misjudged either their scale, their opsec, or the payload’s stealth. A more sophisticated operator could have sold or leased control of these packages to third parties for a significantly higher return, or profited far more by running quieter, long-running exfiltration campaigns.

Why the Attack Wasn’t Worse

The attacker reused the same injected payload across all compromised packages. This made detection easier, as security vendors could quickly roll out signatures.

A more sophisticated attacker could have randomized the payloads, introduced polymorphism, or used lightweight obfuscation techniques to avoid detection by obfuscation and pattern-based detection.

In short: this could have been much worse.

The Client-Side Perspective


While the compromise originated upstream in the npm registry, the final delivery point of the malicious code was the Client-Side. The injected payload’s purpose was to tamper with crypto transactions running inside end-user browsers.

A web application developer cannot prevent a maintainer from being phished. The npm ecosystem cannot guarantee that no package will ever be compromised. But as an application owner, you can control what actually executes in your users’ browsers. And that is where the real battle is fought.

This is where Client-Side Monitoring becomes essential. With Jscrambler’s Webpage Integrity (WPI), every script that loads in the browser is continuously monitored for tampering and malicious behavior. It doesn’t matter if the compromised dependency came from a trusted maintainer or slipped through your CI pipeline: the moment it begins to act maliciously, WPI detects and stops it.

Applied to the chalk and debug attack, WPI would have:

  • Flagged and blocked the malicious code the moment it attempted to monkey-patch browser APIs like fetch or XMLHttpRequest to intercept network traffic.

  • Detected unauthorized attempts to hook into wallet objects such as window.ethereum and modify outgoing transactions before they were signed.

  • Prevented the exfiltration of sensitive data by blocking requests to attacker-controlled domains.

  • Detected changes to the JavaScript itself, updating the application inventory, alerting application owners and describing the new behavioral patterns introduced by the injected code.

In practice, this means that even if a compromised package made it into production, the attack would have failed at the very last mile. Users’ wallets would remain untouched because the malicious script would never be allowed to execute its intended behavior.

And that’s the key point: you can’t stop every supply chain compromise. Even packages with impeccable reputations and long histories of community trust, like those affected in this incident, can be compromised. But you can stop these compromises from ever impacting your users by securing the runtime environment where attackers ultimately need to succeed — the browser. Plus, it’s something that you have 


Parallel with PCI DSS


This attack illustrates exactly why PCI DSS v4 introduced two new eSkimming requirements:

  • 6.4.3 — maintaining an inventory of scripts and ensuring their integrity.

  • 11.6.1 — monitoring scripts for tampering or unauthorized changes.

Just like in e-commerce, the initial compromise is often unstoppable. What matters is what happens afterwards: the controls you put in place to stop malicious scripts from harming your users.
  

This isn’t just a credit card problem — it’s a web problem.

Conclusion

The npm chalk and debug compromise was a wake-up call at massive scale — 2.6 billion weekly downloads affected. The injected payload was a crypto-stealer, targeting MetaMask wallets and tampering with browser-level APIs. However, the real lesson is not limited to this one incident. It’s about the fragility of the web’s trust model:

  • Composability makes the web powerful, but also fragile.

  • Reputation is not security. Even trusted maintainers can be compromised.

  • Static audits alone will never be enough. Runtime monitoring is the only way to know what’s really happening inside your users’ browsers.

Supply chain attacks aren’t going away. By deploying Client-Side security controls, you can protect both your applications and your users — even when trust elsewhere fails.


The web’s trust model is broken. Runtime monitoring is how you stop it from breaking you.

Indicators of Compromise (IOCs):

Compromised npm packages

Package Name

Compromised Package Version

backslash

0.2.1

chalk-template

1.1.1

supports-hyperlinks

4.1.1

has-ansi

6.0.1

simple-swizzle

0.2.3

color-string

2.1.1

error-ex

1.3.3

color-name

2.0.1

is-arrayish

0.3.3

slice-ansi

7.1.1

color-convert

3.1.1

wrap-ansi

9.0.1

ansi-regex

6.2.1

supports-color

10.2.1

strip-ansi

7.1.1

chalk

5.6.1

debug

4.4.2

ansi-styles

6.2.2

proto-tinker-wc

0.1.87

@coveops/abi

2.0.1

duckdb

1.3.3

@duckdb/node-bindings

1.3.3

@duckdb/duckdb-wasm

1.29.2

@duckdb/node-api

1.3.3

Exfiltration Domain

npmjs[.]help

Silent Skimming vs Double-Entry Attacks

by Pedro Marrucho and David Alves, with contributions by Tom Vicary

Client-side attacks against e-commerce aren’t going away and are evolving. Threat actors are constantly changing their methods, shifting from one exploitation technique to another as they assess what works and what slips past current defenses. Silent skimming and double-entry attacks are two methods that employ distinct approaches to steal payment data.

Silent Skimming Attacks


In a silent skimming attack, the primary goal of the threat actor is to execute the operation entirely under the radar. The code is developed in a way that avoids the disruption of the regular payment flows, with no extra prompts, error messages, or drastic visual changes. That way both the user and merchant will see a successful purchase and because it only asks the user to fill its data once there is no noise so it can go undetected for long periods.

The lack of visible clues for the user and the absence of a sales decline is what makes silent skimming especially effective unless you’re monitoring which scripts are loaded in the browser by the website, tracking changes to script content, or monitoring website communications.

Double-Entry Attacks

In a double-entry attack, attackers employ deception rather than stealth. A fake payment form is injected or overlaid onto the page, tricking users into entering their card details before the real form appears. After submitting the fake form, the user sees an error message or simulated glitch indicating an issue with the transaction and is then shown the legitimate form, prompting them to re-enter their details.

This tactic often gets caught faster than silent skimming because the duplicate entry process raises red flag,s and users may report the unusual behavior.

  • Example:

In early 2025, Casio UK was hit by a double-entry skimmer. The attacker hijacked the cart page’s checkout button, displayed a fake multi-step payment pop-up, and stole the card details before redirecting the user to the actual checkout page.

Different Attacks, Same Defenses 

Generic web security controls won’t catch skimming. To deal with real-world skimming attacks, you need defenses that pay attention to what’s happening, what scripts are running, how the DOM changes, and where data is going. These are some of the controls that can be applied:

JavaScript Monitoring at Runtime

  • Flag new or modified scripts in production.

  • Detect scripts accessing sensitive fields or intercepting form data.

  • Catch exfiltration attempts to unfamiliar domains.

 Subresource Integrity (SRI) + CSP

  • Use SRI to pin third-party scripts.

  • Use CSP to restrict script sources and outbound connections.

  • Block inline scripts where feasible.

Form/Iframe Protection

  • Detect overlays, duplicate fields, or injected submit listeners.

  • Validate front-end consistency against the expected UI.

Script Inventory and Authorization

  • Maintain an up-to-date inventory of every script loaded

  • Explicitly authorize each script based on its behavior

Silent Skimming vs Double-Entry Attacks: Make Sure You’re the Winner


Silent skimming and double-entry tactics underscore the evolving and pervasive nature of skimming attacks. While their techniques differ – one operating stealthily, the other using deception to harvest data in plain sight – their objective is the same: to compromise sensitive data by exploiting vulnerabilities in the digital payment ecosystem. Their tactics may differ, but so do the measures used to prevent them.


Defending against these cyber threats requires a unified strategy that reinforces user access, data confidentiality, data collection, data transmission, and web code security. And as these attacks continue to evolve, so must your defences.

The Silent Bug: How JavaScript Memory Leaks Affect Web Performance

While browsing, you may notice that a website or app becomes slower over time. At first, everything loads quickly, but then the pages begin to slow down, the computer or phone starts using more memory, and eventually, the browser may even crash. 

When this happens, many users assume their device is the problem, but the real issue is often hidden in the way the website or app manages memory. Some programs unintentionally retain memory they no longer need, which causes performance to decline over time and leads to memory leaks.

This article explains memory leaks, their impact on website performance, the causes of these leaks, and how to identify and resolve them.

How JavaScript memory leaks affect web performance

When building a website, one of the important things developers prioritize is performance. If a website is slow or unresponsive, visitors may get frustrated and leave. This can reduce traffic, lower sales, and make people less likely to use or visit your product or website.

One primary reason websites slow down over time is due to memory leaks. This happens when a website continues to use memory it no longer needs, instead of freeing it up. As a result, the browser stores more and more unnecessary data, leading to:

  • Increased memory usage – The website continues to consume memory, causing the computer to run slower.

  • Slow response and unresponsive – Pages don’t respond smoothly when scrolling or clicking buttons.

  • High CPU usage – The browser has to work harder to keep the website running.

  • Browser crashes – If too much memory is used, the browser may freeze or close unexpectedly.

Normally, JavaScript has a system called a garbage collector, which automatically frees up memory that’s no longer in use. But when a memory leak occurs, this process fails, and the memory stays occupied, slowing down the website or app.

Above, it’s mentioned that JavaScript’s garbage collector is used when defining memory leaks. This might leave you curious about what exactly it does and how it helps manage memory. 

What does the garbage collector do?

JavaScript has a built-in process called garbage collection that automatically removes unused data from memory. This helps prevent memory from filling up with unnecessary information.

JavaScript keeps track of variables, objects, and functions that are currently in use. When an object is no longer needed (no part of the code references it), the garbage collector removes it from memory.

Common causes and how to prevent memory leaks in JavaScript

Above, it’s mentioned that JavaScript uses a built-in process to manage memory. If the garbage collector automatically removes unused data, why do memory leaks still happen?

Here’s the key issue: while the garbage collector runs in the background, coding mistakes can prevent it from working correctly. When this happens, memory that should be freed stays occupied, leading to memory leaks that slow down websites over time. Here are some of the most common causes of memory leaks in JavaScript:

1. Event Listeners

Event handlers are essential JavaScript functions that are often used to bind functions to events, such as click events or mouse events. Event listeners can also cause memory leaks if the event handler holds onto references to objects that are no longer needed. A simple example of this is a code that triggers a modal. 

const button = document.getElementById("openModal");
const modal = document.getElementById("modal");

button.addEventListener("click", function () {
  modal.style.display = "block";
});

This event listener is not removed when the modal is no longer in use, the browser will keep it in memory, even if the modal element is removed from the DOM. Over time, this can slow down performance.

How to Prevent Event Handler Memory Leaks

To prevent memory leaks, always remove event listeners using `removeEventListener()`  when they are no longer needed:

const button = document.getElementById("openModal");
const modal = document.getElementById("modal");

function showModal() {
  modal.style.display = "block";
}

button.addEventListener("click", showModal);

function cleanup() {
  button.removeEventListener("click", showModal);
}

2. Closure

Closures are a powerful feature in JavaScript, but they can unintentionally retain references to variables that should be freed. When a closure captures a variable from an outer scope, that variable will remain in memory as long as the closure exists.

function rememberUser() {
    let message = "Hello David, are you still there?";

    return function () {
        console.log(message);
    };
}

const saveMessage = rememberUser();
saveMessage(); 


Prevention

The code above logs output and retains memory, preventing garbage collection. To avoid leaks, remove references when they are no longer needed.

function rememberUser() {

    return function () {
        console.log(“Hello David, are you still there?”);
    };
}

const saveMessage = rememberUser();
saveMessage();


Setting the `message = null;` breaks the closure reference and allows JavaScript’s garbage collector to free up memory.

3. Global Variables

Variables declared in the global scope remain in memory throughout the application’s lifecycle, meaning they aren’t automatically removed when they are no longer needed. This can lead to unnecessary memory consumption and potential memory leaks, especially when handling large objects or data structures.

function printProfile() {
  profile = "Hi, My name is John, I'm a Software engineer"; 
}
printProfile();

The code example above has a leak. The profile is a global variable, and when it is declared, it remains in the application’s memory.  

Prevention

To fix this, limit variable scope by declaring variables inside functions. This way, JavaScript will automatically clear them after the function is done running.

function printProfile() {
  let profile = "Hi, My name is John, I'm a Software engineer"; 
}
printProfile();

The above code has a declared variable inside the function created using the let keyword.

4. Set Timers and Intervals

setTimeout and setInterval are used to execute functions after a delay or repeatedly at a set interval. However, if they are not properly cleared, they can cause memory leaks by holding references to objects that are no longer needed.

function startCreating() {
    let student = {
        name: "Jane Smith",
        age: 25
    };

   const intervalId = setInterval(() => {
        console.log(`Student: ${student.name}, Age: ${student.age}`);
    }, 1000);
}

startCreating();

The code above creates an interval that logs user data every second. However, if you don’t stop it, the interval continues running even when the user object is no longer needed.

Prevention

To prevent memory leaks, ensure that you clear timers when they are no longer necessary. Use `clearInterval()` or `clearTimeout()` once the component or function has finished using the timer.

function startCreating() {
    let student = {
        name: "Jane Smith",
        age: 25
        
    };

    const intervalId = setInterval(() => {
        console.log(`Student: ${student.name}, Age: ${student.age}`);
    }, 1000);

    setTimeout(() => {
        clearInterval(intervalId);
        console.log("No more Logging and the memory is cleared forever ?");
    }, 5000);
}

startCreating();

There are several causes of memory leaks in JavaScript, but the following are the most common mistakes developers make that lead to such leaks.

How to detect JavaScript memory leaks

Memory leaks are difficult to spot because the browser doesn’t display an error when they occur. This makes them difficult to notice and fix. However, if your website keeps getting slower over time, it could be a sign of a memory leak. The good thing is that browsers have built-in tools to help find and fix these issues.  

Finding Memory Leaks with Chrome Developer Tools  

Modern web browsers come with developer tools that let you check memory usage, take snapshots, and track down memory leaks. By regularly checking your website’s memory usage, you can identify problems early and prevent them from slowing down your site.  

In a previous article on debugging, it was explained how to use Chrome Developer Tools for debugging JavaScript errors. Now, let’s see how these tools can help us find and fix memory leaks.

In Chrome DevTools, the most useful tool for detecting memory leaks is the Heap Snapshot feature in the Memory tab. 

Memory-tabThe Heap Snapshot tool enables you to capture a detailed memory report of the main thread, web workers, or iframes, allowing you to analyze how memory is being utilized.

Heap-Snapshot-tool
When you click the “Take Snapshot” button, Chrome DevTools records all the objects currently stored in memory on that webpage. This includes elements still linked to the window, timers like setInterval, and other active data. You can think of it as a frozen snapshot that captures how the page is using memory at that exact moment.

You can check these objects individually or filter by the class name to detect memory leaks.

detect-memory-leaks

Third-Party Tools for Detecting JavaScript Memory Leaks

In addition to built-in browser-based tools like Chrome DevTools, Firefox Developer Tools, and WebPageTest’s profiler, which provides deeper insights into performance issues, you can use third-party tools to detect and monitor memory leaks in JavaScript applications. Options like memwatch-next, leakage, Heapdump, and VisualVM help track memory usage. There are more advanced analysis tools, such as Node.js Inspector and Valgrind, that can also be useful.

Conclusion

Memory leaks in JavaScript can silently degrade application performance, leading to slow load times, increasing memory consumption over time, and even causing crashes. While common culprits, such as forgotten timers, event listeners, or closures, are often to blame, leaks can also arise from less obvious sources, including detached DOM nodes, unbounded caches, or lingering references in third-party libraries.

Understanding how these issues occur and how to detect and mitigate them is essential for building high-performance, resilient web applications. Because memory leaks can manifest in subtle and varied ways, developers should make it a habit to profile their applications regularly and stay informed about best practices.

Charity Hacked: Web Skimmer Infected Caritas Spain Websites for Over a Year

with David Alves and Pedro Marrucho


This research documents Jscrambler’s investigation into a stealthy web skimming campaign that infiltrated multiple Caritas Spain websites of Caritas Internationalis, a confederation of over 160 Catholic relief, development, and social service organizations operating in more than 200 countries and territories.
 

All targeted Caritas Spain websites were running WooCommerce, the popular e-commerce plugin for WordPress. The attack chain consisted of two distinct stages, carefully designed to remain undetected while intercepting sensitive payment data.

This campaign reinforces a broader trend that has been observed: web skimming infections are increasingly driven by modular kits. These toolkits enable attackers to mix and match various delivery mechanisms, support multiple communication channels within their infrastructure, and deploy flexible combinations of loader and skimmer scripts. Jscrambler was able to confirm that the threat actors’ automated validation and use of the stolen cards occurs soon after they are captured.

What makes this case especially concerning is the nature of the victim: Caritas is a non-profit dedicated to helping vulnerable communities. Still, attackers were happy to keep their skimming operation going on seventeen Caritas España sites for over a year until it was shut down recently. 

How the attack works


The skimming campaign, like many, was executed in two stages. Stage one served as the loader, laying the groundwork for the attack. Stage two held the skimmer logic itself, injected a fake payment form, and exfiltrated sensitive data.

Stage 1: Entry point and loader

The initial infection vector remains unknown. It could have been an exploited vulnerability, a misconfigured server, a compromised plugin, or credentials theft.  In this type of infection, threat actors usually ensure they can retain access even if the initial vulnerability or misconfiguration is patched or fixed in the meantime.

It was observed that the Stage 1 loader was injected as a one-liner at the bottom of the minified WooCommerce JavaScript: cart-fragments.min.js, which runs on the homepage.

https://www.caritas.es/main-files/plugins/woocommerce/assets/js/frontend/cart-fragments.min.js?ver=6.1.2

Here’s what that one-liner looks like:

Entry-point-and-loaderListing 1 – Sample stage 1 loader


This one-liner (lightly obfuscated) executes the following logic:

  1. Clears a key (XsuHCYmfbgVSRFVx7SHRnU7DfapjFpaf) from localStorage (this entry is associated with a potential web socket connection);

  2. Checks for another key (QIQWJJnh1Ckclw0jFr5KPME2M3kYHTyq) to avoid multiple skimming attacks on the same device;

  3. Dynamically constructs a URL using String.fromCharCode — in this case, https://categorywishlist.com;

  4. Sends a POST request to that endpoint;

  5. If the response is successful, the loader:

    • gets a JSON response that, among other things, includes a property called response.data containing the following subproperties:

  • “script”: the URL from where the stage2 script must be loaded,

  • “handler”: the URL to where skimmed data will be exfiltrated by the Fetch method,

  • “Socket”: an alternative exfil channel using Web Sockets

sample-jscon-returnedListing 2 – Sample JSON returned by the categorywishlist.com endpoint


The arrays above are strings using the same obfuscation technique as before. Using String.fromCharCode, you get:

json-after-decoding-string-elementsListing 3 – JSON after decoding string elements

  • Then “socket” and “handler” are saved, respectively, into Local Storage items “XsuHCYmfbgVSRFVx7SHRnU7DfapjFpaf” and “XsuHCYmfbgVSRFVx7SHRnU7DfapjFpaf_handler”

6. Loads a stage 2 script from https://newsasssets.com/caritas.js by appending it to the head section of the DOM.


Any errors are caught by the script so as to avoid triggering errors that could lead to an earlier discovery of the attack.

Stage 2: Fake payment form and exfiltration

donor-form-caritas-spainImage 1 – Donor form


Once loaded, the second-stage script monitors for the selection of the Tarjeta (payment card) payment option. When detected, it injects a fake Continuar (Proceed) submit button and hides the real one.

payment-card-option-selected-on-the-donor-formImage 2 – Payment Card option selected on the donor’s form

fake-button-inject-functionImage  3 – Fake button inject function (deobfuscated for improved readability)


Once the user submits the initial form, it overlays it with a fake payment form on the same domain (caritas.es) that visually resembles the legitimate payment gateway (Redsys on behalf of Santander).   The skimmed data — cardholder name, number, expiry, and CVV — is collected and exfiltrated via a covert Image.src request to https://hotjarws.com/b.php, base64 encoded.

user-submits-the-initial-form


Collected data:

  • Cardholder data:

    • User Agent

    • Email

    • First Name

    • Last Name

    • Address

    • City And Postcode

    • Cardholder name

    • Phone

    • DNI

    • Payment card number

    • Expiry date

    • CVV

  • Collected from:

    • website

fake-payment-form-running-caritas-spainImage 3 – Fake payment form running at Caritas.es

exfiltrated-dataImage 4 – Exfiltrated data

exfiltrated-data-example

After the user submits the fake payment form, the overlay is removed, and a loading spinner is shown for a few seconds. Then, the donor form is shown again. At this point, provided that the user does not deselect the target (card), if the user clicks Continuar (submit), they are redirected to the legitimate PSP site to proceed with the donation.

legitimate-payment-form

Image 5 – Legitimate payment form on sis.redsys.es

While the visual representation is convincing, the differences become obvious after the user is taken to the second payment form.  By comparing Images 3 and 5, they are easily spotted.  The question is whether these differences are sufficient to prompt users to report them to Caritas, and, if so, how those reports are being processed.

Infected websites


The team checked all Caritas related websites and we found the same skimming campaign affecting the following 17 websites:

  • caritas.es

  • caritasespanola.org

  • caritasdtr.org

  • caritasoa.org

  • caritastortosa.org

  • caritas-sc.org

  • caritasmalaga.es

  • caritasmallorca.org

  • caritasmeba.es

  • caritasplasencia.org

  • caritasvalencia.org

  • caritas-zaragoza.org

  • caritasbi.org

  • caritasbisbatvic.cat

  • caritasjaen.es

  • caritasregiondemurcia.org

  • diocesanaterrassa.caritas.es

Most websites were hosted under the same IP address, indicating centralized management or a shared infrastructure. A few exceptions exist (3), but the majority were under the same IP, and they are likely being managed using a common backend administration panel.

infected-websites-infrastructure-graph

Image 6 – Infected websites infrastructure graph

Research timelines


This campaign was first detected on March 16th, 2025. Soon enough, all seventeen infected websites from Caritas in Spain were found, along with all domains and IP addresses involved. Then, between April 1st and 3rd, 2025, the stage 1 categorywishlist.com and the C2 domain newassets.com went offline, but the websites were still infected with Stage 1. On April 3rd at 1 am, the C2 domain came back with a new IP:

research-timelines

At this point, Jscrambler started reaching out to victims. The team reached out to Caritas on April 4th. All Caritas-infected websites were down for maintenance shortly after. 

websites-under-maintenanceImage 7 – Websites under maintenance

When they returned online, few things had changed. All websites were still infected, but the point of infection was now on a different WooCommerce script:

woocommerce-script


The Jscrambler Research Team couldn’t confirm whether the previous script was cleaned by Caritas or if the threat actors had simply changed the infection point elsewhere, perhaps as an evasion tactic. Evidence suggests that the threat actors have persistent access to the WooCommerce installation. 

Besides that change, the skimmer stack remained the same, except that a skimmer-related domain changed. Instead of using categorywishlist.com for the Stage 1 JSON request, it was now using assetssafepay.com. All other skimmer-related domains remained unchanged.


Jscrambler reached out again to Caritas, and by April 11th, 2025, we confirmed the infection had been removed.  

Other targets and skimming insights

The same campaign targeted several websites beyond Caritas. The Jscrambler Research Team discovered that this particular threat actor was leveraging an unusually high number of domains, totaling 61, most of which were hosted under the same IP address that had recently been assigned to their C2 server (see Image 7).

When an attacker uses such a large volume of domains, attempting to take them down individually is often ineffective. Taking down the IP address — for example, by reporting it to the hosting provider — is typically a more impactful countermeasure, especially when many sites are hosted on the same infrastructure.


Other-targets-skimming-insights

Image 8 – Domains used by the Threat Actor

Across other victim websites, we observed variations of the same attack. These differences appeared in both the loader scripts (Stage 1) and the main skimmer logic (Stage 2):

  • Stage 1 loaders: varied in location and method. Some were injected into different plugins or scripts, including those loaded via a legitimate Google Tag Manager (GTM) or through scripts pretending to be GTM. The loader code itself also showed multiple variations.

  • Stage 2 scripts: clearly generated by a tool, all very similar, usually obfuscated. In some cases, we saw a comment header typically associated with Google Closure Compiler: Copyright The Closure Library Authors. SPDX-License-Identifier: Apache-2.0. While the team cannot confirm whether Closure Compiler was actually used, it is plausible that the attacker inserted this comment deliberately to make the script appear more legitimate. If Closure Compiler was used at some point, its traces may have been overwritten by subsequent layers of obfuscation. It’s worth noting that Closure Compiler is intended for minification, not obfuscation; however, the distinction may be lost on defenders performing a quick analysis.


Interestingly, some victims appeared aware that something was wrong, but lacked the means or knowledge to properly address it. On one merchant’s site, there was an attempt to block the skimmer by injecting a script designed to prevent the stage 2 payload from loading from paysafeprocess.com.

merchant-attempt-fix-the-problemImage 9 – A merchant’s attempt to fix the problem

How fast is stolen data used?


The Jscrambler Research Team wanted to understand how quickly stolen payment data from this campaign was being used. To do so, a canary payment card was deployed from Thinkst — a specially generated card number intended for use as a decoy and not for legitimate transactions. If this card is used or even tested for authorization, it triggers an alert, confirming that it has been compromised. Think of it as a payment card honeypot.

Using a canary card service, a card was created and submitted through the fake form on caritas.es. Within just 10 minutes, an alert was received.

incident-info-canary-payment-card-triggered-alert

Image 10 – Canary Payment Card triggered alert


Given how quickly the card was tested, it’s likely that the threat actor’s infrastructure is automated to validate stolen cards soon after they are captured.

Highly persistent infection


As stated above, the Jscrambler Research Team first detected this infection in mid-March 2025. However, by examining historical snapshots from Archive.org and other web-crawling services, the initial infection was traced back to February 22nd, 2024.

highly-persistent-infection

first-sign-of-infection

Image 11 – First sign of infection on February 22nd, 2024


Over time, it was observed that different scripts were being infected, multiple skimming techniques were deployed, and there were periods of dormancy. These signs strongly suggest that the threat actors had persistent access to the WooCommerce environments, allowing them to reintroduce or modify the skimming code at will, shift infection points, and rotate infrastructure domains as needed.

As of April 11th, 2025, Jscrambler confirmed that the skimmer was no longer active. However, from our external vantage point, we cannot determine whether the attacker still retains any level of access to the affected systems.

How to protect against web skimming


To mitigate the risk of web skimming, organizations that accept online payments should consider implementing robust security solutions. Merchants accepting payment card payments are already contractually obligated to comply with security standards such as PCI DSS.

Specifically, the following requirements apply:

  • Requirement 6.4.3: Merchants must ensure that all payment page scripts are authorized and have a method to assure their integrity. This means cataloging which scripts are expected to run on the payment page, validating their sources, and implementing integrity checks.

  • Requirement 11.6.1: Merchants must implement a change- and tamper-detection mechanism to alert on unauthorized modifications to HTTP headers and the contents of payment pages as received by the consumer browser. This requires the ability to detect if a skimmer or rogue script is injected post-deployment.


Implementing script behavior-based monitoring solutions, such as Jscrambler Webpage Integrity, can detect unauthorized script injections and enforce PCI DSS requirements 6.4.3 and 11.6.1.  

Given that small merchants often lack the expertise or resources to fully implement the stringent requirements of PCI DSS, these automated solutions provide an essential layer of protection. Equally important is having an incident response plan in place that includes how to communicate with users and restore trust after an incident.

Web skimming prevention requires a combination of technical controls, process rigor, and continuous visibility into what is executing on your users’ browsers during checkout.

Conclusion

Losing payment card numbers to threat actors is always a serious matter, regardless of the context. When it happens in a typical online shopping scenario, the impact is significant. However, when someone visits a website solely to make a donation to a charitable cause and their cardholder data is stolen in the process, it feels especially disheartening.

This incident serves as a powerful reminder that web skimming is a threat that transcends organization size, mission, or industry. Threat actors don’t discriminate — if a website processes cardholder data, it’s a viable target.

Skimming operations are increasingly relying on automation. From searching and infecting victims, to validating and using the stolen data. There is a market for skimming kits, which invites new threat actors to set up their skimming operations. This attack also demonstrates that these attacks can persist for extended periods, making web skimming a highly profitable activity for threat actors.

If your organization processes online payments, ensuring the integrity of what users see in their browsers is critical. To learn how Webpage Integrity can help you detect and stop web skimming attacks before they cause harm, get in touch with us — we’re here to help.

Indicators of Compromise (IOCs):
GTM IDs

GTM

GTM-KJJ3BFKC

Indicators of Compromise (IOCs):
Malicious Domains

C2

IP

ASN

ASN-NAME

ASN-COUNTRY

paysafeprocess[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

analysiscache[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

analysisdoubleclick[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

assets-token[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

assetshotjar[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

assetssafepay[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

bundlereferrer[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

analysistoken[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

notificationfeedback[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cachereferrer[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

campaigntransaction[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

categorywishlist[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cdn-secure[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cdnmozilla[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cdnnewassets[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

explorerstripe[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

gravatargo[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

platformwebsite[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

heure-planification[.]com

185[.]208[.]158[.]121

42624

SWISSNETWORK02, SC

Seychelles

hipaye[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

hotjarmultisafe[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

interfaceuser-friendly[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

locationchrome[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

ratingrecommendation[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

recommendationtrend[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

inventoryvoucher[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

timestampcdn[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

referrerlocation[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

refundpurchase[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

safepaymultisafe[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

securedata-ns[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

securi-analitycs[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

staticreview[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

stockbarcode[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

jguery-web[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

web-jguery[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

stripehotjar[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

tag-secure[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

user-friendlyexperience[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

webjguery[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

webjquery[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

wishlistnotification[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

ads-advertise[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

multisafebundle[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

paysafeprocess[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

analysis-ns[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

hotjarws[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

mozillastatus[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

firefoxcaptcha[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

experienceresponsive[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

newsassets[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

checkoutcache[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cdnweb2analytics[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cdnsafepay[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

bundle-referrer[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cloudsolutio[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

cachecaptcha[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

assets-tokens[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

advertcheckout[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

token-analysis[.]com

86[.]54[.]42[.]111

42624

SWISSNETWORK02, SC

Seychelles

approvalreceipt[.]com

stripepayway[.]com

Formjacking: How to Prevent Hackers Hijacking Your Online Forms

In 2018 British Airways became one of the first high-profile victims of a relatively new cyber-attack that compromised 380,000 customer transactions. In the wake of the data breach, a cybersecurity firm discovered malicious code injected into the company’s website. The script was designed to steal financial data (names, email addresses, and credit card details, including the long number, expiry date, and three-digit CVV security code) from online payment forms, earning the technique the name formjacking.

What is formjacking?


Formjacking is a type of cyber-attack that takes place on the client-side in the front-end JavaScript code. It occurs when cybercriminals compromise a website’s payment or data entry forms by injecting malicious code directly into them, hijacking their functionalities without altering their visible/apparent behavior.
 

This attack is designed to steal credit card details and other sensitive information inputted into payment forms that can be captured on online checkout pages. It’s important to distinguish between formjacking and web skimming, which is a broader term. Skimming might use formjacking as a way to steal data, but not every skimming attack uses formjacking, and not every formjacking can be classified as skimming.

How does formjacking work?


Once the malicious script has been injected, it intercepts the input fields, typically targeting payment card information and other sensitive data entered by users, before sending it to the hacker’s server. This exploits users’ trust in established, legitimate web applications, allowing a formjack to remain undetected for a prolonged period.

For instance, the malicious script injected into the British Airways website consisted of only 22 lines of code that grabbed data from the online payment form and sent it to the hackers’ server once a customer hit the “submit” button.

A formjacking attack can be broken down into five steps:

  1. Compromising the website or a third-party script: The hacker exploits a vulnerability related to the web application. This could be anything from the content management system (CMS) to a JavaScript supply chain attack via an open-source or third-party library.

  2. Installing malicious script: With access gained, the hacker installs the malicious script into the web application, or they might add a reference to redirect back to where the script is hosted.

  3. Obfuscating the script: Hackers regularly obfuscate malicious scripts, so they blend into the existing code, making it difficult to spot. 

  4. Skimming the end user data: The hacker can now collect personal and financial data from user forms before exfiltrating it to their server.

  5. Getting financial gain: With the attack successfully executed, the hacker can usually harness the compromised data for financial gain by selling it on the dark web, using it to conduct other attacks, or committing payment card fraud. 


Examples of formjacking attacks

The British Airways formjacking attack in 2018, which resulted in a fine of over £20 million, isn’t the only high-profile incident.

Ticketmaster (2018)

Attackers compromised Ticketmaster’s online payment pages by injecting malicious code via a third-party provider. This allowed them to steal the names, addresses, email addresses, phone numbers, payment details, and Ticketmaster login details of around 40,000 customers. Many victims had fraudulent transactions debited from their accounts on things like money transfer service Xendpay, as well as Uber and Netflix gift cards.
 

Claire’s (2020)

US-based jewelry and accessories retailer Claire’s shuttered its global physical presence on 20 March 2020 after the COVID-19 pandemic blindsided the high street. Within 24 hours, a malicious domain, claires-assets.com, had been registered by an anonymous actor. A sequence of malicious code was injected into Claire’s online store and that of its sister brand, Icing. After lying dormant for four weeks, the code became active, allowing the hacker to intercept customer information entered at checkout and redirect it to a fake server on the malicious domain.

Why is formjacking so difficult to detect?

Formjacking can be difficult to detect because it occurs on the user’s browser (client-side). Traditional security tools like scanners try to identify known vulnerabilities, and have no visibility into how code, including client-side JavaScript, behaves on their browser. A vulnerability can remain undetected in your code because developers lack the visibility to detect it.

Hackers also use obfuscation techniques to frequently change the signatures and appearance of malicious scripts. By designing them to load dynamically, they evade detection by most external scanners. For instance, they might only load in a real client-side environment or remove themselves from memory when they detect code analysis taking place. This makes static scanning and traditional signature-based detection methods ineffective.

How to prevent formjacking


Regularly patch and update software, plugins, and extensions 

Most formjacking code is added to existing scripts on a website via vulnerabilities in the underlying software. Businesses using a third-party e-commerce platform like Magento or a content management system like WordPress must keep this software up to date, including any plugins and extensions.

Consistently applying security updates and patches prevents vulnerabilities before hackers can exploit them to gain access and inject malicious code that corrupts your forms. Rather than performing this manually, which is time-consuming and exposes the business to human error, automate the process for critical software, plugins, and extensions.
 

Conduct regular vulnerability scans and penetration testing

Perform regular security assessments, notably vulnerability scanning and penetration testing, to identify and address potential weaknesses in your website.


Vulnerability scanning is the process of evaluating a system or network to detect known security vulnerabilities. This proactive measure identifies any weaknesses that a hacker could exploit to gain unauthorized access to your website and inject malicious code.


Typical scans include:

  • Automated web application scans: Identifies typical entry points for formjacking like Cross-Site Scripting (XSS), JavaScript injection, malicious script injection, and outdated dependencies.

  • Third-party script audits: Regularly scans external JavaScript resources to identify vulnerabilities introduced by third-party scripts.

  • Real-time monitoring: Continuously monitors changes to web scripts, alerting you in real time if unauthorized scripts are added to your forms or modified.

Penetration tests simulate real-world cyberattacks to identify potential weaknesses manually, complementing automated scanning. An ethical hacker works on behalf of your organization to uncover vulnerabilities like a criminal hacker would. Fundamental to this is the manual analysis of JavaScript, especially scripts on payment and checkout pages, to detect unauthorized or suspicious code. 


Use a secure connection

A secure HTTPS connection can help mitigate the risk of formjacking by encrypting the data transmitted between users and your server. It is a good practice that may make the hacker’s job harder, preventing man-in-the-middle attacks and hiding data in transit. This extra layer of defense makes it significantly more difficult for attackers to intercept or alter sensitive information, like payment details, and maintain data integrity. HTTPS also ensures users communicate with your legitimate website, protecting against spoofed or malicious ones.  


Implement a Web Application Firewall (WAF)

A Web Application Firewall (WAF) actively monitors incoming web traffic, identifying and blocking suspicious activity, including unauthorized script injections, before they compromise the application. By introducing strict security policies, filtering harmful requests, and continuously analyzing traffic patterns, a regularly updated WAF can detect anomalies indicative of formjacking. However, it’s important to note that a WAF primarily protects against server-side threats. Formjacking is a client-side attack that happens inside the browser, meaning a WAF may not detect it unless it specifically monitors outgoing requests.

The Ultimate Protection: Adopting Client-Side Protection

Companies must guarantee protection on both their servers and the client side to mitigate the risk of threats and attacks. The Jscrambler Webpage Integrity product enables businesses to achieve fine-grained control over the behavior and data consumption of their third-party tags. It offers protection against data breaches, formjacking, web skimming attacks, and data exfiltration by blocking unauthorized behavior without slowing down your website. Our security-by-design approach ensures ongoing PCI DSS v4 compliance, transforming your third-party JavaScript into secure assets.

Moreover, with Jscrambler’s Iframe Integrity payment iframes from Payment Service Providers and payment processors can be protected as well. Hardened with Jscrambler’s cutting-edge security, the iframe is continuously monitored and safeguarded against overlay, hijacking, and formjacking attacks targeting payment pages. Unlike traditional security solutions, Iframe Integrity is fully automated and completely transparent, requiring minimum maintenance from TPSPs and zero management from online merchants.

Protect Forms with Form Fencing
The granularity of Jscrambler’s form-fencing feature allows companies to control exactly which scripts can read and access form data, keeping malicious actors from extracting sensitive information that users enter into forms.

Reduce Surface
Area Risk
Gain an extensive view of your client-side attack surface and prevent formjacking attacks via third parties with data leakage controls, real-time alerting, and policies.


Scale Performance Efficiently
Jscrambler is designed to effortlessly support the largest, most demanding websites without causing any slowdown or disruption to the online user experience.

Accelerate PCI DSS Compliance
Authorize scripts, detect malicious behaviors, and deliver assessment-ready reports to achieve PCI DSS compliance with requirements 6.4.3 and 11.6.1.


Conclusion

If your business’s website accepts online payments, don’t adopt a reactive approach to formjacking. Safeguard your user’s data against the inherent vulnerabilities in client-side JavaScript by taking proactive steps to prevent this notoriously difficult-to-detect attack from occurring in the first place – preserving their trust and maintaining your reputation.

Stripe API Skimming Campaign: Additional Victims and Insights

with David Alves and Pedro Marrucho


Research Update 4/4/25 — Given some misrepresentations of the research described below, Jscrambler clarifies that it has not found any security issues with Stripe’s API or infrastructure.

A recently discovered web skimming campaign has introduced a novel technique that leverages a legacy Stripe API to validate stolen payment details before exfiltrating them. This tactic ensures that only valid card data is sent to the attackers, making the operation more efficient and potentially harder to detect. Our research team investigated and found additional victims. Those results, along with some additional insights, are presented here.

How the Attack Works


This web skimming campaign, like many, uses different stages (i.e., multiple JavaScripts) to load skimming logic on the checkout page.

Stage 1: Malicious Loader Injection

The threat actors likely infected their victims by exploiting vulnerabilities and misconfigurations in WooCommerce, WordPress, and others to plant the initial malicious script. 


Similar to almost every web skimming campaign in the last couple of years, stage 1 is just a loader script made to look like Google Analytics. In this case, it is the GAO variant (GoogleAnalyticsObjects) that we’ve seen in previous campaigns.
 

stage-1-malicious-loader-injection
Figure 1 – Sample stage 1 GAO variant


Stage 2: Decoding the Next Stage

The second-stage script is a one-liner that dynamically evals a simple base64-encoded code string that contains the URL for the final stage. It employs a very light form of obfuscation that can be removed effortlessly, but can be effective in preventing detection by hiding sensitive URLs from security controls that analyze scripts statically (e.g. WAFs).

stage-2-decoding-next-stage

Figure 2 – Sample stage 2

Stage 3: The Skimmer

The skimming script hides the legitimate Stripe iframe and overlays it with a malicious one designed to mimic its appearance. It also clones the “Place Order” button, hiding the real one.


stage-3-the-skimmer

After a user types payment details, the skimmer verifies the card’s validity using Stripe’s API. 

skimmer-verifies-the-card-validity-using-stripe-API

If valid, the details are sent via a dynamically invoked form submission to a drop server controlled by the attackers. An error is then shown to the user, requesting a page reload, which clearly qualifies this as a double-entry skimmer. Some other variants of this attack were reportedly silent skimmers, but our research could not observe that across all variants investigated.  After the attack takes place, a _dt entry is inserted into local storage to prevent the skimming code from being triggered twice.

inserted-into-local-storage

Uncovering Additional Victims


While the initial report did not disclose the number of compromised merchants, Jscrambler’s research team conducted an independent investigation and identified 49 merchants affected by this campaign, so far. This number is likely an underestimation, as new victims continue to be discovered.

Interestingly, the Stage 3 skimmer script appears to be dynamically generated and customized for each targeted site. Due to the structure and similarities across the different stage 3 scripts that we’ve seen, it appears that these skimmers are being generated using some skimming generation tool. Attackers are leveraging the Referrer header to determine which specific skimmer version to deliver, reinforcing the highly tailored nature of this campaign.

The analysis revealed that all infected sites found used two domains to serve the attack’s additional stages (2 and 3). However, we found 20 additional domains hosted on the same server / IP address for which we have not yet found live victims. These domains are similar / variations and that could also be actively in use by this campaign.

Jscrambler reached out to the compromised websites, and so far, 15 of them have either successfully removed the malicious scripts or the threat isn’t active anymore.

Brute-forcing additional victim websites


Stage 3 skimmer scripts were clearly tailored to the target website. Since the skimmer backend determines which script to return based on the referrer header, this mechanism can be abused. So, next, we brute-forced the identity of additional victims by fuzzing the Referrer header and inspecting the returned scripts. This technique worked quite well, although it’s unlikely to remain effective for long, as it’s relatively easy for attackers to defend against.


Interestingly, in one of these additional victim instances, we discovered that the skimmer was faking a Square payment iframe, demonstrating that these attacks target different types of merchants and PSPs.

brute-forcing-additional-victim-websites

Figure 3 – Iframed fake square payment form


Common Characteristics Among the Skimmer Variants


Our research uncovered several key traits shared across the various skimmer variants:

  • Minimal obfuscation: Only Stage 2 employs a basic base64 encoding technique. Stage 3 does not use obfuscation at all, which was surprising.

  • No encryption on exfiltrated data: Stolen payment details are transmitted in base64.

  • Attack timeline: We identified evidence suggesting the campaign has been active since at least August 20, 2024, based on domain activity logs.

  • Platform focus: Most compromised websites use WooCommerce, with additional cases involving just WordPress or PrestaShop.

  • Infrastructure insights: Over 20 additional domains were found being served from the same IP, hinting at a broader operation.


Why Are Attackers Using Stripe’s API?


The quick answer could be that they only need to skim valid credit card information. Attackers could easily do that later using, for instance, carding bots or dark Web services. But if you think about it, there are a couple of benefits from doing it client-side:

  • All websites were already using the API as part of their normal payment flow. Although we also observed instances where the threat actors were using hardcoded stripe API keys.

  • Secondly, security tools and researchers often use invalid credit card details as part of their work, so not skimming in these cases means being less likely to be detected.

Could Stripe detect this?

These verifications are hard to detect as fraudulent, because nothing changes compared with the normal use of those APIs:

  • These card validations are called when legitimate users are about to make a purchase, so checking the frequency of API requests should not reveal anomalies. In some variants, attackers were cautious enough to avoid making duplicate requests per purchase.

  • Requests originate from a real user device using a legitimate IP address, so device checks and IP reputation will not reveal anything.

  • Also, it is a real user navigating the browser, so Bot Detection doesn’t work either.

  • However, some hope may rely on the fact that the skimming requests are using a deprecated version of Stripe’s API, contrary to the legitimate requests that are using the latest, and sometimes using an API key associated with the criminals.

Adding payment options to the victim websites!

Sometimes, we observed the skimming code adding other payment options, like paying using cryptocurrencies. 


In these cases, the skimmer dynamically injects an HTML DOM element that mimics a legitimate MetaMask wallet connection window, in the hope of tricking users. 


skimmer-injects-html-dom-element


We added at the bottom the unique wallets addresses seen in all analyzed scripts.

We checked the activity of all wallet IDs that we found in use by the threat actor, but we couldn’t find a single one that had recent transactions. Most wallets had no transactions, and a few only had some in 2013 – so, in this case nothing relevant. But it is probably a matter of time until we see this tactic returning profits for the fraudsters.

How to Protect Against This Attack


To mitigate the risk of web skimming, merchants and PSPs should consider robust security solutions:

  • Merchants can use real-time webpage monitoring
    Implementing solutions like Jscrambler Webpage Integrity can detect unauthorized script injections and enforce PCI DSS 4.0 requirements 6.4.3 and 11.6.1.

  • TPSPs can help their merchants by adopting a hardened iframe implementation
    Jscrambler’s recently launched Iframe Integrity solution prevents unauthorized iframes and form modifications on a webpage. This solution protects merchants by blocking iframe hijacking and ensuring TPSPs (Third-Party Service Providers) can confirm that merchants’ sites are not susceptible to script-based attacks.


Given that small merchants often lack the expertise or resources to fully implement PCI DSS 4.0’s stringent requirements, these automated solutions provide an essential layer of protection.


Conclusion


This sophisticated web skimming campaign highlights the evolving tactics attackers use to remain undetected. And as a bonus, they effectively filter out invalid credit card data, ensuring that only valid credentials are stolen. 

Jscrambler’s research team continues to track this campaign, and we urge all online merchants to prioritize security measures against client-side threats. If you suspect your site might be compromised, reach out to us for a security assessment.

Indicators of Compromise (IOCs): Malicious Domains

C2

IP

ASN

ASN-NAME

ASN-COUNTRY

isjustour[.]site

149[.]255[.]35[.]143

AS29802

HIVELOCITY, Inc.

USA

bsjq-online[.]store

149[.]255[.]35[.]143

AS29802

HIVELOCITY, Inc.

USA

isjustone[.]site

146[.]70[.]53[.]157

AS9009

M247 Europe SRL

Bulgaria

jqbs-get[.]store

146[.]70[.]53[.]157

AS9009

M247 Europe SRL

Bulgaria

Indicators of Compromise (IOCs): Stripe API Keys


pk_live_51H2IxSIClY5DGt7iuSnSBYQMO6zR8DGYtwLd6oGeaCKovAezwxyHGkyrIUyybKvt46PXB8yGbrxeNVrjdL0Nup8S00l8suNOAa

pk_live_51HpgKZFLzaFQsfiXfWB9VE9tXDi8ecUppY46TiJJyB2bAsCcKp9H9e9NdsYXxM7ZmqIjQE0ApCWk61oiRVTnCNa500jmJDOga0

pk_live_51Kn968HWTqClCNSz38qX6cfIiIcUyzPwmBNGIT17xjYtx9fG6zH6ccohpaX0B8AeAt7vv2TQbxwiMNPKWWMYo4RZ00CYoBWe8S

pk_live_Ac5zctYSc2Pb0woVdIdXzydQ

pk_live_BE5dBdmbimjikuSQPSikoTAN

pk_live_E4VDojiYs4GBUMdyAZEw9oil

pk_live_LB9l97CQDVEGxyeLr7VS46EO00mIYbx7FL

pk_live_OLgzcciecW3V6Huzr4IZFBlF

pk_live_iBIpeqzKOOx2Y8PFCRBfyMU000Q7xVG4Sn

pk_live_kCjJzjlsjrVzxN7wVn84X8zC00LHDvjKmL

pk_live_n6g2DkfukoaRZDnfoRK1wLXm00SDyGwxtA

pk_live_qF9asPU7ruSHJBHUaBPFvszL

pk_live_zGG4CClrhqn0vXK91t5N5W9Y00krRYyz9j

Indicators of Compromise (IOCs): Wallets Addresses


BTC

33MLWAaupK5PUBxgDG6gjNH6Qf4VAtFCia

35XK6WbrL93ACUUwx5mBXZrP6QCg6jomr7

38xSCQFaoSt5qhFGRc5NyHFQQmgZBgtcA5

39kDs8XUEAzwrNrT9iQJqVZKafk96To1Po

3DTpBuuB1gEPm7EfwmWm1YKFfaFYnykAWt

3GHDteZ8LH6wYGrPzoBByEwbVcUbpKkeHU

3Hzi9EJkGEPq2M6Xk6kjthRJT99qM9t64F

3N58EQPyTVPj9fWcyBxEsFUEfnU72CxRho

3NMvSSB1Cju2tKBJVcE6JBMARaHUTsxv3W

3PFZG97VbmmJcuGz3GN1j7C5b2TR9o597S

ETH

0x1700714C02789a6C7AB2857BFaEb68D6B129b768

0x26d3fe121627bEe4a5080b3d603D041AE5664D2a

0x2b673A5BDa1f036749F66A09505DbB6dbafEf690

0x6a119c4F155667119b9aF705F8DeaCad2bffC200

0x893B0B82294432c003cFe4DBd80781039C75d082

0x8E6D26c16909D469A2d21AA0F1F37E360Bb705aa

0x952bc2A8C0Df54f6759623B14Cf421686C27ff70

0xB6F5Fd8f898DEA61CAFACdB9FF093F9C06E6C204

0xE3D24E5b326dfee08C652ee0030B303D22C44691

0xF555ad1446D9E54D4caA4cd64AC1aC54a70BBCf0

XMR

44iQSvdhaFHTSZkVwW4oxbdZjZBieEBjPJbjopgmfSozDBPpMipBeCoftyUhc8G7tdNd1H4Tfa4UqSH8wunh2B5zSmt9x5D

45WR5Fo5z66JhZd8GuRsu1Uci7tBnPCWBH2CDk9ya5Pjdb4gFRzW6z3aw83JQzdmpw3Ly7SqJW6ScedpiaLeQexfQgTEmEZ

473HYYUMBT3JJ4fBi4Q33UWqyELj77nxGKZb8FdrC7MBPvjDgERsqXUFhuBqjoRs9KUrLeXrxK4uJK5f1LAx1C5BG2Net6i

47RVsuruym44SaTZwdp5fRLcCuWcGd8pp3gmMQM61QGjCgoD5VewCwvPH3yGh2if21NPs2DaHP1wUCvLJkhNLgaKFxUHTLX

48XBaRawyzA9jY3hsbm5pGdVxkNV1zytJ1cozWePCvpS5v4ndc9Nf6J3dFihNgyxNH2qTWfVbZ75q5swnTBhZQ5xVNvSgC5

49X3qTsXHfza54tjwzYFFJJJyedAqxCohNTxUtUNHsEnJMsc9f6MG9CbWU96mevFm9iwQ1MuLzC2nfqCc8oV8nuJ4Arcrsz

49eDKoKnnAGXqKV6LdEZMAfkHbAqGq6mKc1PEAVUYFRuKEBeHnabbxKDfzJzDmQXUuNe1dG6vJuuXLTaTou325unNaL9TaF

49pSaeq6QZahprKHnQRkfC5zDZcYStoE2VMpkk3UBkMdRP5JSQsKdmiV7Qio9rcqXdW23mJkSty3FXGTS3rw5aVTSMbcgKA

4A6SWsBAK7XTcoYDFiJZQpLZtDfsjeTUgC6G6QWmzL82d3bcLB15KFVSSHvQgmDY4hX3JUH7uFn4cLwCjR7Msb4G2ccVSWr

4AA5nNfK81jFrD3zbQJYVsdvauFTFrTxu5rnoAu6yrKM5Ci9dYRKk5ahp7WnexPkPremq9ZgurYDtfZByXkVWFsbDWvDEby