Back to Blog

Approved-App Binary Integrity: A Hash Ledger with Trust On First Use

Christopher 8 min read
securityintegritysupply-chaincross-platform

ET Ducky records the binary hash of every approved application installed across an organization's fleet and raises a detection when the same application version appears on any host with a hash that differs from the one the organization already trusts. This post documents the architecture: what the agent hashes, how the cloud builds the per-organization ledger, how the trusted reference is chosen, what happens when a hash diverges, and the attacks the design does and does not cover.

The feature builds on the software inventory pipeline the agent already runs. It complements the behavioral rule engine, which watches what processes do at runtime; hash integrity watches what is on disk between runs.

What the agent hashes

During the software inventory pass, the agent computes a SHA-256 of each installed application's primary executable and reports it in the inventory snapshot as lowercase hex. On Windows the executable is resolved from the installed-application metadata the collector already gathers.

On Linux the agent covers rpm and dpkg packages, and in both cases it hashes the file on disk rather than reading a digest from the package database. The distinction matters. The rpm database stores the digest recorded at install time, so a binary modified after installation still presents its original clean digest when the database is queried. The dpkg database stores only MD5 sums. Reading either database verifies that the package manager once installed the right bytes, not that the right bytes are still there. The agent instead resolves the package's primary executable from the package file list, restricted to executable files in binary directories and preferring the file whose name matches the package, and hashes that file directly. For an unmodified file the self-computed hash equals the package database digest, so the two sources agree on clean systems.

Snap and Flatpak packages are excluded. Both distribute immutable images with their own signature verification, and their on-disk layout does not map to a single primary binary the way conventional packages do. macOS is not supported by the agent.

Hashing is bounded: at most 500 files are hashed per inventory pass, and files larger than 300 MB are skipped. A cache keyed by path, file size, and modification time avoids re-reading unchanged binaries on later passes; the cache lives in process memory, so an agent restart re-hashes everything once. Failures are non-fatal. A package the agent cannot hash is reported without a hash and takes no part in integrity checking.

The per-organization hash ledger

The cloud maintains a table, AppBinaryHashes, with one row per distinct combination of organization, application name, version, architecture, and SHA-256. Application names are matched case-insensitively. A unique index enforces the one-row-per-combination rule, and a partial unique index enforces that at most one row per (organization, name, version, architecture) group is marked as the trusted reference. Each row records which agent first reported the hash, when it was first seen, and when it was last seen.

Rows are scoped to a single organization, enforced both by explicit filters in the processing code and by a row-level security policy on the table. A hash observed in one organization never informs another organization's ledger or reference selection. This is the same tenant-isolation constraint that shapes the fleet baseline design: cross-customer consensus would produce a better reference signal, and the design rejects it because it requires sharing derived data between tenants.

Processing model

An hourly cloud pass performs the comparison. The pass is leader-elected through a Postgres advisory lock, so exactly one API instance runs it regardless of how many replicas are deployed. Only organizations that maintain an approved-applications list are processed, and only packages whose name matches that list are considered. For each organization the pass loads the newest inventory snapshot per agent, validates each reported hash as 64 hexadecimal characters, and folds the observations into the ledger. An error while processing one organization is logged and does not stop the pass for the others.

Trust on first use and reference election

The trust model is trust on first use. The first time a given application, version, and architecture appears in an organization, its hash becomes the trusted reference for that group. There is no external ground truth: the system does not consult vendor-published hashes or code-signing state. What it verifies is internal consistency, that the binary a host carries today matches the binary the organization first accepted, and that all hosts carrying the same version agree with each other.

Naive trust on first use has an ordering problem. If the reference were simply the first row the pass happened to process, whichever agent sorted first in the database would set organization-wide trust, and a single compromised host could poison the reference for every clean host that reports later. The pass avoids this by electing rather than picking: when a group is seen for the first time, candidate hashes from all agents reporting in the same cycle are collected first, and the reference is the hash reported by the largest number of distinct agents. Ties resolve to the lexicographically smallest hash so that repeated runs produce the same result. A single tampered host in a fleet where several hosts report the application does not win the election.

The election helps only when more than one host reports the application in its first cycle. If exactly one host carries the application when it first appears, that host's hash becomes the reference. That is the irreducible assumption of any trust-on-first-use scheme. Once elected, a reference is stable; later cycles never re-elect it.

Divergence and exactly-once detection

Any observed hash that differs from a group's reference inserts a new ledger row and emits one detection. Deduplication is by row existence: if the (organization, name, version, architecture, hash) row already exists, the divergence has already been reported, and the pass only refreshes the row's last-seen timestamp. A given divergent hash therefore produces exactly one detection no matter how many hosts carry it or how many cycles observe it.

The detection lands in the same table, dashboard stream, and toast pipeline as the behavioral rules, under the rule id approved-app-hash-mismatch. It carries the application name, version, architecture, the observed hash, the reference hash, and the reporting agent.

Shadow mode and enablement

The rule ships disabled by default. While disabled, the system still records every divergence in the ledger and writes the detection at Info severity with a shadow tag, but sends no dashboard toast and fires no automation. An operator can review what would have fired before enabling anything. When the rule is enabled for an organization, detections are written at High severity, pushed to open dashboards, and fire the approved_app_hash_mismatch automation trigger with the application, version, architecture, agent, and both hashes as context.

Divergences recorded during shadow mode are not lost. Each ledger row tracks whether its detection has been emitted in active mode; on the first pass after the rule is enabled, previously shadow-recorded divergences are re-emitted once at High severity. The per-row flag makes the backfill idempotent across passes.

Failure behavior

Insert races, such as a rerun after a crash colliding with the unique index, are absorbed by retrying the batch row by row and dropping only the conflicting rows, so one collision does not discard an organization's cycle. Packages that report no hash, whether from an unsupported source, an oversized file, or a hashing failure on the agent, are skipped. Malformed hashes are ignored. Hosts running an agent version that does not report hashes simply do not participate; nothing errors.

Querying the ledger

The ledger is a queryable dataset. AppBinaryHashes is registered in the reporting engine as a registry table, meaning no time window is applied and the full ledger is visible, and detections are filterable by rule id. A natural-language question such as "apps flagged for wrong hashes in the last 30 days" routes to the detections table filtered on approved-app-hash-mismatch.

What this detects, and what it does not

The design detects a binary that was replaced or modified on disk after installation while its version metadata stayed the same: a tampered file, a repackaged installer, a partial supply-chain swap, or any case where two hosts carry different bytes for the same declared version.

The limitations follow from the design:

Frequently asked questions

What is trust on first use in binary integrity monitoring?

Trust on first use accepts the first observed state as the trusted state and alerts on later deviations from it. It requires no vendor cooperation and no hash feed, and its cost is that the first observation is trusted without verification. This system reduces that cost by electing the reference across all hosts reporting in the first cycle instead of accepting whichever host reports first.

Why hash the file on disk instead of asking the package manager?

Package databases record what was installed, not what is present. The rpm database returns the digest captured at install time, so a file modified afterward still presents a clean digest. dpkg stores only MD5 sums. Hashing the on-disk file is the only way to observe tampering that happened after installation.

What happens when a divergence is found?

A ledger row is written for the new hash and one detection is emitted with the observed and reference hashes attached. With the rule disabled, the detection is recorded at Info severity for review. With the rule enabled, it is High severity, appears as a dashboard toast, and can fire an automation. The same hash never generates a second detection.

ET Ducky

Documentation and pricing are available on this site. Approved-app binary integrity is included with all agent installations; the detection rule is enabled per organization from the dashboard's security policy.

View Pricing