SUPPLY CHAIN

GitHub 'Verified' Commits Can Be Cloned Into New Hashes Without the Signing Key

New research shows anyone can take a signed Git commit and mint a second copy with identical files, author, and a valid signature, but a different hash, and GitHub still stamps it Verified. Hash-based blocklists, dedup, and provenance systems inherit the flaw. Three attack routes cover every signature scheme GitHub verifies, and no forge has shipped a fix.

Matt Lucas  |  July 9, 2026  |  5 min
GitHub 'Verified' Commits Can Be Cloned Into New Hashes Without the Signing Key — editorial hero illustration
3
attack routes, covering every scheme GitHub verifies
0
fixes shipped by Git or any forge as of publication
6 mo
since disclosure to GNU and Git in January 2026
2
live demo repos still showing Verified on GitHub
TL;DR
  • What: A CMU researcher showed that anyone, without the signing key, can rewrite a signed Git commit into a new commit with identical content, a still-valid signature, a GitHub Verified badge, and a different hash.
  • Impact: Any system that treats a verified commit hash as a unique name for content is exposed: hash blocklists can be re-pushed around, and dedup, provenance logs, and reproducible-build records keyed on commit hashes inherit the same gap.
  • Fix / mitigation: There is no CVE and nothing to patch in your repos; the fix is forge-side signature canonicalization, which neither Git nor GitHub had shipped as of the paper's July 2 publication despite January and March disclosures.
  • Who's at risk: Forge operators and anyone who blocks, deduplicates, or records provenance by commit hash; developers pinning Actions or modules to full hashes are not the target and pinning still works.

A signed Git commit's hash is not the unique name most of the software supply chain assumes it is. Research published July 2 by Jacob Ginesin, a Carnegie Mellon PhD student and cryptographic auditor at Cure53, shows that anyone can take a signed commit and, without the signing key, produce a second commit with the same files, same author, same date, and a valid signature that GitHub stamps 'Verified.' Only the hash differs. Ginesin's five-page arXiv paper ships with a public tool that runs all three attack variants and two demo repositories where the rewritten commits still display the Verified badge on GitHub today.

The practical failure is immediate: if you block a malicious commit by hash, an attacker re-pushes the identical content under a fresh, still-Verified hash your blocklist has never seen. Deduplication systems, provenance logs, and reproducible-build records that key on commit hashes all inherit the same soft spot. A compromised mirror can serve cloners validly signed commits whose hashes do not match the canonical forge, and GitHub's own compare view treats the twins as divergent histories despite byte-identical trees.

Three routes, every scheme covered

The root cause is signature malleability. A commit's hash covers everything in the commit object, including the raw bytes of the signature itself. Many signature formats can be rewritten into a different but still-valid encoding, and changing those bytes changes the hash without touching a line of code. Ginesin demonstrates one route per scheme, together covering every GPG type GitHub verifies plus S/MIME:

The common enabler is that GitHub does not normalize a signature before verifying it: no strict DER encoding for S/MIME, no stripping of the OpenPGP unhashed fields, and non-canonical ECDSA values accepted as-is. GitHub then files a Verified record against each commit hash and never re-checks it, so a commit stays Verified even after its signing key is revoked.

Hash chain malleability

Because every commit names its parent by hash, malleating one commit forces new hashes on every commit above it. Ginesin's tool rewrites that chain to keep it consistent, an effect he calls 'hash chain malleability.' There is a catch for attackers: a signed descendant loses its own badge the moment its parent pointer changes, because the child's signature was made over the old parent hash. The malleation propagates, but the verified status does not automatically follow it up the chain.

This is not a hash collision

No one is forcing two different commits to share one hash, and nothing here breaks SHA-1 or SHA-256 or touches Git's SHA-256 migration. It is the reverse: one commit that can be written many valid ways, each with its own hash. Crucially, it is also not a way to slip different code past a signature check. The files are identical in every copy, so a hash you pinned still fetches exactly the content you expected, or it fails.

Bitcoin already fought this

The core move is old. Bitcoin faced the identical ECDSA symmetry years ago, when anyone could flip the s value in a transaction signature and change the transaction ID without the owner's key. The fix was to accept only the low-S form, and later to move signatures out of the ID entirely with SegWit. Ginesin's recommended fixes rhyme with that: canonicalize the signature encoding before you trust the hash. This is a known lesson, not exotic new cryptography, which makes the absence of a forge-side fix harder to excuse.

How this connects to the Actions tag hijacks

The paper ties the work to the recent GitHub Actions supply-chain incidents, the 2025 tj-actions/changed-files compromise and the 2026 trivy-action attack it cites. The guidance after those was to pin to a full commit hash rather than a movable tag, and that advice still holds; pinning stopped those attacks and this research does not change that. The narrower point is a caution against a specific tell. In the Trivy case, the malicious commits stood out because they could not be validly signed. Ginesin's work shows why you should not lean too hard on that signal: a valid signature proves who signed a commit, but it does not make the commit's hash a one-of-a-kind name for its contents.

Who has to act

Not the developer pinning an Action or a module. A pinned hash still fetches the right code, and there is nothing to change in your own repository. The work belongs to the forges: canonicalize signatures before trusting them. Any tooling that blocks, deduplicates, or records provenance by commit hash should do the same, verifying and canonicalizing first rather than trusting the raw hash of a signed object an attacker can re-encode. Exposure is uneven: schemes that also pin an independent hash of the fetched files, such as Nix fixed-output derivations, keep a backstop; systems that stop at a verified commit hash do not.

Six months out, still unpatched

Ginesin reported the issue to GNU and Git in January 2026 and to GitHub in March. As of the paper's July 2 publication, neither Git nor any forge had addressed it, and two demo repositories still show Verified on the rewritten commits. There is no CVE and no vendor advisory. The most obvious starting point is the S/MIME case, where GitHub accepts a signature that a strict local check already rejects.

For security and IT teams, the action item is an inventory question, not a patch: find every place a decision hangs on a verified commit hash being unique, blocklists, dedup, provenance, and reproducible-build attestations, and confirm each one canonicalizes and independently verifies content rather than trusting the raw hash of a signed object. Until forges ship normalization, treat a Verified badge as proof of authorship only, never as proof that a hash is the single canonical name for what it points to.

Questions about your exposure?

RedEye Security provides assessments for organizations that need to understand their real risk.

Talk to us