Attacking the Supply Chain: Dependency Confusion

Introduction – Package Dependency

 

Modern web applications are typically built using a combination of in-house custom code and third-party libraries. The in-house code leverages functionality from typically open-source libraries that provide convenient access in the chosen development language to common functions (such as email sending or data structure access). These libraries will typically be deployed to the webserver serving the web application along with the in-house code.

The packages are most commonly downloaded from public registries such as NPM, Maven Central, Packagist, and Python Package Index, taking advantage of the open-source ecosystem. It is also common for organisations to utilise private registries to mirror the public index or distribute internal packages which cannot be published publicly. To accommodate this, package management tools such as npm, yarn, pip, maven, or composer allow specifying multiple sources from which to download components.

 

Supply Chain Attacks

 

Relying on these public feeds introduces a vector for so-called supply chain attacks which can result in remote code execution if installed dependencies contain malicious code. This is because the included libraries execute within the same trusted context as the in-house code that loads them, or within the build context when installing the dependency.

 

Attack Vectors

 

A simple example of an attack vector on a package supply chain would be a malicious actor publishing a package with a name that is close to a legitimate package (“typo squatting”) and waiting for a developer to accidentally specify the malicious package name rather than the intended legitimate package.

Alternatively, an attacker may be able to infect a legitimate package for example by submitting pull request to the maintainer that contains malicious code while appearing to be benign, or compromising the build process for the legitimate package.

 

Supply Chain Substitution

 

Recently Alex Birsan published an article highlighting the behaviour of some package managers when selecting which source to use when a package with the same name is available from both a private and a public registry. His research showed that npm, gems, and pip (and potentially other package managers) would select the package with the highest version number (as opposed to defaulting to the private registry as may be intended). This can lead to a critical security vulnerability when the package name is unregistered on the public feed since an attacker can register the name along with a very high version number, which causes the package manager to select the malicious package from the public registry rather than the legitimate package (with a lower version number) from a private registry. Alex has dubbed this issue Dependency Confusion, however you may see it referred to as Supply Chain Substitution Attacks.

 

Package Name Discovery

 

To exploit dependency confusion via this vector, an attacker must discover or guess a package name that is used internally by an organisation, but which is unregistered to date on the public registry. One method of accomplishing this for NPM is to analyse the package.json file if it is disclosed by the application (this is often accidentally published to the web server when deploying an application in what might appear to be a relatively benign information disclosure vulnerability).

A package.json file looks something like this (truncated for brevity):

 

{

  “name”: “myPackage”,

  “version”: “1.12.2”,

  “description”: “My package that does useful things.”,

  “main”: “index.js”,

  “repository”: “github:appcheck-ng/example”,

  “engines”: {

    “node”: “>=6.4.0”

  },

  “scripts”: {

    “unit”: “node test/test.js”,

    “install”: “node install.js”,

  },

  “author”: “AppCheck”,

  “license”: “Apache-2.0”,

  “dependencies”: {

    “debug”: “^4.1.0”,

    “ws”: “^6.1.0”

  },

  “devDependencies”: {

    “@types/debug”: “0.0.31”,

    “typescript”: “3.2.2”

  },

  }

}

 

The package.json file lists packages in three different potential keys: dependencies, devDependencies, and suggests.  The packages names can then be checked against the public npm registry to identify unregistered packages, or scopes (note that an attacker cannot publish a package under a registered scope they do not control even if the package under that scope does not exist).

Another location from which package names can be retrieved is source map files. Source map files are useful for debugging client-side JavaScript within the browser, mapping the minified version of JavaScript typically served to the end user (for file transfer/bandwith optimisation) to the original version. The source map file location is embedded within the JavaScript file, HTTP response header, or follows a naming convention (“.map” added to the JavaScript filename), and can therefore be retrieved by an attacker if it is served by the webserver. Within the source map files is a list of JavaScript files included within the bundle: this includes node packages which are typically easily identifiable as the source has “node_modules” in it. The package names can be extracted and checked against the public npm registry to identify unregistered packages or scopes.

An example of a source map file (truncated for brevity) is shown below:

 

