
Problema con tough, versioni precedenti alla 0.20.0 (Multiple CVE)
Nel marzo 2025 sono state divulgate multiple vulnerabilità di sicurezza nella libreria Tough di AWS Labs (un client Rust di TUF – The Update Framework). Questi problemi sono tracciati come CVE-2025-2885, CVE-2025-2886, CVE-2025-2887 e CVE-2025-2888 e sono stati corretti nella versione 0.20.0 di Tough. Gli ingegneri della sicurezza possono utilizzare questo documento per comprendere i dettagli tecnici di ciascuna vulnerabilità, la causa principale nel codice e le patch che le risolvono. A tutti gli utenti di Tough < 0.20.0 è fortemente consigliato di aggiornare alla v0.20.0 o successiva.
Tough non riusciva a convalidare in sequenza la versione dei Root metadata durante l'aggiornamento. Un attaccante che controlla un repository (o con capacità man-in-the-middle) poteva fornire un file di root metadata con un numero di versione inaspettato, portando il client a recuperare e considerare attendibile una versione errata. In sostanza, Tough non garantiva che la versione di un nuovo root metadata fosse esattamente superiore di uno rispetto alla versione precedentemente considerata attendibile, violando il requisito di TUF per una catena continua di fiducia. Ciò poteva portare a attacchi di rollback o mix-and-match, in cui un root vecchio (ma firmato correttamente) viene accettato come se fosse l'ultimo, potenzialmente reintroducendo chiavi di firma ritirate o fiducia scaduta.
tough/src/lib.rs. L'implementazione vulnerabile controllava solo che la versione del nuovo root non fosse inferiore a quella del precedente, invece di richiedere che fosse la versione sequenziale successiva. Questa omissione viola la regola della specifica TUF secondo cui i client devono scaricare le versioni intermedie del root in ordine (senza saltare versioni).Implementazione vulnerabile: Nella versione 0.19.x e precedenti, dopo aver scaricato un nuovo root.json, Tough verificava le firme ma non applicava rigorosamente la continuità delle versioni. Consentiva alla nuova versione del root di avere qualsiasi valore >= la versione considerata attendibile. Ad esempio, se il root attualmente considerato attendibile era la versione N, Tough accettava un nuovo root con versione N+2 o superiore (purché le firme fossero valide), saltando la N+1. Lo snippet di codice seguente illustra il controllo precedente alla correzione:```rust
// (Prior to fix) Allow new root version to be >= old version – too permissive
ensure!(root.signed.version <= new_root.signed.version, error::OlderMetadataSnafu {
role: RoleType::Root,
current_version: root.signed.version,
new_version: new_root.signed.version
});
Un attaccante potrebbe sfruttare questo presentando <b>metadata di root con versione superiore che in realtà sono un vecchio set di chiavi</b>. Tough lo accetterebbe, pensando che sia un aggiornamento, e [si fiderebbe di contenuti firmati con chiavi obsolete](https://github.com/advisories/GHSA-5vmp-m5v2-hx47#:~:text=The%20tough%20client%20will%20trust,with%20a%20previous%20root%20role).
<b>Implementazione corretta:</b> Il codice corretto (in Tough 0.20.0) richiede esplicitamente che la versione della nuova root sia esattamente uno in più rispetto alla vecchia versione, vedi [commit 0eeb60a](https://github.com/awslabs/tough/commit/0eeb60aefe27f00b65730634b788a1aafb8bf3c6). Inoltre garantisce che la nuova versione sia più alta (impedisce l'uguaglianza o la diminuzione) e previene qualsiasi salto maggiore di +1:```rust
// (Fixed in v0.20.0) Enforce sequential root version update (new_version == old_version + 1)
ensure!(
root.signed.version < new_root.signed.version &&
root.signed.version.get() + 1 == new_root.signed.version.get(),
error::OlderMetadataSnafu {
role: RoleType::Root,
current_version: root.signed.version,
new_version: new_root.signed.version
}
);
Questa modifica garantisce che il client scarichi e applichi i metadati root in ordine (N, N+1, N+2, ...) senza saltare passaggi. Se viene trovato un file di metadati root con una versione diversa da old_version+1, Tough ora lo considera un potenziale attacco e interrompe l'aggiornamento.
Tough gestiva erroneamente i ruoli di destinazione delegati “terminating” come definiti da TUF. In un repository TUF, una delegazione può essere marcata come terminating, il che significa che se una ricerca di target raggiunge quella delegazione e il target non viene trovato, la ricerca deve fermarsi (e non continuare verso altre delegazioni a priorità inferiore). (vedi qui) A causa di un errore logico, Tough non riusciva a terminare la ricerca in questo caso: continuava a cercare nelle delegazioni successive anche quando avrebbe dovuto fermarsi. Questo potrebbe permettere a un attaccante che controlla una delegazione a priorità inferiore di servire contenuti per target che non dovrebbe controllare, bypassando i confini di fiducia previsti.
tough/src/editor/targets.rs (e il relativo codice di risoluzione delle delegazioni). Il codice vulnerabile non gestiva correttamente il flag terminating sulle delegazioni. Iterando attraverso le delegazioni alla ricerca di un target, Tough passava alla delegazione successiva anche se quella corrente era marcata come terminante e non aveva trovato corrispondenza, contrariamente alle specifiche TUF.