‘Geverifieerde’ commits van GitHub kunnen worden herschreven in nieuwe hashes zonder handtekeningen te verbreken

Nieuw onderzoek toont aan dat de hash van een ondertekende Git-commit niet de unieke naam is die een groot deel van de softwarewereld ervan uitgaat. Gegeven een ondertekende commit kan iemand zonder de ondertekeningssleutel een tweede commit maken met dezelfde bestanden, auteur en datum, en een geldige handtekening. GitHub stempelt nog steeds ‘Geverifieerd’.

Alles wat een recensent zou controleren, komt overeen. De hash van de commit doet dat niet. Dat is belangrijk omdat zoveel systemen een geverifieerde commit-hash behandelen als een permanente, unieke naam voor de inhoud ervan.

Hier is de concrete mislukking: blokkeer een slechte commit op basis van de hash, en een aanvaller kan dezelfde inhoud opnieuw pushen onder een nieuwe, nog steeds ‘geverifieerde’ hash die je blokkeerlijst nog nooit heeft gezien. Ontdubbeling, herkomstlogboeken en reproduceerbare records die op de hash sleutelen, erven dezelfde zwakke plek.

Een gecompromitteerde of vijandige spiegel kan kloners geldig ondertekende commits overhandigen waarvan de hashes verschillen van die op de canonieke smederij.

Wat dit niet is, is een manier om andere code voorbij een handtekeningcontrole te glippen. De bestanden zijn in elke kopie identiek, dus een hash die u hebt vastgezet, haalt nog steeds precies de inhoud op die u verwachtte, of mislukt.

Er is geen CVE en geen leveranciersadvies, en er valt niets te veranderen in uw eigen opslagplaats: de fout zit in de manier waarop een smederij beslist wat ‘Geverifieerd’ betekent, en de oplossing hoort aan de kant van de smederij te liggen.

Het werk is afkomstig van Jacob Ginesin, een PhD-student aan de Carnegie Mellon University en een cryptografische auditor bij Cure53. Zijn artikel van vijf pagina’s, op 2 juli op arXiv geplaatst, wordt geleverd met een openbare tool die alle drie de aanvallen uitvoert, plus twee demo-repository’s waar de gemanipuleerde commits nog steeds ‘Verified’ laten zien op GitHub.

Omdat elke commit zijn ouder een hash noemt, forceert het hameren van één commit nieuwe hashes op de commits erboven. De tool herschrijft die keten om deze consistent te houden. Een getekende afstammeling verliest echter zijn eigen badge op het moment dat de ouderaanwijzer verandert. Ginesin noemt het effect “maakbaarheid van de hasjketen.”

De oorzaak is de kneedbaarheid van de signatuur. De hash van een commit wordt berekend over alles wat erin zit, inclusief de onbewerkte bytes van de handtekening in de header. Veel handtekeningen kunnen worden herschreven in een andere, maar nog steeds geldige vorm, en het veranderen van die bytes verandert de hash zonder ook maar één regel code aan te raken.

De drie routes omvatten elk GPG-schema dat GitHub verifieert, plus S/MIME:

  • ECDSA-sleutels: draai de handtekening om met een klassiek stuk elliptische-curve-algebra (verander de waarde s in n – s). Beide vormen zijn geldig. Dit passeert een lokale git verificatie-commit en verdient een GitHub-badge.
  • RSA- en EdDSA-sleutels: voeg een extra, genegeerd veld toe aan de sectie ‘niet-gehasht’ van de handtekening, het deel dat de handtekening opzettelijk niet bedekt. De handtekening wordt nog steeds uitgecheckt, maar de bytes van de commit en de hash ervan veranderen. Lokaal en GitHub accepteren het beide.
  • S/MIME-toetsen (X.509): herschrijf een lengteveld in de DER-structuur van de handtekening in een langere, niet-standaardvorm. Een strikte lokale controle (via gpgsm) wijst het af, maar GitHub markeert het nog steeds als ‘Geverifieerd’, wat de tool beide reproduceert.

