
Harvesting credit card numbers and passwords from websites - swyx
https://medium.com/@david.gilbertson/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5
======
btown
One of the biggest problems here is that there is no “chain of custody” from
Github source to uploaded NPM module; otherwise one of the developers using
the malicious package could have audited the source code before including it
in their own code. ‘npm publish’ would ideally insist on reproducible builds,
enforce this by minifying or compiling packages itself, and finally encourage
the community to always audit the code associated with a module. Of course,
people are lazy, NPM has no incentive to incur that server and engineering
overhead, and someone could sneak in code anyways with a minor version
update... There’s no clear solution here, and I think the only thing keeping
up this house of cards is that there are much easier ways for black hats to
make money.

~~~
djsumdog
Even if you had that, no one is going to inspect all that code. npm is a
cluster-farkle of insane amounts of packages.

The whole point of the article is you should implement CSP.

~~~
mulmen
Sure but why not implement CSP and also get your packages from somewhere
trustworthy and audit the code you actually run? Just because "most people are
lazy" doesn't mean you have to be. "Audit the code you run" is still good
advice right?

~~~
JonathonW
I have a fairly simple Node project at work; it pulls in nine runtime
dependencies, plus 13 development-time dependencies (most of those are babel
or eslint-related).

Assuming none of those are pulling shenanigans like mentioned in the article
(distributing different code than in their source repositories, or
deliberately obfuscating malicious code), it's not completely unreasonable for
me to go through and audit my direct dependencies. But, since the Javascript
standard lib is crap, all of my direct dependencies have their own large pile
of dependencies, which themselves depend on a bunch of stuff, and so on.

By the time it's all said and done, my "simple" Node project pulls in several
hundred dependencies (I didn't go through and count, but my 'yarn.lock' on
that project has ~4200 lines). I can't audit all of that code.

