|  | 
|  | 1 | +```toml | 
|  | 2 | +[advisory] | 
|  | 3 | +id = "RUSTSEC-0000-0000" | 
|  | 4 | +package = "gix-features" | 
|  | 5 | +date = "2025-04-03" | 
|  | 6 | +url = "https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-2frx-2596-x5r6" | 
|  | 7 | +categories = ["crypto-failure"] | 
|  | 8 | +cvss = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N" | 
|  | 9 | +keywords = ["hash-collision", "sha-1", "weak-hash"] | 
|  | 10 | +aliases = ["CVE-2025-31130", "GHSA-2frx-2596-x5r6"] | 
|  | 11 | +license = "CC0-1.0" | 
|  | 12 | + | 
|  | 13 | +[affected.functions] | 
|  | 14 | +"gix_features::hash::bytes_of_file" = ["< 0.41.0"] | 
|  | 15 | +"gix_features::hash::bytes" = ["< 0.41.0"] | 
|  | 16 | +"gix_features::hash::bytes_with_hasher" = ["< 0.41.0"] | 
|  | 17 | +"gix_features::hash::hasher" = ["< 0.41.0"] | 
|  | 18 | +"gix_features::hash::Hasher::update" = ["< 0.41.0"] | 
|  | 19 | +"gix_features::hash::Hasher::digest" = ["< 0.41.0"] | 
|  | 20 | +"gix_features::hash::Write::new" = ["< 0.41.0"] | 
|  | 21 | +"gix_features::hash::Write::write" = ["< 0.41.0"] | 
|  | 22 | +"gix_features::hash::Write::flush" = ["< 0.41.0"] | 
|  | 23 | + | 
|  | 24 | +[versions] | 
|  | 25 | +patched = [">= 0.41.0"] | 
|  | 26 | +``` | 
|  | 27 | + | 
|  | 28 | +# SHA-1 collision attacks are not detected | 
|  | 29 | + | 
|  | 30 | +### Summary | 
|  | 31 | +gitoxide uses SHA-1 hash implementations without any collision detection, leaving it vulnerable to hash collision attacks. | 
|  | 32 | + | 
|  | 33 | +### Details | 
|  | 34 | +gitoxide uses the `sha1_smol` or `sha1` crate, both of which implement standard SHA-1 without any mitigations for collision attacks. This means that two distinct Git objects with colliding SHA-1 hashes would break the Git object model and integrity checks when used with gitoxide. | 
|  | 35 | + | 
|  | 36 | +The SHA-1 function is considered cryptographically insecure. However, in the wake of the SHAttered attacks, this issue was mitigated in Git 2.13.0 in 2017 by using the [sha1collisiondetection](https://github.com/crmarcstevens/sha1collisiondetection) algorithm by default and producing an error when known SHA-1 collisions are detected. Git is in the process of migrating to using SHA-256 for object hashes, but this has not been rolled out widely yet and gitoxide does not support SHA-256 object hashes. | 
|  | 37 | + | 
|  | 38 | +### PoC | 
|  | 39 | +The following program demonstrates the problem, using the two [SHAttered PDFs](https://shattered.io/): | 
|  | 40 | + | 
|  | 41 | +```rust | 
|  | 42 | +use sha1_checked::{CollisionResult, Digest}; | 
|  | 43 | + | 
|  | 44 | +fn sha1_oid_of_file(filename: &str) -> gix::ObjectId { | 
|  | 45 | +    let mut hasher = gix::features::hash::hasher(gix::hash::Kind::Sha1); | 
|  | 46 | +    hasher.update(&std::fs::read(filename).unwrap()); | 
|  | 47 | +    gix::ObjectId::Sha1(hasher.digest()) | 
|  | 48 | +} | 
|  | 49 | + | 
|  | 50 | +fn sha1dc_oid_of_file(filename: &str) -> Result<gix::ObjectId, String> { | 
|  | 51 | +    // Matches Git’s behaviour. | 
|  | 52 | +    let mut hasher = sha1_checked::Builder::default().safe_hash(false).build(); | 
|  | 53 | +    hasher.update(&std::fs::read(filename).unwrap()); | 
|  | 54 | +    match hasher.try_finalize() { | 
|  | 55 | +        CollisionResult::Ok(digest) => Ok(gix::ObjectId::Sha1(digest.into())), | 
|  | 56 | +        CollisionResult::Mitigated(_) => unreachable!(), | 
|  | 57 | +        CollisionResult::Collision(digest) => Err(format!( | 
|  | 58 | +            "Collision attack: {}", | 
|  | 59 | +            gix::ObjectId::Sha1(digest.into()).to_hex() | 
|  | 60 | +        )), | 
|  | 61 | +    } | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +fn main() { | 
|  | 65 | +    dbg!(sha1_oid_of_file("shattered-1.pdf")); | 
|  | 66 | +    dbg!(sha1_oid_of_file("shattered-2.pdf")); | 
|  | 67 | +    dbg!(sha1dc_oid_of_file("shattered-1.pdf")); | 
|  | 68 | +    dbg!(sha1dc_oid_of_file("shattered-2.pdf")); | 
|  | 69 | +} | 
|  | 70 | +``` | 
|  | 71 | + | 
|  | 72 | +The output is as follows: | 
|  | 73 | + | 
|  | 74 | +``` | 
|  | 75 | +[src/main.rs:24:5] sha1_oid_of_file("shattered-1.pdf") = Sha1(38762cf7f55934b34d179ae6a4c80cadccbb7f0a) | 
|  | 76 | +[src/main.rs:25:5] sha1_oid_of_file("shattered-2.pdf") = Sha1(38762cf7f55934b34d179ae6a4c80cadccbb7f0a) | 
|  | 77 | +[src/main.rs:26:5] sha1dc_oid_of_file("shattered-1.pdf") = Err( | 
|  | 78 | +    "Collision attack: 38762cf7f55934b34d179ae6a4c80cadccbb7f0a", | 
|  | 79 | +) | 
|  | 80 | +[src/main.rs:27:5] sha1dc_oid_of_file("shattered-2.pdf") = Err( | 
|  | 81 | +    "Collision attack: 38762cf7f55934b34d179ae6a4c80cadccbb7f0a", | 
|  | 82 | +) | 
|  | 83 | +``` | 
|  | 84 | + | 
|  | 85 | +The latter behaviour matches Git. | 
|  | 86 | + | 
|  | 87 | +Since the SHAttered PDFs are not in a valid format for Git objects, a direct proof‐of‐concept using higher‐level APIs cannot be immediately demonstrated without significant computational resources. | 
|  | 88 | + | 
|  | 89 | +### Impact | 
|  | 90 | +An attacker with the ability to mount a collision attack on SHA-1 like the [SHAttered](https://shattered.io/) or [SHA-1 is a Shambles](https://sha-mbles.github.io/) attacks could create two distinct Git objects with the same hash. This is becoming increasingly affordable for well‐resourced attackers, with the Shambles researchers in 2020 estimating $45k for a chosen‐prefix collision or $11k for a classical collision, and projecting less than $10k for a chosen‐prefix collision by 2025. The result could be used to disguise malicious repository contents, or potentially exploit assumptions in the logic of programs using gitoxide to cause further vulnerabilities. | 
|  | 91 | + | 
|  | 92 | +This vulnerability affects any user of gitoxide, including `gix-*` library crates, that reads or writes Git objects. | 
0 commit comments