{

    “version”: 3,

    “sources”: [

        “../../node_modules/react/index.js”,

        “../../../packages/react-router/index.tsx”,

        “../../node_modules/@babel/runtime/helpers/esm/extends.js”,

        …

    ],

    “names”: [

        “module”,

        “exports”,

        “require”,

        “invariant”,

        …

     ],

     “mappings”: “;8FAGEA,EAAOC,QAAUC,…”,

    “file”: “static/js/2.cb58acbf.chunk.js”,

    “sourcesContent”: [“’use strict’;\n\nif (process.env.NODE_ENV === ‘production’) …”],

    “sourceRoot”: “”

}

 

Composer is a package manager for PHP which uses the composer.json file. It has several mitigations in place to help protect against Dependency Confusion attacks as described in their article: https://blog.packagist.com/preventing-dependency-hijacking/. Composer strictly enforces a vendor prefix for all package names which is restricted to the first maintainer to publish a package using that prefix on packagist.org. Additionally, if a repository is configured for a package, it is treated as canonical, preventing the public registry being searched.

However, errors in configuration may allow successful attacks if the vendor prefix has not been claimed: for example, if package name is not correctly associated with a repository (such as a spelling error in the package name) an attacker could publish a package which would be used by the misconfigured system. A vulnerable composer.json file might look like the following (truncated for brevity):

 

{

    “name”: “myPackage”,

    “type”: “package”,

    “description”: “My package that does useful things.”,

    “require”: {

        “appcheck/somepackage”: “1.0.*”

    }

    “repositories”: [

        {

            “type”: “package”,

            “package”: {

                “name”: “appcheck-ng/somepackage”,

                “version”: “1.0.5”,

                “dist”: {…},

                “source”: {…},

        }

   ]

}

 

Defence against Dependency Confusion attacks

 

Guarding against Dependency Confusion attacks requires a multi-layer approach.

 

TIP 1 – Reference one private registry, not multiple. Many package managers do not enforce order or priority when querying multiple feeds, for these package managers a single private registry should be configured. Note this may require pushing public packages to the private feed. Ensure the private feed is configured to prevent public packages overriding private packages.

 

TIP 2 – Claim private package names on the public registry to prevent them being hijacked.

 

TIP 3 – Some package managers support controlled scopes, namespaces, or prefixes which can be used with packages you control to protect against an attacker hijacking a name that you use privately, and provide confidence that any packages you release publicly are legitimate. NPM allows you to configure a scope prefix in combination with a registry, since only the configured registry will be searched for that scope this prevents a substitution attack via the public registry. Extreme care must be taken to ensure that this configuration is implemented on all clients that use the package.json file. Composer strictly enforces a vendor prefix in all package names and allows a repository to be configured which is treated as canonical (preventing substitution attacks). However, care must be taken to ensure the repository is configured to prevent Composer falling back to packagist.org (the default public registry) where an attacker may be able to publish a package with the vendor prefix. Note that packagist.org restricts access to the vendor prefix to the first maintainer to publish a package, therefore you may wish to publish a dummy package to any vendor prefix you utilise internally to prevent exploitation in the event of misconfiguration of the composer.json file.

 

TIP 4 – Since the substitution attack relies on the package manager selecting the package with the highest version, another potential mitigation is to explicitly pin the dependency version rather than specifying an open range.

 

TIP 5 – Although so-called security through obscurity is not a robust mitigation, consider whether files such as package.json, composer.json, and source map files must be served by the application, and if they must, whether access to them should be restricted.

 

How can AppCheck Help?

 

AppCheck can help you with providing assurance in your entire organisation’s security footprint. AppCheck performs comprehensive checks for a massive range of web application vulnerabilities from first principle to detect vulnerabilities in in-house application code.

AppCheck identifies package.json, composer.json, and source map files, and will now also automatically identify and report any unregistered scopes/vendor prefix or packages.

AppCheck also draws on checks for known infrastructure vulnerabilities in vendor devices and code from a large database of known and published CVEs. The AppCheck Vulnerability Analysis Engine provides detailed rationale behind each finding including a custom narrative to explain the detection methodology, verbose technical detail, and proof-of-concept evidence through safe exploitation.

 

About AppCheck

 

AppCheck is a software security vendor based in the UK, offering a leading security scanning platform that automates the discovery of security flaws within organisations websites, applications, network, and cloud infrastructure.

As always, if you require any more information on this topic or want to see what unexpected vulnerabilities AppCheck can pick up in your website and applications then please get in contact with us: info@appcheck-ng.com

Get started with Appcheck

No software to download or install.
Contact us or call us 0113 887 8380

Start your free trial