FlawOpen

flawopen.com/Incidents/xz-utils-backdoor-march-2024

● Xz · CVE-2024-3094 · March 2024 · CVSS 10.0 · Critical
FlawOpen Security Research

XZ Utils supply-chain compromise (CVE-2024-3094)

CVE-2024-3094 affected XZ Utils 5.6.0/5.6.1 tarballs. Exposure depended on distribution builds; identify and replace affected packages.[1], [3], [4], [5], [6]

CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H[9]
💡 Plain English Explainer (ELI5)

Think of a factory that receives a sealed parts kit and assembles it into a finished product. The kit’s public recipe looks normal, but one shipped kit contains an extra concealed component. A factory that builds from the recipe does not receive that component; a factory that uses the sealed kit might. A separately authenticated manifest can detect a substituted kit, but even a valid supplier signature cannot prove an authorized kit is harmless: the compromised XZ kits were official releases. Replace affected packages under distribution guidance; use manifest checks only as a separate control against substitution.[5], [6]

Core Concepts & Subsystem Terms

Release tarball
An archive distributed for a software release. It is a build input and must be verified independently from the repository source it is expected to represent.[3], [4], [6]
Build provenance
Evidence connecting a built artifact to reviewed source, a defined build process, and a trusted release identity. Provenance makes a source-versus-artifact mismatch detectable.[5], [6]
Artifact verification
Checking a release artifact against a trusted signed manifest or independently reproduced digest before allowing it into a build or package pipeline.

Framework mappings

  • CWE reference for the weakness described in this guide: CWE-506

Incident Timeline

24 February 2024

XZ Utils 5.6.0 was released; the project’s later incident statement says its release tarball contained a backdoor.[3], [6]

9 March 2024

XZ Utils 5.6.1 was released; the same incident statement says its release tarball contained a backdoor.[4], [6]

Before 29 March 2024

Freund reported an injected build script in distributed tarballs that was absent from the corresponding upstream source file, after unusual SSH CPU use and Valgrind errors prompted investigation.[5], [6]

29 March 2024

Andres Freund publicly reported the issue.[5]

Step-by-Step Attack Flow

Step 1

A release artifact differed from expected source

Andres Freund reported that an injected build script existed in distributed tarballs but not in the corresponding upstream source file. This is a provenance failure: review of one input did not prove the identity of the other.[5], [6]

Step 2

Build and distribution conditions mattered

The reports describe data being drawn from test .xz files during the affected tarball build. Freund also noted that a build from Git would not inject the code and that the dependency path depended on distribution build choices.[5], [6]

Step 3

Anomalous behavior triggered investigation

Freund reported unusually high SSH CPU use and Valgrind errors. Those observable symptoms prompted investigation of the release artifact and its build behavior rather than acceptance of an unexplained performance regression.[5]

Step 4

Containment begins with package replacement and verification

Treat this as an affected-package and build-provenance incident. Follow distribution guidance and replace affected packages. For future artifacts, verify provenance and signatures: this can detect substitution, but cannot establish that an authorized release is benign.[1], [6]

Source Code: Flaw vs. Secure Implementation

UNPATCHED FLAW
# Accepting a downloaded build input without proving its identity.
from pathlib import Path

def stage_release(artifact: Path, build_inputs: Path) -> None:
    build_inputs.joinpath(artifact.name).write_bytes(artifact.read_bytes())

# A successful copy says nothing about whether this archive matches
# the reviewed release or a trusted publisher manifest.
HARDENED SECURE PATCH
# Illustrative release gate: verify an expected SHA-256 from a trusted manifest.
import hashlib
import hmac
from pathlib import Path

def verify_release(artifact: Path, expected_sha256: str) -> None:
    digest = hashlib.sha256(artifact.read_bytes()).hexdigest()
    if not hmac.compare_digest(digest, expected_sha256.lower()):
        raise ValueError("release artifact does not match the trusted manifest")

# Obtain expected_sha256 through a separately authenticated release process,
# then call verify_release before the archive becomes a build input.

# This catches substitution, not a malicious but authorized release; follow vendor guidance for affected packages.

Engineering & System Hardening Checklist

Sources