In some cases, the easiest way for threat actors to compromise an application is not finding security flaws within the app itself. Instead, they manipulate the application’s software supply chain through a dependency confusion attack, which substitutes malicious code for legitimate application dependencies.

Here’s a breakdown of what dependency confusion means, how it works, and how to prevent dependency confusion attacks.

In this article:

What is a dependency confusion attack?

A dependency confusion attack is a type of software supply chain attack that deploys malicious code in place of legitimate application dependencies. To explain fully what that means, let’s discuss what application dependencies are and how attackers can exploit them.

What is an application dependency?

A dependency is a third-party component that needs to be present when an application runs. For example, many applications rely on various software libraries, which supply functionality that the applications use but that is not built into the applications themselves. Likewise, an app may require certain modules or frameworks to be present on a host system in order for the app to run.

Application dependencies are typically defined in one of two places. The first is within an application itself. For example, if you write a Python app, you might include code such as the following, which declares a dependency on the math module:

import math

The second common way to specify an application dependency is to include configuration code in the application’s package that specifies other packages that should be installed on the system where the application runs. For instance, the Debian package (a common packaging format on Linux-based operating system) for Firefox includes a file called control whose contents look like this:

Package: firefox

Version: 126.0.1-1

Architecture: amd64

Maintainer: Maintainers of Mozilla-related packages <[email protected]>

Installed-Size: 244175

