Skip to content

Instantly share code, notes, and snippets.

@archisgore
Last active February 17, 2021 05:52
Show Gist options
  • Save archisgore/c1141abbd2b52a83548ed8e0509569bf to your computer and use it in GitHub Desktop.
Save archisgore/c1141abbd2b52a83548ed8e0509569bf to your computer and use it in GitHub Desktop.
NPM/Node.js code injection attack

NPM/Node.js recently had a clever, yet simple, code injection attack using "dependency confusion" as the vulnerability. I describe the attack as conducted (simulated, really), and a systemic solution Polyverse has been building for the past two years designed to solve specifically this problem.

A recap of the attack, for baseline:

Node dependencies are specified by name and version but not address/location, i.e., {“sorter”: “1.0”, “binary-search”: “2.0”, “polyverse-billing”: 1.0}.

Notice the last one? It’s intended to be Polyverse internal and contains our proprietary (and sensitive) billing code. Obviously it does not exist on npmjs.com, the public upstream node package repository. It instead comes from a private repository hosted by Polyverse.

In a Sequence Diagram, this is how the flow worked before the attack. Pretty straight-forward.

+---------------+                   +---------------------+ +-------+
| App Server    |                   | PrivateNPMRegistry  | | npmjs |
+---------------+                   +---------------------+ +-------+
        |                                      |                |
        | Give me sorter                       |                |
        |------------------------------------------------------>|
        |                                      |                |
        |                                      |   Here you go! |
        |<------------------------------------------------------|
        |                                      |                |
        | Give me binary-search                |                |
        |------------------------------------------------------>|
        |                                      |                |
        |                                      |   Here you go! |
        |<------------------------------------------------------|
        |                                      |                |
        | Give me polyverse-billing            |                |
        |------------------------------------------------------>|
        |                                      |                |
        |                                      |      Not Found |
        |<------------------------------------------------------|
        |                                      |                |
        | Give me polyverse-billing            |                |
        |------------------------------------->|                |
        |                                      |                |
        |                         Here you go! |                |
        |<-------------------------------------|                |
        |                                      |                |

All an attacker had to do was host a public repository called "polyverse-billing" on npmjs and the flow would go like this:

+---------------+ +---------------------+ +-------+                                +-----------------+
| App Server    | | PrivateNPMRegistry  | | npmjs |                                | MaliciousActor  |
+---------------+ +---------------------+ +-------+                                +-----------------+
        |                    |                |                                             |
        |                    |                |          Upload package 'polyverse-billing' |
        |                    |                |<--------------------------------------------|
        |                    |                |                                             |
        |                    |                | Ok                                          |
        |                    |                |-------------------------------------------->|
        |                    |                |                                             |
        | Give me sorter     |                |                                             |
        |------------------------------------>|                                             |
        |                    |                |                                             |
        |                    |   Here you go! |                                             |
        |<------------------------------------|                                             |
        |                    |                |                                             |
        | Give me binary-search               |                                             |
        |------------------------------------>|                                             |
        |                    |                |                                             |
        |                    |   Here you go! |                                             |
        |<------------------------------------|                                             |
        |                    |                |                                             |
        | Give me polyverse-billing           |                                             |
        |------------------------------------>|                                             |
        |                    |                |                                             |
        |                    |   Here you go! |                                             |
        |<------------------------------------|                                             |
        |                    |                |                                             |

The attacker was able to inject code through a dependency called polyverse-billing that I did not intend.

The fundamental vulnerability: Package identity does not consider package source.

Simply put, if there are two packages originating from two different places:

  1. https://goodpackagesjs.com/sorter AND
  2. https://evilpackagesjs.com/sorter

Once downloaded, either sorter can be substituted for the other and their identity is considered the same.

A necessary step to mitigation

A simple and effective necessary step to ensuring all dependencies are sourced from the expected place is to identify the most restricted location in the dependency chain, and use that and only that as the single source for all dependencies.

What this means is, if any one dependency in the entire tree comes from a private repository (more restricted than a public repository), then ALL dependencies should be served from a Private Repository (and ideally only one private repository.)

An all-private repository closure looks like:

+---------------+                   +---------------------+ +-------+                                +-----------------+
| App Server    |                   | PrivateNPMRegistry  | | npmjs |                                | MaliciousActor  |
+---------------+                   +---------------------+ +-------+                                +-----------------+
        |                                      |                |                                             |
        |                                      |                |          Upload package 'polyverse-billing' |
        |                                      |                |<--------------------------------------------|
        |                                      |                |                                             |
        |                                      |                | Ok                                          |
        |                                      |                |-------------------------------------------->|
        |                                      |                |                                             |
        | Give me sorter                       |                |                                             |
        |------------------------------------->|                |                                             |
        |                                      |                |                                             |
        |                         Here you go! |                |                                             |
        |<-------------------------------------|                |                                             |
        |                                      |                |                                             |
        | Give me binary-search                |                |                                             |
        |------------------------------------->|                |                                             |
        |                                      |                |                                             |
        |                         Here you go! |                |                                             |
        |<-------------------------------------|                |                                             |
        |                                      |                |                                             |
        | Give me polyverse-billing            |                |                                             |
        |------------------------------------->|                |                                             |
        |                                      |                |                                             |
        |                         Here you go! |                |                                             |
        |<-------------------------------------|                |                                             |
        |                                      |                |                                             |

