Skip to content

Back to the catalog

chainguard-dev/malcontent
github.comrepo

chainguard-dev/malcontent

malcontent is Chainguard's open-source malware/supply-chain discovery tool that analyzes binaries (ELF, Mach-O, PE, a.out) and scripting-language source across Linux, macOS and Windows using 14,500+ YARA rules (including third-party rules from Avast, Elastic, FireEye, Mandiant, Nextron and ReversingLabs). Its standout feature is differential analysis ("mal diff"), which compares two versions of a build artifact or OCI image against a known-good baseline to surface newly introduced risky behaviors — the technique the project demonstrates on the 3CX supply-chain compromise by flagging unexpected high-risk changes to libffmpeg. It also supports threshold-based scanning ("mal scan") and unfiltered capability enumeration ("mal analyze"), and is designed to run inside CI/CD pipelines, including air-gapped environments via embedded rules.

TL;DR Chainguard's open-source malcontent uses 14,500+ YARA rules and differential binary analysis to catch supply-chain compromises like 3CX.
Document

What it is

Operational details for malcontent, Chainguard's YARA-based malware/supply-chain scanner, covering its OCI registry authentication model, a UPX trust-boundary control, build/install steps, and a known blind spot: other scanners flag malcontent itself as malicious.

How it works

OCI image pulls default to anonymous. Authentication is opt-in via --oci-auth, which reads credentials from MALCONTENT_REGISTRY_USER/MALCONTENT_REGISTRY_PASS rather than the ambient Docker keychain (~/.docker/config.json), which malcontent never touches. Credentials are scoped to exactly one registry host: they are sent only when the target image's registry matches MALCONTENT_REGISTRY_HOST, falling back to anonymous pulls for any other registry.

flowchart TD
    A[OCI pull requested] --> B{Registry host matches MALCONTENT_REGISTRY_HOST?}
    B -->|yes| C[Send Basic auth from env vars]
    B -->|no| D[Fall back to anonymous pull]

Separately, malcontent's UPX integration trusts a binary at /usr/bin/upx (or other standard install directories) by default. Pointing it elsewhere via MALCONTENT_UPX_PATH is treated as an explicit trust override that bypasses the directory allowlist — but the tool still enforces that the path is absolute, resolves to a regular executable file, and is not group- or world-writable.

Preconditions / Affected

  • Host normalization matters: Docker Hub's host is index.docker.io, not docker.io — set MALCONTENT_REGISTRY_HOST=index.docker.io or authentication won't trigger.
  • The UPX override is only relevant if you need a non-standard UPX binary location; otherwise the default allowlist applies silently.

What to do

Build from source requires Go, Rust, YARA-X, pkgconf, and libssl-dev:

sudo apt-get install -y pkgconf libssl-dev
make install-yara-x
make out/mal
sudo install out/mal /usr/local/bin

Or pull the prebuilt image:

docker pull cgr.dev/chainguard/malcontent:latest

Caveats

The page states that other YARA-based scanners (e.g., Elastic Agent, per issue #78) have historically flagged malcontent itself as malicious, since YARA tools often contain strings matching the malicious patterns they search for. This is a documented false-positive source when running malcontent alongside other security tooling, not a defect in malcontent's detections.

Technologies named here

Techniques named here

Threat actors named here

  • 3CX Compromise