Skip to main content
PyPI Malware Detection Playbook for Python TeamsResearch
4 min readFor Security Engineers

PyPI Malware Detection Playbook for Python Teams

Between July 16 and 22, 2025, three malicious packages—uuid32-utils, colorinal, and termncolor—were on PyPI delivering ZiChatBot malware. They've been removed, but new threats are constantly emerging. If your team pulls from PyPI without automated verification, you're at risk. Here's how to prevent malware from reaching your production environment.

The Threat of Supply Chain Attacks

Supply chain attacks through package repositories are a real and growing threat. ZiChatBot used Zulip APIs as command-and-control infrastructure, turning a legitimate collaboration platform into a backdoor. Your dependency tree likely includes hundreds of PyPI packages. A single compromised package can exfiltrate credentials, establish persistence, or move laterally through your network.

Attackers often use typosquatting—creating packages with names similar to legitimate libraries—or upload seemingly useful packages that contain malicious payloads. Developers install them during routine updates, and the malware executes during installation or runtime. By the time you notice unusual network traffic, the attacker may have been inside for weeks.

Preparing Your Defense

Infrastructure:

  • Set up a private PyPI mirror or artifact repository (Artifactory, Nexus, or AWS CodeArtifact).
  • Implement a CI/CD pipeline with package scanning capabilities.
  • Use a SIEM or log aggregation tool to capture package installation events.

Tools:

  • Use pip-audit for vulnerability scanning.
  • Employ safety for detecting known malicious packages.
  • Utilize bandit for Python static analysis.
  • Cross-reference with osv-scanner against the Open Source Vulnerabilities database.

Access:

  • Ensure admin access to your artifact repository.
  • Be able to modify CI/CD pipeline configurations.
  • Manage network firewall rules to restrict outbound connections.

Baseline Data:

  • Maintain a complete inventory of current Python dependencies across all projects.
  • Create a list of approved PyPI packages from your requirements.txt files.

Implementing Your Defense Strategy

Phase 1: Immediate Defense (Week 1)

Restrict PyPI access at the network level. Configure your firewall to block direct connections to pypi.org from production and development environments. Force all package installations through your internal mirror.

# Add to pip.conf or pip.ini
[global]
index-url = https://your-internal-mirror.company.com/pypi/simple
trusted-host = your-internal-mirror.company.com

Set up your private mirror with a 24-hour delay before packages appear, allowing security tools time to analyze new uploads.

# Example with Artifactory
curl -X PUT "https://artifactory.company.com/api/repositories/pypi-remote" \
  -H "Content-Type: application/json" \
  -d '{
    "retrievalCachePeriodSecs": 86400,
    "missedRetrievalCachePeriodSecs": 7200
  }'

Phase 2: Automated Scanning (Week 2)

Integrate package scanning into your CI/CD pipeline. Every pull request that modifies requirements.txt or pyproject.toml must pass these checks:

# .github/workflows/dependency-scan.yml
name: Dependency Security Scan
on: [pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install scanning tools
        run: |
          pip install pip-audit safety bandit
      - name: Audit dependencies
        run: pip-audit -r requirements.txt --format json --output audit.json
      - name: Check for known malicious packages
        run: safety check --file requirements.txt --json
      - name: Static analysis of new code
        run: bandit -r . -f json -o bandit.json

Set failure thresholds. A package with a critical CVE or flagged as malicious blocks the merge. Medium-severity findings require security team review.

Phase 3: Runtime Monitoring (Week 3)

Deploy runtime monitoring to catch malicious behavior. Focus on network connections that abuse legitimate APIs for command-and-control communication.

Install process monitoring on development and production systems:

# Using osquery for behavior monitoring
osquery> SELECT 
  p.name, 
  p.cmdline, 
  pos.remote_address, 
  pos.remote_port
FROM processes p
JOIN process_open_sockets pos ON p.pid = pos.pid
WHERE p.name LIKE '%python%'
  AND pos.remote_address NOT IN ('10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16');

Alert on Python processes making connections to unexpected domains, especially collaboration platforms or file-sharing services.

Phase 4: Dependency Pinning and Verification (Week 4)

Pin all dependencies to specific versions with hash verification:

# Generate pinned requirements with hashes
pip-compile requirements.in --generate-hashes --output-file requirements.txt

# Result looks like:
# requests==2.31.0 \
#     --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1

This prevents an attacker from uploading a malicious version that your unpinned dependencies would automatically install.

Create a weekly job to check for updates, run them through your scanning pipeline, and generate a pull request for review:

#!/bin/bash
# update-dependencies.sh
pip-compile requirements.in --upgrade --generate-hashes -o requirements-new.txt
pip-audit -r requirements-new.txt
if [ $? -eq 0 ]; then
  git checkout -b dependency-update-$(date +%Y%m%d)
  mv requirements-new.txt requirements.txt
  git add requirements.txt
  git commit -m "Weekly dependency update"
  git push origin HEAD
fi

Validating Your Security Measures

Test your defenses with a controlled experiment:

  1. Create a test package with an intentionally suspicious network connection.
  2. Upload it to your private mirror's staging environment.
  3. Attempt to install it in a development environment.

Your pipeline should:

  • Flag the package during scanning.
  • Block installation if you've configured allowlist-only mode.
  • Generate alerts in your SIEM showing the attempted connection.

Run this test monthly. If the package installs without alerts, your controls have gaps.

Check your dependency inventory against PyPI's malware removal announcements. When PyPI removes malicious packages, verify they're not in your mirror and haven't been installed anywhere:

# Search all environments for specific package
for env in $(find /opt/apps -name "site-packages"); do
  if [ -d "$env/uuid32_utils" ]; then
    echo "FOUND: $env"
  fi
done

Ongoing Maintenance

Daily:

  • Review security alerts from pip-audit and safety scans.
  • Monitor SIEM for Python processes with unexpected network connections.

Weekly:

  • Update your scanning tools (pip install --upgrade pip-audit safety bandit).
  • Review and merge dependency update pull requests.
  • Check PyPI security advisories and cross-reference against your inventory.

Monthly:

  • Test your detection controls with a simulated malicious package.
  • Audit your approved package list—remove unused dependencies.
  • Review network connection patterns from Python processes for anomalies.

Quarterly:

  • Conduct a full dependency tree analysis to identify transitive dependencies.
  • Update your incident response playbook with recent supply chain attack patterns.
  • Review and adjust your private mirror's cache delay based on threat intelligence.

The uuid32-utils, colorinal, and termncolor packages are gone. The next threats are being uploaded right now. Your defense needs to be operational before your developers run pip install tomorrow morning.

Topics:Research

You Might Also Like