De drie routes delen één enabler: GitHub normaliseert een handtekening niet voordat deze wordt gecontroleerd. Geen strikte codering op S/MIME, geen verwijdering van die OpenPGP-velden en niet-canonieke ECDSA-waarden die worden geaccepteerd zoals ze zijn.

GitHub archiveert vervolgens een “Geverifieerd” record voor elke commit-hash en controleert deze niet opnieuw, zodat een commit “Geverifieerd” blijft, zelfs nadat de ondertekeningssleutel ervan is ingetrokken. Duw een origineel en zijn tweelingbroer naar twee vertakkingen, en GitHub’s vergelijkingsweergave behandelt ze als uiteenlopende geschiedenissen, één commit voor en één achter, ondanks identieke bestanden.

Voor alle duidelijkheid: dit is geen hash-botsing. Het verbreekt SHA-1 of SHA-256 niet, en heeft niets te maken met Git’s overstap naar SHA-256. Niemand dwingt twee verschillende commits om één hash te delen; het is het omgekeerde, één commit die op veel geldige manieren kan worden geschreven, elk met zijn eigen hash.

De kernbeweging is oud. Bitcoin vocht jaren geleden tegen exact dezelfde ECDSA-symmetrie, toen iedereen de s-waarde in een transactiehandtekening kon omdraaien en de ID van de transactie kon wijzigen zonder de sleutel van de eigenaar. De oplossing was om alleen de “low-S”-vorm te accepteren en later handtekeningen uit de ID te verplaatsen met SegWit.

De oplossingen van het artikel rijmen daarmee: maak de codering heilig voordat je de hash vertrouwt. Een bekende les, geen exotische nieuwe cryptografie.

Het artikel brengt dit ook in verband met de recente GitHub Actions-tagkapingen, de tj-actions/changed-files uit 2025 en de trivy-action-aanvallen uit 2026 (het haalt de laatste aan). Daarna was het advies simpel: vastzetten op een volledige commit-hash, niet op een verplaatsbare tag. Dat advies geldt nog steeds.

Pinning stopte die aanvallen, en dit onderzoek verandert daar niets aan. De punt is smaller. In de Trivy-zaak vielen de kwaadwillige commits op omdat ze niet geldig konden worden ondertekend. Dit is een waarschuwing om niet te hard op die tell te leunen: een geldige handtekening bewijst wie een commit heeft ondertekend, maar het maakt de hash van de commit niet tot een unieke naam voor wat deze bevat.

Dus wie moet iets doen? Niet de ontwikkelaar die een actie of module vastzet; een vastgezette hash haalt nog steeds de juiste code op. Het werk is voor de smederijen. De krant zegt dat ze handtekeningen heilig moeten verklaren voordat ze worden vertrouwd.

Tooling die de herkomst blokkeert, ontdubbelt of registreert door middel van commit-hash zou hetzelfde moeten doen, eerst verifiëren en canonicaliseren in plaats van te vertrouwen op de onbewerkte hash van een ondertekend object dat een aanvaller opnieuw kan coderen. Niet alle systemen zijn in gelijke mate blootgesteld: schema’s die ook een onafhankelijke hash van de opgehaalde bestanden vastzetten, zoals Nix’s afleidingen met vaste uitvoer, houden een backstop in; degenen die stoppen bij een geverifieerde commit-hash doen dat niet.

Ginesin zegt dat hij de kwestie in januari aan GNU en Git en in maart aan GitHub heeft gemeld, en dat op het moment van de publicatie van de krant noch Git, noch enige smederij er iets aan had gedaan. De oplossing aan de smederij is goed bekend, en de voor de hand liggende plaats om te beginnen is de S/MIME-zaak, waarin GitHub nog steeds accepteert wat een strikte lokale controle afwijst.

Thijs Van der Does