Depends: libasound2t64 (>= 1.0.16), libatk1.0-0t64 (>= 1.12.4), libc6 (>= 2.38), libcairo-gobject2 (>= 1.10.0), libcairo2 (>= 1.10.0), libdbus-1-3 (>= 1.9.14), libevent-2.1-7t64 (>= 2.1.8-stable), libffi8 (>= 3.4), libfontconfig1 (>= 2.12.6), libfreetype6 (>= 2.3.5), libgcc-s1 (>= 4.2), libgdk-pixbuf-2.0-0 (>= 2.22.0), libglib2.0-0t64 (>= 2.38.0), libgtk-3-0t64 (>= 3.13.7), libnspr4 (>= 2:4.32~), libnss3 (>= 2:3.99~), libpango-1.0-0 (>= 1.14.0), libstdc++6 (>= 13.1), libvpx9 (>= 1.12.0), libx11-6, libx11-xcb1 (>= 2:1.8.7), libxcb-shm0, libxcb1, libxcomposite1 (>= 1:0.4.5), libxdamage1 (>= 1:1.1), libxext6, libxfixes3, libxrandr2 (>= 2:1.4.0), zlib1g (>= 1:, fontconfig, procps, debianutils (>= 1.16)

Recommends: libavcodec60 | libavcodec-extra60 | libavcodec59 | libavcodec-extra59 | libavcodec58 | libavcodec-extra58 | libavcodec57 | libavcodec-extra57 | libavcodec56 | libavcodec-extra56 | libavcodec55 | libavcodec-extra55 | libavcodec54 | libavcodec-extra54 | libavcodec53 | libavcodec-extra53

Suggests: fonts-stix | otf-stix, fonts-lmodern, libgssapi-krb5-2 | libkrb53, libcanberra0, pulseaudio

Breaks: xul-ext-torbutton

Provides: gnome-www-browser, www-browser

Section: web

Priority: optional

Description: Mozilla Firefox web browser

 Firefox is a powerful, extensible web browser with support for modern

 web application technologies.

The strings following the Depends: parameter define names and versions of packages that are automatically installed alongside Firefox, if you install the browser using a standard package management tool.

How can attackers abuse dependencies?

Normally, it’s perfectly fine to install dependencies when installing an application. In fact, if you don’t satisfy an app’s dependencies, the app usually won’t run properly.

However, the security risk surrounding dependencies is that attackers might find ways to trick an application installer or package manager into downloading malicious code instead of legitimate dependencies. When that happens, a dependency confusion attack occurs.

How does a dependency confusion attack work?

Dependency confusion attacks typically involve the following steps:

  1. Attackers inspect dependency configurations for applications and identify dependency packages that they can exploit.
  2. Attackers plant a malicious version of the dependency in a place where application installers or package managers will access it.
  3. When an admin or user installs a vulnerable application, the application installer or package manager installs the malicious code to satisfy its dependencies.

From the perspective of attackers, the most difficult step in this process is number 2: Finding a way to “trick” application installers or package managers into downloading malicious packages in place of legitimate ones. There are multiple ways to do this:

  • Creating malicious software packages and repositories with the same names as those of legitimate ones. This type of attack works in situations where dependency configurations don’t define a complete URL or download path for a dependency, causing the installer to default to using the first dependency package it finds whose name matches the dependency defined by the application.
  • Injecting malicious code into legitimate software packages that satisfy dependencies. For example, attackers could “contribute” malicious code to an open source project. If none of the project’s legitimate developers notice the malicious code, anyone who downloads the package would end up installing the malicious software on their systems alongside the legitimate code.
  • Using typosquatting, which involves planting malicious versions of packages whose names or URLs are misspelled versions of legitimate ones. This method can cause dependency confusion in cases where developers mistype the name or download path for a dependency (hence the term typosquatting), causing an installer to pull the malicious version.
  • Modifying network settings to redirect application installers to malicious repositories. For example, by changing DNS configurations, attackers could point requests to to a site they control. This effectively tricks installers into pulling packages from the malicious site, while it appears to users that they come from GitHub.

The ease of carrying out dependency confusion attacks can vary widely depending on factors like how carefully application installers validate dependency source paths and who creates and maintains the software that satisfies dependencies. A major open source library that has hundreds of active maintainers would be challenging to poison with malicious code, for example. However, an obscure project with little activity could be easier to breach as part of a dependency confusion exploit. 

Real-world examples of dependency confusion PoC

Dependency confusion attacks are far from theoretical. Real-world examples of major dependency confusion incidents include:

Dependency attacks via package hallucination: An emerging trend

More recently, an interesting twist on dependency confusion attacks has emerged among threat actors seeking to take advantage of code produced by generative AI tools. In some cases, that code contains references to dependencies that AI models “hallucinate” – meaning they refer to dependencies that don’t actually exist initially. By creating malicious packages with names that match those of the hallucinated dependencies, attackers can plant malicious code on victims’ systems.

In a 2024 dependency confusion proof-of-concept for this type of exploit, security researchers created a fake package (which didn’t actually contain malicious code, but which easily could have if the researchers had been threat actors) with a name that AI models frequently hallucinated. Over a period of three months, the package was downloaded more than 30,000 times, presumably by unwitting application installers.

Preventing a dependency confusion attack

Given the multiple ways in which dependency confusion attacks can happen, there is no simple solution to blocking them all. Instead, organizations should deploy a variety of defenses to minimize their risk of falling victim to dependency confusion:

  • Scan applications and dependencies comprehensively before installing them to detect malicious code inside the apps. This is important even if the applications appear to originate from legitimate sources, since attackers can sometimes insert malicious code into otherwise legitimate software components, as noted above.
  • Ensure that application installers and package managers check the complete download path when satisfying dependencies. This helps prevent situations where installers default to using the first package they find with a matching name, which could be malicious.
  • Define the versions of dependencies explicitly so that installers download a specific version rather than defaulting to the most recent one.
  • Use signed packages. Signed packages contain digital signatures that verify their sources. This won’t protect against all dependency confusion risks because again, attackers sometimes find ways to compromise legitimate packages. But it will protect against instances where installers download dependencies from illegitimate sources.
  • Only allow installers to download dependencies from registries or other sources that you trust. You can do this by creating an allow list of URLs or repositories that installers can use.
  • Installing network security monitoring tools to detect network-based attacks, such as DNS spoofing, that could lead to dependency confusion.

Reducing dependency confusion risks with Aqua

Dependency confusion is a real threat, and it’s likely only to get worse as generative AI technology creates novel ways to launch dependency confusion attacks.

Aqua can help keep you safe. Using the Aqua platform, businesses can automatically scan all applications and dependencies to detect malicious code. No matter where your dependencies come from, Aqua helps you identify dependency risks before they give attackers access to your systems.

The Cloud Native Experts
"The Cloud Native Experts" at Aqua Security specialize in cloud technology and cybersecurity. They focus on advancing cloud-native applications, offering insights into containers, Kubernetes, and cloud infrastructure. Their work revolves around enhancing security in cloud environments and developing solutions to new challenges.