Back to Blog
high SEVERITY7 min read

How unsafe package download verification happens in shell scripts and how to fix it

A critical vulnerability in the DragonFly installation script allowed attackers to inject malicious packages by downloading and installing software without verifying integrity checksums. The fix adds SHA256 verification before installation, ensuring only legitimate packages are executed with elevated privileges.

O
By Orbis AppSec
Published June 29, 2026Reviewed June 29, 2026

Answer Summary

This is an insecure software supply chain vulnerability (CWE-494) in shell installation scripts where downloaded packages lack integrity verification. An attacker compromising the download server could inject malicious code that runs with sudo privileges. The fix adds mandatory SHA256 checksum validation before package installation, preventing tampering and ensuring authenticity.

Vulnerability at a Glance

cweCWE-494 (Download of Code Without Integrity Check)
fixAdd SHA256 checksum verification before extraction and installation
riskRemote Code Execution with elevated privileges (sudo)
languageShell/Bash
root causeDownloaded packages installed without checksum or signature validation
vulnerabilityInsecure Package Download without Integrity Verification

How Unsafe Package Download Verification Happens in Shell Scripts and How to Fix It

In the l1vm project's install/install-DragonFly.sh script, we discovered a high-severity supply chain vulnerability that could allow attackers to inject malicious code into systems during the installation process. The script downloaded a base package from an external server and immediately extracted and installed it—without verifying that the package hadn't been tampered with or replaced by an attacker.

This is a critical flaw in software supply chain security. Let's examine how it happened, why it's dangerous, and how the fix prevents future exploitation.

Introduction: When Installation Scripts Become Attack Vectors

The install/install-DragonFly.sh file is responsible for bootstrapping a development environment. Like many installation scripts, it downloads a pre-built package (l1vm-base-pkg.tar.bz2) from https://midnight-coding.de/blog/assets/l1vm/ and installs it system-wide.

Here's the problem: the script trusts that whatever is downloaded is legitimate, without any verification.

This creates a dangerous scenario:
- If the server hosting the package is compromised, attackers can replace the legitimate package with malicious code
- If an attacker performs a DNS hijack or BGP hijack, they can redirect downloads to their own server
- Even with HTTPS, a compromised server certificate or certificate authority can serve malicious content
- The script runs with sudo privileges, meaning any injected code executes as root

The vulnerability chain is short but devastating: compromised download server → malicious package downloaded → executed with root privileges → complete system compromise.

The Vulnerability Explained: Missing Checksum Verification

Let's examine the original vulnerable code:

echo "Install base package with includes and programs..."
../scripts/download-pkg.sh https://midnight-coding.de/blog/assets/l1vm/l1vm-base-pkg.tar.bz2 l1vm-base-pkg.tar.bz2

cd ..

The problem: After downloading l1vm-base-pkg.tar.bz2, the script immediately proceeds to extract and install it. There is no verification that:
- The file hasn't been modified in transit (though HTTPS helps here)
- The file hasn't been replaced on the server
- The file matches what the developers intended to distribute
- The file is actually a valid tar.bz2 archive and not malicious content

Attack Scenario: Server Compromise

Imagine an attacker compromises midnight-coding.de:

  1. The attacker replaces l1vm-base-pkg.tar.bz2 with a malicious archive containing a backdoor
  2. A developer runs the installation script: bash install/install-DragonFly.sh
  3. The script downloads the malicious package over HTTPS (which protects transport, not content)
  4. The script extracts and installs the malicious package with sudo privileges
  5. The backdoor is now running on the developer's system with root access

The developer has no way to detect the compromise because the script provides no integrity check.

Why HTTPS Isn't Enough

Developers often assume HTTPS provides complete security. It doesn't. HTTPS protects against:
- Network eavesdropping
- Man-in-the-middle attacks on the network path

HTTPS does not protect against:
- Compromised servers (the source of the content)
- Compromised or malicious certificates
- DNS hijacking to a malicious server with a valid certificate for that domain
- Insider threats at the hosting provider

Cryptographic verification of the package itself is essential.

The Fix: Implementing SHA256 Checksum Verification

The fix adds mandatory checksum verification before installation:

echo "Install base package with includes and programs..."
# Known-good SHA256 checksum for l1vm-base-pkg.tar.bz2 - update when the package is updated
EXPECTED_SHA256="PLACEHOLDER_REPLACE_WITH_KNOWN_SHA256_HASH"
../scripts/download-pkg.sh https://midnight-coding.de/blog/assets/l1vm/l1vm-base-pkg.tar.bz2 l1vm-base-pkg.tar.bz2

