Insights

Utilising Dependency Confusion to Conduct Software Supply Chain Attacks 

Author:

Stephen Devereux

Software supply chain attacks have become much more prevalent in recent years, particularly after the successful attack by “APT29/CozyBear” against the SolarWinds Orion application.  

Malicious threat actors have recognised, if they weren’t already aware, the potential benefits of compromising a trusted dependency or component utilised by many organisations. Successfully exploiting this type of vulnerability can yield substantial rewards relative to the time and effort invested in developing the attack. 

There are many ways in which a software supply chain attack can be conducted, and this insight will explore one of them – utilising Dependency Confusion. 

A brief overview of Dependency Confusion 

Dependency Confusion occurs when a threat actor can execute malicious code on a company’s network by “overriding” privately developed software packages. This is done by leveraging public repositories that host packages with the same name but higher version numbers.  

Modern package management systems for languages such as NPM, Python, and Ruby face complex decisions when installing packages and their dependencies. These systems are designed to choose the best match, often opting for the package with the correct name and the highest version value.  

For various reasons, organisations may choose not to publish their internally developed packages to public repositories, including not providing even placeholder information to secure the package name publicly. As a result, when Continuous Integration/Continuous Deployment (CI/CD) environments are not optimally configured, the risk of dependency confusion becomes a valid attack vector for organisations using these in-house developed packages. 

Key requirements for Dependency Confusion  

The key requirement for conducting this type of attack is to identify the names of the internally developed packages that an organisation uses and to verify that these packages are not already registered in a public repository. While this blog does not delve deeply into the various techniques for gathering such data, we have provided a list below of sources where such information can be obtained:  

  1. package.json – For NPM packages for example, this file will also list dependencies 
  2. JavaScript files – Public client-side JavaScript files can contain require() methods, which will list the names of dependant packages. 
  3. Error messages – Verbose errors in applications can leak the names of used packages. 

Example of Dependency Confusion 

In the following steps, we provide an example of how, under default configurations, dependency confusion can occur. 

In the example below, dummy packages have been created and published to a private repository. We will use the NPM package manager for JavaScript and configure a local repository for NPM to use. 

NPM Package Manager - Pentest Limited

Since this is merely an example, we create the appropriate package.json file for each dummy package. Below we have the package.json file for the dummy package: superlaser. 

Superlaser package json - Pentest Limited

Below we have the package.json file for the dummy package: deathstar-engine. 

Deathstar-engine package json - Pentest Limited

Now we publish the dummy packages to our private repository. 

Private Repository - Superlaser package json - Pentest Limited
Private Repository - Deathstar package json - Pentest Limited

Before installing the packages locally using the NPM package management tool. We go to the NPM public repository, and a package called “superlaser” has been published in the NPM public repository. This package has an identical name to our superlaser package but with a higher version value. 

NPM Repository - Pentest Limited

Now, we install the deathstar-engine package. This has a dependency of superlaser, which we have also added to our private repository. So, we might expect that NPM will now install both of our internally developed packages. 

Install package - Pentest Limited

However, as we can see below, once the installation completes the package management tool has installed the superlaser package from the public NPM repository. If this was a real attack an attacker would have been able to achieve code execution during the package installation process, constituting a serious security breach within the organisation’s infrastructure. 

It is to be noted in this example, the public package was installed as the private repository by default has an “upstream” public NPM repository configured, allowing the NPM package manager to search private and public repositories for packages. Thus, even though we set the NPM package manager tool with a private repository for retrieving packages this was overridden by a missed setting within the private repository’s configuration. 

Package overridden - Pentest Limited

Preventing Dependency Confusion 

Dependency confusion has been a concern for a few years and was first highlighted in research conducted by Alex Birsan, who coined the term. During his research, he demonstrated that CI/CD environments that are not optimally configured for an organisation’s development processes are vulnerable to dependency confusion attacks, affecting several well-known blue-chip organisations. 

Preventing Dependency Confusion attacks can vary depending on the technologies in use. However, there are several general strategies that organisations can implement in their CI/CD environments to help mitigate these risks: 

  1. Package Verification: Verify a package’s authenticity and integrity by comparing the retrieved package against a known checksum/hash.

  2. Utilise Scopes: Scopes help avoid naming collisions between private and public packages. By creating a scope for private packages, developers can ensure packages have unique names and, with the use of internal repositories, are only sourced from these, reducing the likelihood of confusion with public packages.

  3. Private/Internal Repository: By using an internal repository, developers can ensure that all packages used by their applications are retrieved from within the organisation. However, it’s important to note that default configurations for internal repositories still allow access to public repositories. Therefore, if internal repositories are set up with default settings, an organisation may still be vulnerable to dependency confusion attacks.

  4. Configure repositories securely: Internal repositories can be configured to search only private/internal repositories for scoped packages.

Implementing these strategies can significantly enhance the security of an organisation’s development processes.

Could be Dependency Confusion be a danger to your organisation? 

Until it’s confirmed all required security controls are in place, Dependency Confusion is a definite risk.  

If you’re using a language such as Node.JS, Python or Ruby, which make use of package managers for package installation and have developed in-house packages, which have not been published in public repositories, in any part of your web application development, then depending on CI/CD environment configurations, there is a chance that dependency confusion could be utilised by an attacker against your organisation. 

The only way to ensure that you’re not vulnerable is to put your applications to the test and that’s exactly what our web application testing service is designed to do. Whilst not specifically necessary, Dependency Confusion issues are also a good reason for providing source code during testing, as it will allow consultants to confirm issues much faster than without. 

So, if you’re worried about dependency confusion, or wider supply chain attacks for that matter, why not get in touch and see how we can help provide the security assurances you require. 

Looking for more than just a test provider?

Get in touch with our team and find out how our tailored services can provide you with the cybersecurity confidence you need.