Back to Blog
critical SEVERITY5 min read

Silent Code Injection: How Missing Signature Verification Defeats Checksum Security

A critical vulnerability in a Python build script allowed potential man-in-the-middle attackers to bypass SHA256 checksum verification by serving malicious checksums alongside compromised binaries. This fix implements proper cryptographic signature verification, ensuring that downloaded artifacts are genuinely from trusted sources—not just matching a potentially tampered checksum file.

O
By Orbis AppSec
Published May 6, 2026Reviewed June 3, 2026

Answer Summary

This is a checksum verification bypass vulnerability (CWE-347: Improper Verification of Cryptographic Signature) in a Python build script that allowed man-in-the-middle attackers to serve both malicious binaries and matching checksums, defeating SHA256 verification. The fix implements cryptographic signature verification to ensure artifacts are genuinely from trusted sources, not just matching an attacker-controlled checksum file. This prevents supply chain attacks where both the artifact and its checksum are compromised together.

Vulnerability at a Glance

cweCWE-347 (Improper Verification of Cryptographic Signature)
fixImplement GPG signature verification to authenticate artifact source before checksum validation
riskMan-in-the-middle attackers can inject malicious code by serving compromised binaries with matching checksums
languagePython
root causeSHA256 checksum verification without cryptographic signature validation
vulnerabilityChecksum verification bypass via MITM attack

Introduction

In the world of software development, we often trust that the tools and dependencies we download are exactly what they claim to be. But what happens when the very mechanism designed to verify integrity can itself be compromised?

Today, we're examining a critical vulnerability discovered in a CPython build script that highlights a common but dangerous misconception: SHA256 checksums alone do not guarantee authenticity. This vulnerability could have allowed attackers to inject malicious code into developer environments completely undetected.

If you're building software that downloads and verifies external resources, this post is essential reading.

The Vulnerability Explained

What Went Wrong?

The vulnerable code in plugins/python-build/scripts/add_cpython.py followed a seemingly reasonable security practice:

  1. Fetch the CPython binary from a URL derived from the GitHub API
  2. Download a corresponding SHA256 checksum file over HTTPS
  3. Verify the binary matches the checksum
  4. Proceed with installation

On the surface, this looks secure. HTTPS provides transport encryption, and SHA256 is a strong cryptographic hash. So what's the problem?

The Critical Flaw: No Signature Verification

The checksum file itself had no cryptographic signature verification (GPG/PGP). This means the script trusted any checksum file served from the expected URL without verifying it was actually created by the legitimate maintainers.

Think of it this way: you're verifying that a package matches a shipping label, but you never verified that the shipping label itself is authentic.

How Could This Be Exploited?

An attacker performing a man-in-the-middle (MITM) attack or DNS spoofing could:

  1. Intercept the checksum request and serve a malicious checksum file
  2. Intercept the binary download and serve a compromised CPython binary
  3. Ensure the malicious checksum matches the malicious binary

The result? The SHA256 verification passes with flying colors, and the developer unknowingly installs attacker-controlled code.

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│  Developer  │ ──────► │  Attacker   │ ──────► │   GitHub    │
│   Machine   │ ◄────── │   (MITM)    │ ◄────── │   Servers   │
└─────────────┘         └─────────────┘         └─────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │ Serves matching │
                    │ malicious binary│
                    │ + fake checksum │
                    └─────────────────┘

Real-World Impact

This is a supply chain attack vector. The consequences could include:

  • Backdoored Python installations on developer machines
  • Compromised CI/CD pipelines building production software
  • Credential theft from development environments
  • Lateral movement into production systems

Supply chain attacks like SolarWinds and Codecov have shown us that compromising developer tools is one of the most effective ways to breach organizations at scale.

The Fix

What Changed?

The fix implements proper cryptographic signature verification for downloaded artifacts. Instead of trusting any checksum file served over HTTPS, the script now:

  1. Downloads the checksum file
  2. Downloads the corresponding GPG/PGP signature
  3. Verifies the signature against known, trusted public keys
  4. Only then proceeds with the SHA256 verification

The Security Improvement

This creates a chain of trust:

