Improper timestamp caching during snapshot rollback in tough
Description
During a snapshot rollback, the client incorrectly caches the timestamp metadata. If the client checks the cache when attempting to perform the next update, the update timestamp validation will fail, preventing the next update until the cache is cleared. Users should upgrade to tough version 0.20.0 or later and ensure any forked or derivative code is patched to incorporate the new fixes.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A snapshot rollback in tough versions before 0.20.0 causes the client to incorrectly cache rejected timestamp metadata, blocking future updates until the cache is cleared.
Root
Cause
CVE-2025-2888 is an issue in the tough Rust client library for TUF repositories where, during a snapshot rollback, the client incorrectly caches the timestamp metadata even after correctly rejecting the rollback [1][2]. According to the advisory and commit details, the library failed to properly invalidate or clear the cached timestamp when a rollback was detected, meaning the stale metadata persisted in the cache [3].
Exploitation
Preconditions
The vulnerability triggers during a normal snapshot rollback scenario. An attacker capable of performing a rollback attack could supply an older, validly-signed timestamp metadata file. The client correctly rejects the rollback but then caches the rejected metadata. When the client subsequently checks this cache on the next update cycle, the timestamp version validation fails because the cached version is now considered older than the trusted version, preventing the client from consuming any valid updates [1][2]. No additional authentication or network position is required beyond what is typical for a TUF client; the flaw exists entirely in the client-side cache logic.
Impact
An attacker can exploit this to effectively freeze a client by denying it the ability to process any future updates. This denial of service (DoS) prevents the client from applying security patches or other critical updates, leaving the system in a known, potentially vulnerable state until the cache is manually cleared [1][2][4]. The issue does not allow arbitrary code execution or direct data compromise, but it undermines the primary security guarantee of a secure update system.
Mitigation
AWS and the tough maintainers have released a fix in version 0.20.0 [2][4]. The commit at [3] adds explicit checks for rollback detection in the timestamp metadata, including validation that the timestamp meta contains exactly one entry (snapshot.json) and proper version comparison before caching. Users must upgrade to tough >=0.20.0 and ensure any forked or derivative code incorporates the same fixes [1][2][4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
toughcrates.io | < 0.20.0 | 0.20.0 |
Affected products
3- AWS/toughv5Range: 0.1.0
Patches
19b400e1c8b7dtough: detect rollback in timestamp metafiles
2 files changed · +71 −2
tough/src/error.rs+28 −0 modified@@ -256,6 +256,34 @@ pub enum Error { backtrace: Backtrace, }, + /// A timestamp metadata file must contain exactly one entry + #[snafu(display( + "Timestamp version {} meta length {} is not exactly one", + version, + meta_length + ))] + TimestampMetaLength { version: u64, meta_length: usize }, + + /// A timestamp metadata file must contain a meta entry for snapshot.json + #[snafu(display("No snapshot meta in timestamp.json version {}", version))] + MissingSnapshotMeta { version: u64 }, + + /// The snapshot version in a newer timestamp metadata file must be greater than + /// or equal to the version in an older timestamp. + #[snafu(display( + "Snapshot version {} in timestamp {} is less than {} in timestamp {}", + snapshot_new, + timestamp_new, + snapshot_old, + timestamp_old + ))] + OlderSnapshotInTimestamp { + snapshot_new: u64, + timestamp_new: u64, + snapshot_old: u64, + timestamp_old: u64, + }, + /// The library failed to parse a metadata file, either because it was not valid JSON or it did /// not conform to the expected schema. ///
tough/src/lib.rs+43 −2 modified@@ -883,7 +883,7 @@ async fn load_timestamp( role: RoleType::Timestamp, })?; - // 2.1. Check signatures. The new timestamp metadata file must have been signed by a threshold + // 5.4.2. Check signatures. The new timestamp metadata file must have been signed by a threshold // of keys specified in the trusted root metadata file. If the new timestamp metadata file is // not properly signed, discard it, abort the update cycle, and report the signature failure. root.signed @@ -892,7 +892,22 @@ async fn load_timestamp( role: RoleType::Timestamp, })?; - // 2.2. Check for a rollback attack. The version number of the trusted timestamp metadata file, + // 4.6. The meta component must contain exactly one entry, snapshot.json + ensure!( + timestamp.signed.meta.len() == 1, + error::TimestampMetaLengthSnafu { + version: timestamp.signed.version, + meta_length: timestamp.signed.meta.len(), + } + ); + let snapshot_meta = timestamp.signed.meta.get("snapshot.json"); + ensure!( + snapshot_meta.is_some(), + error::MissingSnapshotMetaSnafu { + version: timestamp.signed.version, + } + ); + // 5.4.3.1. Check for a rollback attack. The version number of the trusted timestamp metadata file, // if any, must be less than or equal to the version number of the new timestamp metadata // file. If the new timestamp metadata file is older than the trusted timestamp metadata // file, discard it, abort the update cycle, and report the potential rollback attack. @@ -910,6 +925,32 @@ async fn load_timestamp( new_version: timestamp.signed.version } ); + // 4.6 trusted timestamp meta must have one entry, snapshot.json + ensure!( + old_timestamp.signed.meta.len() == 1, + error::TimestampMetaLengthSnafu { + version: old_timestamp.signed.version, + meta_length: old_timestamp.signed.meta.len(), + } + ); + let old_snapshot_meta = old_timestamp.signed.meta.get("snapshot.json"); + ensure!( + old_snapshot_meta.is_some(), + error::MissingSnapshotMetaSnafu { + version: old_timestamp.signed.version, + } + ); + // 5.4.3.2 trusted snapshot version less than or equal to new snapshot version + // (rollback attack to fetch older snapshot object) + ensure!( + old_snapshot_meta.unwrap().version <= snapshot_meta.unwrap().version, + error::OlderSnapshotInTimestampSnafu { + snapshot_new: snapshot_meta.unwrap().version, + timestamp_new: timestamp.signed.version, + snapshot_old: old_snapshot_meta.unwrap().version, + timestamp_old: old_timestamp.signed.version, + } + ); } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/awslabs/tough/releases/tag/tough-v0.20.0ghsapatchWEB
- aws.amazon.com/security/security-bulletins/AWS-2025-007/mitrevendor-advisory
- github.com/advisories/GHSA-76g3-38jv-wxh4ghsaADVISORY
- github.com/awslabs/tough/security/advisories/GHSA-76g3-38jv-wxh4ghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2025-2888ghsaADVISORY
- aws.amazon.com/security/security-bulletins/AWS-2025-007ghsaWEB
- github.com/awslabs/tough/commit/9b400e1c8b7d6b9ab8009104fa7fe5884db05f18ghsaWEB
News mentions
0No linked articles in our index yet.