(This is particularly bad in Node and Javascript, but applies to other
languages too. I don't think anyone's ever fully audited all of our Nuget
dependencies, or Python dependencies... fortunately, those both tend to be
more self-contained, so at least we know what we're getting there.)

~~~
Tomte
> I have a fairly simple Node project at work; it pulls in nine runtime
> dependencies, plus 13 development-time dependencies (most of those are babel
> or eslint-related).

A well known German blog just claimed that creating a new skeleton project
using @angular/cli results in 31 direct dependencies, almost _a thousand_
dependencies in total and 300 MB code.

That's just wow.

------
jarofgreen
> 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.

Which Time Zone? Hah!

(Not that this one nit pick takes away from the general very well made point
of the article, I just love how TimeZone problems infect _everything_ )

~~~
nikanj
The browser’s? People do tend to keep their PC in the local timezone.

~~~
jarofgreen
I wonder how much pen testing is done by hand and how much by automatic tools?

Anyway, I'm being pedantic. There are a lot of great points in this article.

~~~
ehsankia
Sure, the night build fails, then in the morning the engineer comes in, looks
at it, can't replicate, marks as fixed.

------
jmadsen
I'm more of a back-end dev who doesn't know all the ins and outs of the actual
software used - can someone explain to me why this is an _npm_ problem, and
not an excessive dependencies problem?

I thought npm was simply a package manager - I don't see anything in the
article that is specific to npm, except he happens to say that word.

~~~
milesvp
What makes npm particularly bad, is that JS has such a teribad stdlib. When
you need to write your own lpad() at some point its easier to include a stupid
little package that has dealt with all the edge cases you don't want to care
about. So you end up with way more third party deps than a kitchen sink type
language.

Otherwise, yes this is a fundamental dependency issue.

~~~
swyx
could we actually take a snapshot of npm’s top used but relatively inert
libraries every 6 months or so and freeze them? (call it UserlandJS 2018a,
2018b, etc) and then have a separate dependency manager that only downloads
those frozen libraries we include. Userland Package Manager or something. this
would approach a stdlib without much effort and we would have more of a chance
to catch up on malicious security stuff since there are commonly agreed upon
frozen versions that everyone can pore over.

im a total noob at security, please attack/modify this idea if it has any
value?

~~~
swyx
discussing on twitter here
[https://twitter.com/swyx/status/949946224043610112](https://twitter.com/swyx/status/949946224043610112)

------
spiznnx
The best part is when you get caught, you can just play dumb and say your npm
credentials were compromised (assuming precautions were taken in not using a
collection domain tied to you).

~~~
deckard1
Worse than that, I think. You can claim the npm account that published the
package has zero relation to your github page that hosts the untainted source.

The amount of times I've gone to npmjs.com, looked for the "official" version
of a package only to get lost in a web of conflicting version and ownership
"signals" is staggering. And yet... if it's on npmjs.com, then it's going into
your package.json. No one audits that thing. Unless there is an obvious bloat
issue. And dependencies of dependencies? NEVER. We have something like 400MB
of shit in our node_modules folder. There is no human on earth that could go
through that. This is a few times the size of the _entire_ Linux kernel, for
context. And it's minified.

------
fny
Couldn't one circumvent CSP by sending the data to a legitimate analytics
service that everyone uses like Google Analytics?

~~~
saryant
This is why, when I recently built a credit card form, we didn't include any
trackers or third-party code (beyond our vendor's). No dependencies at all—our
vendor also has no dependencies in the JS we use to implement the CC form.

That did mean no jQuery, no Google Analytics, no NPM modules and we had to
build it as a standalone page outside our React setup, but it's worth it to be
able to definitively inspect every line of code and provide a CSP that locks
that page down tight to just our subdomain and our vendor's.

~~~
cesarb
That's the same as protecting just the login page with https. What prevents
code outside the form page from replacing the link which goes to the form
page?

~~~
martin-adams
Exactly my thought. I guess that would mean it would need some intimate
knowledge of the target site this reducing the scalability of the attack. But
then again, thee are some clever people out there who could automate it.

------
Shoothe
So it seems developers are in fact responsible for dependencies that they
use... Who would've thought...

~~~
dkersten
I was going to say the following:

    
    
        Except that being responsible for your dependencies (and the dependencies of your dependencies...) is impossibly hard. You would need to build everything yourself after auditing the code.
    
    

But then I thought about it some more and its likely that you don’t need to
audit the code, since the malware probably isn’t in the public git repo. Yes,
its still a risk, but the probability of malware is much lower (and at least
you CAN audit the code if you wanted to).

You still need to get the source for all your dependencies and all of their
dependencies and so on and build it yourself, but you should probably do that
anyway and host the artifacts in your own private repo. That’s good practice
and avoids issues like the left-pad thing.

~~~
Silhouette
It's only impossibly hard to audit this kind of thing if you have an insanely
large and deep tree of transitive dependencies in the first place. This seems
to be a particularly bad problem in the JS world, for this and many other
reasons, but most programming languages and their communities don't work that
way. Auditing a small number of larger dependencies, when most of them are
probably widely used and from reasonably trustworthy sources, is much more
achievable.

~~~
dmitriid
The other languages have the same problem. It's only spread over different
places.

For example, JS doesn't have a standard library. Many languages do. So when JS
downloads loads of transitive dependencies, many are there because there's no
stdlib.

Have you audited your C++, or Java, or Ruby, or... stdlib lately? Especially
those that come preinstalled with your OS of choice?

Same goes for anything that you download via Maven, or gems, or easy_install,
or include as direct GitHub references in your Go code.

~~~
Silhouette
Those standard libraries are typically widely used. In many cases, the source
can be examined. In most cases, there's a large professional team at a
reasonably reputable organisation responsible for maintaining them. In almost
all cases, there are no transitive dependencies not managed by the same
people. Once installed on your system, they generally don't change unless you
actively change them. While there is some risk in any dependency (cf.
_Reflections on Trusting Trust_ ) the level of risk is on an entirely
different scale in this sort of situation compared to what much of the JS
world does every day.

~~~
dmitriid
> In many cases, the source can be examined.

Have you examined them, though? ;) And yes, I was thinking about _Reflections
on Trusting Trust_ as well :)

> In almost all cases, there are no transitive dependencies not managed by the
> same people.

That's why I said the risk is spread very differently compared to JS :)

------
danschumann
What if the author snuck his code into frontend modules, and also snuck his
code into backend modules?

If CSP is enabled, the frontend checks to see if the backend code has opened
up the particular port or route on the backend.

The back-end code could sniff through require.cache to see if he could hook
into the existing server instance ( same port ), or open a new port (
depending on CSP ).

I suppose the CSP equivalent on the backend is some sort of firewall. I also
suppose servers have better monitoring of requests. Still, this method would
circumvent CSP!!!

Also, hooking into the existing server instance would throw red flags if the
instance was ever console.logged. You might also do an audit of your ports and
see a suspicious one opened in that method. And a firewall likely would block
other ports. But, still, it's feasible even with a CSP, until discovered.

~~~
saryant
He'd have to somehow make outbound requests from the server. IIRC, the default
AWS VPC config would prevent this. Not sure about other cloud environments.

Where I work, outbound requests must be made through proxy servers which have
a whitelisted set of allowed domains, which is only allowed after a security
review.

~~~
tyingq
You can exfiltrate data a number of ways though. DNS requests might be one
obvious method.

~~~
mwarkentin
AWS has GuardDuty now which can monitor for this type of traffic for pretty
cheap.

------
beams_of_light
Well-written, educational, incites action. Thank you.

------
gcornut
I'm sometimes frustrated by uMatrix blocking every requests to third party
website and forcing me to accept each of them when needed. But when I see
exploits like this I'm happy that I haven't uninstalled it :D. You still have
to be vigilant when whitelisting requests in the uMatrix panel though.

~~~
moreless
Note that uMatrix doesn't protect you (by default) from sending data out.

It also depends on the page you are visiting. If it's just some random post on
net I actually don't care much if they do manage to post data elsewhere (the
risk in that case is actually higher if I allow ajax.googleapis.com than some
random non-google page), but with banking sites and credit card forms you need
to be careful.