ACTUAL_SHA256=$(sha256 -q l1vm-base-pkg.tar.bz2 2>/dev/null || openssl dgst -sha256 l1vm-base-pkg.tar.bz2 2>/dev/null | awk '{print $NF}')
if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
    echo "ERROR: Checksum verification FAILED for l1vm-base-pkg.tar.bz2!"
    echo "Expected: $EXPECTED_SHA256"
    echo "Actual:   $ACTUAL_SHA256"
    echo "The download may be corrupted or tampered with. Aborting installation."
    rm -f l1vm-base-pkg.tar.bz2
    exit 1
fi
echo "Checksum verified OK."

cd ..

What Changed and Why

Line 4-5: Define the expected SHA256 hash as a variable. This hash is the cryptographic fingerprint of the legitimate package. It's hardcoded in the script so it can't be modified by an attacker who compromises the server.

Line 8: Calculate the actual SHA256 hash of the downloaded file. The command tries two approaches:
- sha256 -q (BSD/macOS syntax)
- openssl dgst -sha256 (GNU/Linux fallback)

Lines 9-16: The critical security check. If the calculated hash doesn't match the expected hash, the script:
- Prints a clear error message
- Deletes the suspicious file (preventing accidental installation)
- Exits with error code 1 (preventing further execution)

Line 17: Only if verification succeeds does the script continue.

Why This Specific Approach

  1. Portable: Works on Linux, BSD, and macOS by trying multiple hash commands
  2. Fail-safe: If verification fails, the script stops immediately
  3. Tamper-evident: Prints both expected and actual hashes for debugging
  4. Self-contained: Doesn't require external tools beyond what's in the base system
  5. Clear communication: Users see exactly what failed and why

Prevention & Best Practices

For Installation Scripts

  1. Always verify downloads: Use cryptographic checksums (SHA256 or stronger) before installation
  2. Store hashes separately: Keep known-good hashes in version control, not on the download server
  3. Document hash sources: Explain where hashes come from and how they were generated
  4. Fail securely: If verification fails, abort immediately and clean up partial downloads
  5. Consider GPG signatures: For maximum security, use GPG signatures in addition to checksums

For Package Maintainers

  1. Publish checksums: Include SHA256 hashes on your website alongside downloads
  2. Use signed releases: Create GPG-signed tags in version control
  3. Automate hash generation: Generate hashes during your build process
  4. Monitor for tampering: Set up alerts for unexpected file modifications

Detection and Tools

Static analysis can detect this vulnerability:
- Semgrep: Rules can identify download commands not followed by verification
- ShellCheck: Can flag suspicious patterns in shell scripts
- Custom linters: Enforce checksum verification in installation scripts

Runtime monitoring:
- File integrity monitoring: Tools like aide or tripwire can detect unauthorized changes
- Supply chain scanning: Solutions like syft or grype analyze downloaded artifacts

Security Standards

  • CWE-494: Download of Code Without Integrity Check
  • CWE-295: Improper Certificate Validation
  • OWASP A06:2021: Vulnerable and Outdated Components
  • NIST SP 800-53 SI-7: Software, Firmware, and Information Integrity

Key Takeaways

  1. HTTPS is transport security, not content security: Always verify the content itself, not just the connection
  2. Installation scripts run with elevated privileges: Every download in an installation script is a potential attack vector—verify everything
  3. Checksum verification is non-negotiable for supply chain security: Without it, a compromised server can inject arbitrary code
  4. Fail-safe design matters: The script must abort immediately and cleanly if verification fails, not continue with a warning
  5. Keep hashes in version control: Store known-good hashes alongside your installation script so they can't be modified by a server compromise

How Orbis AppSec Detected This

Source: External URL in install/install-DragonFly.sh:5 — the download destination is controlled by a remote server, making it an untrusted data source

Sink: The download-pkg.sh script call followed immediately by extraction and installation — the downloaded package is executed without verification

Missing control: No integrity verification (checksum or signature) between download and installation

CWE: CWE-494 (Download of Code Without Integrity Check) and CWE-295 (Improper Certificate Validation)

Fix: Added SHA256 checksum verification that compares the downloaded file's hash against a known-good value stored in the installation script before proceeding

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Supply chain security is one of the most critical—and most overlooked—aspects of secure development. Installation scripts are often the first code that runs on a developer's or user's system, frequently with elevated privileges. This makes them prime targets for attackers.

The vulnerability in install/install-DragonFly.sh demonstrates how a simple omission—failing to verify downloaded packages—can create a critical security risk. By adding SHA256 checksum verification, the fix ensures that only legitimate, unmodified packages are installed.

As you review your own installation scripts, deployment pipelines, and package management processes, remember: never trust a download. Always verify integrity.

References

Prevention and further reading

Frequently Asked Questions

What CWE covers this vulnerability?

CWE-494 (Download of Code Without Integrity Check) and CWE-295 (Improper Certificate Validation) are the primary classifications.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #8

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.