With this model, you are insulated from actions of anyone else, malicious or otherwise. There is only one source of truth and it is the most restrictive of all the original sources (therefore any less restrictive source of truth only becomes that much more controlled.)

This leaves an open question though.

How do I enforce this?

The problem with the model above is that Node itself still doesn't know the difference between:

  1. https://goodpackagesjs.com/sorter AND
  2. https://evilpackagesjs.com/sorter

Code signing does help in transport, but doesn't do much good at execution time. How do you ensure that at the time your application is executing, it isn't executing a sorter it obtained from https://evilpackagesjs.com/sorter?

This is where Polyscripting comes in. Description of Polyscripting is out of scope for this document, but can be found on our website here: https://polyverse.com/products/php-security-polyscripting/

The quick recap on Polyscripting is that it creates a grammar that is unique to your instance, cluster, fleet, team, organization, etc. Packages with Regular Grammar (or any other Polyscripted Grammars) are completely unexecutable on instances with any particular unique Polyscripted Grammar. This enforcement happens in the interpreter itself at the very source of execution. This means that even if a package signature is forged, or encryption is broken or somehow a side-channel transport is used to place it on a host, it remains unexecutable by the authoritative entity meant to execute it.

With Polyscripting it is obvious how such an enforcement would work.

An Organization may have any number of N unique grammars assigned to individual instances. The transformation dictionary and its association known only to the registry. Let's assume for simplicity that there is only one Grammar that is unique across the entire organization.

Now let's see how the code injection flow would work with Polyscripting:

                                      +-----------+                                 +---------------------+ +-------+                                +-----------------+
                                      | AppServer |                                 | PrivateNPMRegistry  | | npmjs |                                | MaliciousActor  |
                                      +-----------+                                 +---------------------+ +-------+                                +-----------------+
                                            |                                                  |                |                                             |
                                            |                                                  |                |          Upload package 'polyverse-patents' |
                                            |                                                  |                |<--------------------------------------------|
                                            |                                                  |                |                                             |
                                            |                                                  |                | Ok                                          |
                                            |                                                  |                |-------------------------------------------->|
                                            |                                                  |                |                                             |
                                            | Give me sorter                                   |                |                                             |
                                            |------------------------------------------------->|                |                                             |
                                            |                                                  |                |                                             |
                                            |            Here you go! (Polyscripted Grammar 1) |                |                                             |
                                            |<-------------------------------------------------|                |                                             |
                                            |                                                  |                |                                             |
                                            | Give me binary-search                            |                |                                             |
                                            |------------------------------------------------->|                |                                             |
                                            |                                                  |                |                                             |
                                            |            Here you go! (Polyscripted Grammar 1) |                |                                             |
                                            |<-------------------------------------------------|                |                                             |
                                            |                                                  |                |                                             |
                                            | Give me polyverse-billing                        |                |                                             |
                                            |------------------------------------------------->|                |                                             |
                                            |                                                  |                |                                             |
                                            |            Here you go! (Polyscripted Grammar 1) |                |                                             |
                                            |<-------------------------------------------------|                |                                             |
                                            |                                                  |                |                                             |
                                            | Give me polyverse-patents                        |                |                                             |
                                            |------------------------------------------------->|                |                                             |
                                            |                                                  |                |                                             |
                                            |                                       Not Found! |                |                                             |
                                            |<-------------------------------------------------|                |                                             |
                                            |                                                  |                |                                             |
                                            | Give me polyverse-patents                        |                |                                             |
                                            |------------------------------------------------------------------>|                                             |
                                            |                                                  |                |                                             |
                                            |                                    Here you go! (Regular Grammar) |                                             |
                                            |<------------------------------------------------------------------|                                             |
------------------------------------------\ |                                                  |                |                                             |
| Unknown syntax for 'polyverse-patents'. |-|                                                  |                |                                             |
| Unable to execute!                      | |                                                  |                |                                             |
|-----------------------------------------| |                                                  |                |                                             |

As you can see the unintended code would never execute. You are assured that the code that executes came from an auditable authorized place, and if it did not, you are assured that it is never executed. Even when someone adds a new package or pulls a package out-of-band.

This is Polyscripting solution to supply chain attacks, especially Dependency Confusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment