Investigación demuestra que los commits ‘Verified’ de GitHub pueden reescribirse sin romper la firma

Nuevas investigaciones muestran que el hash de un commit firmado de Git no es el nombre único que gran parte del mundo del software asume. Dado cualquier commit firmado, alguien sin la clave de firma puede acuñar un segundo commit con los mismos archivos, autor y fecha, y una firma válida, y GitHub seguirá marcándolo como 'Verified'.

Todo lo que un revisor verificará coincide. El hash del commit no. Esto importa porque muchos sistemas tratan un hash de commit verificado como un nombre permanente y único para su contenido.

He aquí el fallo concreto: si bloqueas un commit malicioso por su hash, un atacante puede volver a subir el mismo contenido bajo un hash nuevo y 'Verified' que tu lista de bloqueo nunca ha visto. La deduplicación, los registros de procedencia y los registros de compilación reproducible que se basan en el hash heredan la misma vulnerabilidad.

Un espejo comprometido u hostil puede entregar a los clonadores commits firmados válidamente cuyos hashes difieren de los del repositorio oficial.

Esto no es una forma de deslizar código diferente más allá de la verificación de firma. Los archivos son idénticos en cada copia, por lo que un hash que hayas fijado aún obtiene exactamente el contenido que esperabas, o falla.

No hay CVE ni aviso del proveedor, y no hay nada que cambiar en tu propio repositorio: el fallo está en cómo una plataforma decide qué significa 'Verified', y la corrección corresponde a la plataforma.

El trabajo proviene de Jacob Ginesin, estudiante de doctorado en la Universidad Carnegie Mellon y auditor criptográfico en Cure53. Su artículo de cinco páginas, publicado en arXiv el 2 de julio, incluye una herramienta pública que ejecuta los tres ataques, más dos repositorios demo donde los commits modificados aún muestran 'Verified' en GitHub.

Debido a que cada commit nombra a su padre por hash, modificar un commit fuerza nuevos hashes en los commits superiores. La herramienta reescribe esa cadena para mantenerla consistente. Un descendiente firmado, sin embargo, pierde su propia insignia en el momento en que cambia el puntero al padre. Ginesin llama a esto 'maleabilidad de la cadena de hash'.

La causa es la maleabilidad de la firma. El hash de un commit se calcula sobre todo lo que contiene, incluidos los bytes brutos de la firma en su cabecera. Muchas firmas pueden reescribirse en una forma diferente pero aún válida, y cambiar esos bytes cambia el hash sin tocar una línea de código.

Las tres rutas de ataque

  • – Claves ECDSA: invierte la firma con un álgebra de curva elíptica clásica (convertir el valor s en n – s). Ambas formas son válidas. Esto pasa una verificación local de git verify-commit y obtiene la insignia de GitHub.
  • – Claves RSA y EdDSA: añade un campo adicional ignorado a la sección 'no hasheada' de la firma, la parte que la firma deliberadamente no cubre. La firma sigue siendo válida, pero los bytes del commit y su hash cambian. Local y GitHub lo aceptan.
  • – Claves S/MIME (X.509): reescribe un campo de longitud en la estructura DER de la firma en una forma más larga y no estándar. Una verificación local estricta (mediante gpgsm) lo rechaza, pero GitHub aún lo marca como 'Verified', lo cual la herramienta reproduce.

Causa raíz: falta de normalización

Las tres rutas comparten un habilitador: GitHub no normaliza una firma antes de verificarla. Sin codificación estricta para S/MIME, sin eliminar esos campos de OpenPGP, y valores ECDSA no canónicos aceptados tal cual.

GitHub luego archiva un registro 'Verified' contra cada hash de commit y no lo vuelve a verificar, por lo que un commit permanece 'Verified' incluso después de que su clave de firma sea revocada. Sube un original y su gemelo a dos ramas, y la vista de comparación de GitHub los trata como historias divergentes, un commit adelante y otro atrás, a pesar de tener archivos idénticos.

¿Es una colisión de hash?

Para ser claros: esto no es una colisión de hash. No rompe SHA-1 ni SHA-256, y no tiene nada que ver con la migración de Git a SHA-256. Nadie está forzando a dos commits diferentes a compartir un hash; es lo contrario, un commit que puede escribirse de muchas maneras válidas, cada una con su propio hash.

El movimiento central es antiguo. Bitcoin luchó exactamente la misma simetría ECDSA hace años, cuando cualquiera podía invertir el valor s en una firma de transacción y cambiar el ID de la transacción sin la clave del propietario. La solución fue aceptar solo la forma 'low-S', y luego mover las firmas fuera del ID con SegWit.

Las soluciones del paper se alinean con eso: canonizar la codificación antes de confiar en el hash. Una lección conocida, no criptografía nueva exótica.

Conexión con ataques previos y recomendaciones

El paper también conecta esto con los recientes secuestros de etiquetas de GitHub Actions, los ataques tj-actions/changed-files de 2025 y trivy-action de 2026 (cita este último). Después de esos, el consejo fue simple: fijar un hash de commit completo, no una etiqueta movible. Ese consejo sigue vigente.

Fijar detuvo esos ataques, y esta investigación no cambia eso. Su punto es más estrecho. En el caso de Trivy, los commits maliciosos se destacaban porque no podían estar firmados válidamente. Esto es una advertencia contra confiar demasiado en esa señal: una firma válida prueba quién firmó un commit, pero no hace que el hash del commit sea un nombre único para lo que contiene.

¿Quién debe actuar?

Entonces, ¿quién tiene que hacer algo? No el desarrollador que fija una Action o un módulo; un hash fijado aún obtiene el código correcto. El trabajo es para las plataformas. El paper dice que deberían canonizar las firmas antes de confiar en ellas.

Las herramientas que bloquean, deduplican o registran procedencia por hash de commit deberían hacer lo mismo, verificar y canonizar primero en lugar de confiar en el hash bruto de un objeto firmado que un atacante puede recodificar. No todos los sistemas están igualmente expuestos: los esquemas que también fijan un hash independiente de los archivos obtenidos, como las derivaciones de salida fija de Nix, mantienen un respaldo; aquellos que se detienen en un hash de commit verificado no lo hacen.

Ginesin dice que reportó el problema a GNU y Git en enero y a GitHub en marzo, y que hasta la publicación del paper, ni Git ni ninguna plataforma lo habían solucionado. La corrección del lado de la plataforma es bien conocida, y el lugar obvio para comenzar es el caso S/MIME, donde GitHub aún acepta lo que una verificación local estricta rechaza.

Cybersecurity
Cybersecurity
Cybersecurity
Cybersecurity

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *