Dependency chain abuse, listed among the OWASP Top 10 CI/CD Security Risks, refers to an attacker’s ability to abuse flaws relating to how engineering workstations and build environments fetch code dependencies. Dependency chain abuse results in a malicious package inadvertently being fetched and executed locally when pulled.
Dependency chain abuse is the exploitation of vulnerabilities within a project's dependency chains in a CI/CD (continuous integration and continuous deployment) environment. Also known as CICD-SEC-3 on OWASP’s top 10 list, this type of abuse extends beyond traditional vulnerabilities in dependencies.
CICD-SEC-3 involves the unauthorized or malicious manipulation of software dependencies through various techniques, including the publication of malicious packages in public repositories, targeting popular packages used in CI/CD pipelines.
Dependency chains refer to interconnected networks of software libraries, external packages, and modules on which an application relies to function. In modern microservices-based development where engineers routinely accelerate the build by reusing existing code, it’s common for projects to depend on third-party libraries and open-source packages, as well as custom internal packages.
The dependency chain represents the relationships between external components and the main project. In a given application, one dependency could be used numerous times. A dependency can also have dependencies of its own. More importantly, a package or library within the chain could include a set of vulnerabilities or weaknesses that attackers can exploit.
The advantages of dependency chains, like most new and emerging technologies, come with risks. Developers need to understand the composition of their chains. Because vulnerabilities in one dependency can propagate through the chain, developers should track dependency use and rely on verified sources for all their dependencies and dependency updates.
Effective dependency chains result from effective dependency management practices. DevOps teams implement a solid package management strategy using tools such as npm, PyPI, or language-specific package managers for secure dependency handling. The organization maintains an updated inventory documenting the purpose and functionality of each dependency — including the dependency’s origin, version, and potential vulnerabilities. They also monitor for security advisories and vulnerability disclosures related to their dependencies. Comprehensive risk assessment, version control mechanisms, and secure repository management work together to ensure:
In CI/CD pipelines, dependency chains are integrated into the automated build, test, and deployment processes. The pipeline fetches dependencies from repositories and incorporates them into the application build, establishing consistent and reproducible software releases.
Packages are often fetched using a dedicated client for the associated programming language, typically from a combination of self-managed package repositories, such as Jfrog Artifactory, and language-specific SaaS repositories. Node.js, for example, uses npm and the npm registry, Python’s pip uses PyPI, and Ruby’s gems uses RubyGems. These package managers facilitate the resolution of transitive dependencies, ensuring the deployment artifacts include all required packages and their compatible versions.
But automation involves numerous systems operating in a fast deployment cycle, which has elevated the complexity of managing dependencies and external packages used by self-written code. Today’s reliance on third-party packages demands staunch CI/CD security measures.
Many organizations conduct static analysis of both self-written and third-party code to detect usage of packages with known vulnerabilities. In the context of using dependencies, though, organizations need to address another important set of controls to secure the dependency ecosystem — controls that define how dependencies are pulled.
Inadequate configurations may cause an unsuspecting engineer — or worse, the build system — to download a malicious package, rather than the intended package. The malicious package often executes immediately due to pre-install scripts and similar processes designed to run a package’s code once the package is pulled. The main attack vectors in this context include:
Dependency Confusion
The confusion tactic involves the publication of malicious packages in public repositories with the same names as internal packages. In this type of attack, the bad actor hopes to trick the package manager into fetching the malicious package instead of the intended internal package.
Dependency Hijacking
Attackers gain control of the account of a legitimate package maintainer on a public repository and upload a malicious version of a widely used package in dependency hijacking. Unsuspecting clients who pull the latest version of the package may unknowingly introduce the malicious code into their projects.
Typosquatting
In typosquatting attacks, malicious actors publish packages with names similar to popular packages. The goal is to exploit developers' typographical errors when specifying package names, leading them to unintentionally download the malicious package instead of the authentic one.
Brandjacking
Brandjacking involves publishing malicious packages that mimic the naming conventions or other characteristics of trusted brand packages. By wrongly associating these packages with the trusted brands, attackers attempt to deceive developers into fetching and using them.
The security of the dependency chain is of paramount importance, as an exploit in a single, intricately interconnected component can have a cascading effect that compromises the entire application. When organizations fail to address risks associated with dependency chains, they expose themselves to various threats, such as data breaches, unauthorized access, and the infiltration of critical systems.
By establishing and maintaining secure dependency chains, organizations can mitigate potential risks and vulnerabilities introduced by third-party code and libraries. Doing so involves implementing reliable mechanisms to validate the integrity, authenticity, and reliability of dependencies throughout the development and deployment process.
In addition to the consequences beyond data leakage, dependency chain abuse can introduce long-term risks, as attackers may use the compromised dependency to steal credentials and move laterally from the build server to production environments and into the network. In many cases, malicious packages would continue to maintain the original, safe functionality the user expects, resulting in a lower probability of discovering the advanced and persistent threat.
Impact of Dependency Chain Abuse on a Large-Scale Deployment
Security researcher Alex Birsan demonstrated the severity of dependency chain abuse in a blog post titled Dependency Confusion: How I Hacked into Apple, Microsoft, and Dozens of Other Companies. Birsan executed a supply chain attack by uploading public packages with names matching internal packages used by major organizations. Due to misconfigured build systems, these organizations unknowingly downloaded the malicious packages, compromising their systems' security. The incident exposed the potential risks of relying solely on public package repositories without implementing additional security measures.
How a Data Breach Occurred Due to Dependency Chain Abuse
In another incident, the widely used code coverage tool, Codecov, incurred a data breach impacting thousands of organizations. Attackers exploited a vulnerability in the tool's Docker image build process, injecting malicious code that exfiltrated sensitive credentials, including API tokens and access keys. The compromised Docker image was distributed to users who unknowingly installed it, leading to unauthorized access and data exposure.
Detecting signs of dependency chain abuse can prove challenging, but knowing what to look for can prevent potential abuse.
Sudden Changes in Package Behavior
Watch for unexpected changes in package behavior, such as increased resource usage, unauthorized network connections, or unusual system access requests, as they could signify malicious code within the dependency chain.
Abnormal Package Size or Hash Mismatches
Deviations in package sizes or checksums compared to the expected values can signal potential tampering with dependencies. Such discrepancies could result from unauthorized modifications, the insertion of malicious code, or the replacement of legitimate packages with compromised versions.
Unusual Package Versions
Spotting unexpected or unauthorized package versions in the dependency chain may suggest the existence of malicious or compromised packages.
Discrepancies in Package Names or Sources
Notice anomalies in package names, like slight variations or misspellings, and inconsistencies in package sources compared to trusted repositories, as these may signal potential dependency chain abuse.
Verify and Trust Packages
Exercise caution with dependencies originating from unverified or untrusted repositories or packages with insufficient documentation, poor community support, or suspicious maintainers.
Security teams will choose from a range of mitigation methods depending on the configuration of various language-specific clients and how they use internal proxies and external package repositories. All recommended controls, nonetheless, share the same guiding principles.
Implement Proxy and Internal Repositories
Secure repository management helps prevent unauthorized or malicious packages from entering the dependency chain. Any client pulling code packages should not be allowed to fetch packages directly from the internet or untrusted sources. Instead, the following controls should be implemented:
Verify Package Integrity
Use package integrity verification mechanisms, enabling checksums and cryptographic signatures, to validate the authenticity and integrity of packages during the build and deployment process.
Lock Package Versions
Avoid configuring clients to pull the latest version of a package. Prefer configuring a pre-vetted version or version ranges. Use framework-specific techniques to continuously “lock” the package version required in your organization to a stable and secure version.
Manage Package Scopes
Isolate Installation Scripts
When executing installation scripts, ensure that a separate context without access to secrets and other sensitive resources exists for those scripts.
Include Configuration Files
To override any insecure configuration that may exist on a client fetching the package, ensure that internal projects always contain configuration files of package managers within the project’s code repository.
Protect Internal Project Names
Avoid publishing names of internal projects in public repositories.
Prioritize Detection and Mitigation
Considering the numerous package managers and configurations in use, fully preventing third-party chain abuse is near-impossible. Organizations should prioritize detection, monitoring, and mitigation to ensure they can quickly identify incidents, minimizing potential damage and allowing for a swift response.
Properly harden all relevant systems according to the guidelines under the CICD-SEC-7: Insecure System Configuration risk.
Organizations can significantly reduce the risk of dependency chain abuse and enhance the security of their CI/CD pipelines with a combination of proactive measures, ongoing vigilance, and technology.
Various tools can enhance the security of dependency chains in the CI/CD pipeline. These tools can automate vulnerability scanning, enforce access controls, and alert teams to potential security risks. The section will provide details about how these tools can be integrated into the CI/CD pipeline.
Security Testing
Incorporate security testing into the CI/CD pipeline, including static code analysis testing (SAST), dynamic application security testing (DAST), and software composition analysis (SCA) with the creation and maintenance of a software bill of materials (SBOM).
Automated tests identify potential vulnerabilities and security weaknesses within the dependency chain early in the build process.
Dependency Scanning
Implement automated dependency scanning capabilities that continuously monitor the project's dependencies for known vulnerabilities. These capabilities, particularly when integrated in a CNAPP, alert developers to high-risk components and provide guidance on remediation.
Fuzz Testing
Apply fuzz testing techniques to simulate unexpected inputs and validate the robustness of dependencies. Fuzz testing can help uncover vulnerabilities that may lead to abuse or exploitation of the dependency chain.
Establishing strong security policies is crucial for safeguarding dependency chains. This includes regularly updating and patching dependencies, reviewing and vetting third-party components, and maintaining rigorous access controls. Monitoring these policies to ensure compliance will further reduce the risk of dependency chain abuse.
Implement Robust Dependency Management
Implement reliable dependency management practices by identifying, tracking, and documenting all external packages and libraries in a project. This involves maintaining an updated inventory, comprehending the purpose and functionality of each dependency, and monitoring for security advisories or vulnerability disclosures related to those dependencies.
Assess Risks
Conduct a comprehensive risk assessment to identify potential vulnerabilities within the dependency chain. Evaluate the reputation and security track record of each package, assess the quality of code and documentation, and consider the community support and responsiveness of package maintainers.
Control Versions
Use version control mechanisms to maintain the integrity and stability of the dependency chain. Ensure consistent use of specific dependency versions across development, testing, and production environments. Version control also facilitates quick identification and remediation of security vulnerabilities by enabling efficient and secure updates or patching.