Trusted Public Key (pre-installed)
         │
         ▼
    Signature Verification ────► Proves checksum file is authentic
         │
         ▼
    SHA256 Verification ────► Proves binary matches authentic checksum
         │
         ▼
    Safe Installation

An attacker would now need to compromise the private signing keys of the legitimate maintainers—a significantly higher bar than intercepting network traffic.

Defense in Depth

This fix exemplifies the principle of defense in depth:

Layer Protection
HTTPS Encrypts transport (but can be intercepted)
SHA256 Verifies integrity (but not authenticity)
GPG Signature Verifies authenticity (requires private key)

Each layer addresses different threat vectors, and together they provide robust protection.

Prevention & Best Practices

For Developers Building Download/Verification Systems

  1. Never trust checksums alone — Always implement signature verification for critical downloads

  2. Pin trusted public keys — Include known-good public keys in your codebase or configuration
    python TRUSTED_KEYS = [ "A035C8C19219BA821ECEA86B64E628F8D684696D", # Example key fingerprint ]

  3. Verify the entire chain — Ensure you're checking signatures, not just that a signature exists

  4. Use established libraries — Leverage well-tested libraries like python-gnupg rather than rolling your own verification

Security Recommendations

  • Audit your build scripts — Review any code that downloads external resources
  • Implement SBOM (Software Bill of Materials) — Track all external dependencies
  • Use reproducible builds — Ensure builds can be independently verified
  • Consider binary transparency logs — Services like sigstore provide public verification

Detection Tools and Standards

  • CWE-494: Download of Code Without Integrity Check
  • CWE-345: Insufficient Verification of Data Authenticity
  • OWASP: A08:2021 – Software and Data Integrity Failures
  • SLSA Framework: Supply chain Levels for Software Artifacts

Tools to detect similar issues:
- Static analysis tools with supply chain rules
- Dependency scanning (Snyk, Dependabot)
- Build process auditing

Conclusion

This vulnerability serves as a powerful reminder that security controls must be complete to be effective. A SHA256 checksum without signature verification is like a lock without a door—it looks secure but provides no real protection against determined attackers.

Key Takeaways

  1. Checksums verify integrity, not authenticity — You need both
  2. HTTPS is not enough — Transport security doesn't prevent all MITM scenarios
  3. Supply chain attacks are real — Your build scripts are attack surfaces
  4. Defense in depth matters — Layer your security controls

As developers, we must think like attackers when designing security controls. Ask yourself: "If I controlled the network between my code and the resource it's fetching, what could I do?" If the answer is "serve malicious content," you have work to do.

Stay secure, verify signatures, and never trust the network.


Want to learn more about supply chain security? Check out the SLSA framework and sigstore for modern approaches to software supply chain integrity.

Frequently Asked Questions

What is checksum verification bypass?

Checksum verification bypass occurs when an attacker can manipulate both a downloaded file and its checksum, making malicious content appear legitimate. This happens when checksums are retrieved from the same untrusted source as the file itself, allowing a man-in-the-middle attacker to serve matching pairs of compromised content and checksums.

How do you prevent checksum verification bypass in Python?

Prevent checksum verification bypass by implementing cryptographic signature verification using GPG or similar tools. Verify signatures against a locally stored, trusted public key before validating checksums. This ensures artifacts come from the legitimate publisher, not an attacker who controls both the file and checksum.

What CWE is checksum verification bypass?

Checksum verification bypass is classified as CWE-347 (Improper Verification of Cryptographic Signature). It's related to CWE-494 (Download of Code Without Integrity Check) and falls under the broader category of supply chain security vulnerabilities where code authenticity cannot be verified.

Is HTTPS enough to prevent checksum verification bypass?

No, HTTPS alone is not sufficient. While HTTPS protects against passive eavesdropping, it doesn't prevent attacks where the download server itself is compromised or DNS is hijacked. An attacker controlling the server can serve both malicious binaries and matching checksums over HTTPS. Cryptographic signatures from a trusted key are required.

Can static analysis detect checksum verification bypass?

Yes, static analysis tools can detect missing signature verification in download scripts by identifying patterns where checksums are downloaded and verified without corresponding signature validation. Tools like Semgrep can flag checksum verification logic that lacks cryptographic signature checks against trusted keys.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #3439

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.