------
SuperNinKenDo
Seems particularly prescient given
[https://news.ycombinator.com/item?id=16087024](https://news.ycombinator.com/item?id=16087024)

~~~
noxecanexx
Exactly, most especially after seeing this comment
[https://news.ycombinator.com/item?id=16087079](https://news.ycombinator.com/item?id=16087079)

------
nailer
If this prompts you to action, and you need a quick and efficient way to build
a CSP policy for the various services you use:
[https://www.npmjs.com/package/csp-by-api](https://www.npmjs.com/package/csp-
by-api)

~~~
bdcravens
A bit ironic, given the article is about all the ways you could get screwed by
NPM packages if you don't have a CSP.

~~~
nailer
The source code is thankfully pretty tiny and you're welcome to inspect what
yarn pulls down to make sure I haven't trojaned it!

------
0xmohit

        Amazon has no CSP at all, nor does eBay.

------
systematical
This is a problem for the PHP composer ecosystem as well. I took a look at how
many vendor packages our core has and its about 20...

~~~
mkopinsky
The difference with composer is that in most cases you're getting the exact
version from github, including the full .git directory. No minification, no
"the tarball doesn't match the source". And most composer packages have 0-5
dependencies, whereas most npm packages seem to have 10-20.

But too-many-dependencies is a problem there too.

~~~
slayer_birden
not exactly, actually. composer allows you to pull "dist" version - which is
an archive that can be whatever really. you can of course set "prefer-source",
but I think theoretically you can have a package without source provided at
all.

------
sam_goody
A perfect example of this is the Hot Pockets deal.

Some guy made a cookie session middleware called node-yummy, which eventually
became a dependency of Express. Express has a bajillion downloads, so yummy up
and brokered a deal by which on every install they tweeted a like for Hot
Pockets. So, Hot Pockets really started soaring, with no one having any idea
what they were tweeting by installing and updating Express, until someone
ruined all the fun by posting it on Medium, and getting picked up by HN [2]

IMO the really not acceptable part is that the open souce projects are not
being pulled from Github. When something claims to be open source, we should
have a gaurantee from NPM that we can see the source. The current setup
implies the opposite [1] [https://github.com/defunctzombie/node-
yummy/issues/7](https://github.com/defunctzombie/node-yummy/issues/7) [2]
[https://medium.com/friendship-dot-js/i-peeked-into-my-
node-m...](https://medium.com/friendship-dot-js/i-peeked-into-my-node-modules-
directory-and-you-wont-believe-what-happened-next-b89f63d21558)

------
drdrey
Reminds me of Writing Worms for Fun and Profit by lcamtuf:

[https://www.darknet.org.uk/2006/12/writing-worms-for-fun-
or-...](https://www.darknet.org.uk/2006/12/writing-worms-for-fun-or-profit/)

------
macawfish
I recently had a problem with a package that doesn't have its npm releases
tagged in git. It surprised me how hard it was to figure out which git
revisions corresponded to specific npm releases. I had tracked down the npm
version of the package that introduced the bug I was trying to fix, but
without getting in and doing some real diffing, I couldn't figure out which
commit introduced the bug.

At the time, I'd never added anything to npm, but since then, I have. It dawns
on me that npm versions aren't tied to git revisions at all!

~~~
rmrfrmrf
Yep, only authors who are good about using `npm version` _and_ pushing the
resulting git tag to origin are going to have the proper linkage.

------
z3t4
There is this trade-off between usability and security. For example being able
to load data from other domains, now with origin policy we have do fetch the
data server side. One nice thing about web apps is that they do not require a
server to work. But due to xxs injections we cant have nice things.

~~~
mulmen
It's almost like the browser is being abused to do something it fundamentally
should not be doing. If you need to do that much client side heavy lifting
maybe a web browser is not the place for it?

The ship has probably sailed on that line of thinking but in my opinion a lot
of the pain we experience in web security today comes from people trying to do
things they really should not be doing.

~~~
z3t4
There's this vision for the web that we should be able to access public _"
data"_, not only web _sites_ "guarding" it.

~~~
mulmen
Maybe you and I have different definitions of _Internet_ and _web_ then. You
can still access data without javascript or webapps.

~~~
z3t4
Accessing data either require's identification to a database or you access the
data over the web/http. Do you have any examples where you can access public
data without logging in ? I can only think of FTP servers that lets you access
anonymously ...

------
shaneos
while this post is about the more general attack vector, its worth pointing
out that if you use a modern credit card tool like Stripe Elements it is
impossible to steal credit card numbers as they are embedded in an iframe and
your page just gets a token that is meaningless to anyone else

~~~
Qerub
The attacker could inject code that replaces the Stripe integration with
something that looks identical to the end-user but harvests the CCs.

~~~
shaneos
True, but this is easily detected. It’d have to provide tokens to the page,
which would fail 100% of the time when sent to the Stripe API. These types of
attacks are really only effective when difficult to detect, and therefore
allowed to run for more than a very short time

~~~
wahnfrieden
The Stripe frames are set up by JS. What’s stopping an attacker from wrapping
it in their own form, collecting, and having it still submit the details to
Stripe?

~~~
antsar
The attacker's code isn't hosted on Stripe's domain. It could render the real
Stripe iframe, but could not interact with it (ie. populate form values and
submit).

An attacker could try to POST the values directly to Stripe instead, but
Stripe presumably uses CSRF-prevention techniques (eg. a token in the form) to
stop this as well.

------
program_whiz
Easy Solution: on login and payment forms, go to a plain HTML page, use a
standard form to submit without JS (or only JS you write). Forget client-side
validation and/or write it yourself.

~~~
pmlnr
Half the internet could go static html with css animations without an js and
do bacis analytics from server logs. The rest could do webassembly, compiled
from another language, missing and npm-dependecy-hell.

------
bmease
I've gotta post the obligatory Ken Thompson hacking the compiler story

[http://wiki.c2.com/?TheKenThompsonHack](http://wiki.c2.com/?TheKenThompsonHack)

------
MollyR
Wow thats a rough read. Could something similar be done to django sites ?

~~~
rollcat
Yes. There are basically three steps here...

1\. Use social engineering to get your package included as a dependency;

2\. Use obfuscation techniques to hide the real intent of the package;

3\. Capture data and send it off to a remote server.

First can be pulled off, but really depends on the community / maintainers
doing their job right. In case of Django, I imagine that would be pretty hard
- while the codebase is giant, by itself it has no other dependencies (except
stdlib and pytz). Then 3 could be either much easier or much harder on the
backend, depending on how well the box is secured (e.g. outgoing firewall
rules).

However the impact could be much more severe, since you're executing code on
the server - at the very least you can inject any malicious JS you like,
rewrite the CSP headers (if present), just dump the entire DB right away, and
a lot of other bad things.

This basic recipe has a chance to work regardless of the target
language/package manager. It's all up to your dev process and security regime
to catch it.

~~~
joneil
One advantage Python might have is that the dependencies you use are human
readable, they're not compiled, minified or obfuscated. This in theory would
make auditing dependencies easier, but I imagine 99% of the time people aren't
thoroughly auditing.

------
kw71
Yeah, and Call Manager has a bug that lets incoming callers transfer
themselves to intercom. Keep an eye on that Cisco phone on your doctor's desk
while he discusses your HIV test results with you. I could be recording it.

------
sdamodharan
What could be the npm package the author of this article is referring to?

~~~
GoToRO
"This post is entirely fictional, but altogether plausible, and I hope at
least a little educational.

Although this is all made up, it worries me that none of this is hard."

------
codedokode
The author mentions that Chrome Extensions are a bad distribution method. I
think he is wrong. First, there are more users of Chrome Extensions than the
users of npm, second, most of them don't care what those extensions send over
the network. And I guess CSP doesn't apply to browser extensions.

So if you want to steal passwords, make some extension like "Mp3 Youtube
Downloader" or "Ad Blocker" and get access to millions of happy users'
browsers.

CSP looks like an ugly hack rather than a good solution. Why would you need to
specify allowed sources for scripts if you control your HTML code? I don't
understand why the author praises it. What a stupid time-wasting technology.

~~~
prh8
I think you misunderstand the role of NPM in this concept. Using NPM ensures
that web sites distribute the code to their users. So it doesn't matter if
there's only a few hits using NPM, if they are good hits. As the article
mentions, neither Amazon nor eBay are protecting against this. If they somehow
added the package to their app, that provides access to every credit card
added to those sites. That is much broader than user's installing Chrome
extensions.

