
Vulnerabilidad de denegación de servicio en ecdsa (PyPI)
Vulnerabilidad de denegación de servicio en ecdsa (PyPI)
Identifiqué y divulgué de forma responsable una vulnerabilidad de severidad Moderada en python-ecdsa, una biblioteca de criptografía de Python ampliamente utilizada con 47,8 millones de descargas en el último mes.
Encontré este problema mientras revisaba python-ecdsa con una pregunta muy concreta en mente:
¿Qué ocurre si un DER malformado miente sobre su longitud y el analizador se fía demasiado de ella?
En este caso, esa pregunta condujo a un error real.
Las funciones auxiliares de análisis DER en ecdsa.der aceptaban datos truncados en casos donde la longitud codificada declaraba más bytes de los que realmente estaban presentes. Esa entrada malformada debería haberse rechazado de inmediato. En su lugar, podía pasar a mayor profundidad en la lógica de análisis y, finalmente, desencadenar un IndexError interno durante el análisis de claves.
Ese problema se convirtió en CVE-2026-33936.
Proyecto: python-ecdsa en GitHub
Paquete: ecdsa (pip)
CVE: CVE-2026-33936
attacker-controlled malformed DER → truncated length accepted as valid → parser continues past trust boundary → SigningKey.from_der() reaches internal exception path → unexpected IndexError / application-level DoS risk
python-ecdsa es una biblioteca de Python ampliamente utilizada para criptografía de curva elíptica.
Entre otras cosas, se encarga de:
Eso significa que su código de análisis se sitúa directamente en un límite de seguridad.
Siempre que una biblioteca acepta material de claves suministrado externamente o entrada binaria estructurada, la corrección no es solo una cuestión de calidad. Es una propiedad de seguridad.
Si se acepta una entrada malformada cuando debería rechazarse, el código posterior empieza a hacer suposiciones sobre un estado inválido. Ahí es donde los errores dejan de ser «solo equivocaciones de análisis» y empiezan a convertirse en vulnerabilidades.
El análisis DER es una de esas áreas donde los pequeños errores de validación pueden tener efectos desproporcionados.
La clase de error es sencilla:
Ese es exactamente el tipo de fallo de límite que merece la pena comprobar en una revisión de seguridad.
No estaba buscando comportamientos criptográficos extraños aquí. Buscaba fallos de confianza en el manejo de entradas estructuradas.
Ese era el lugar adecuado para mirar.
El problema raíz fue la validación inadecuada de los campos de longitud DER al analizar entradas malformadas o truncadas.
En concreto, ecdsa.der.remove_octet_string() aceptaba entradas donde la longitud DER declarada superaba el número de bytes realmente disponibles en el búfer.
Así, en lugar de rechazar el DER malformado como este:
40963la función auxiliar lo aceptaba y devolvía contenido truncado como si fuera válido.
Eso ya es un error.
Pero el impacto más fuerte se manifestaba más adelante.
Debido a que la entrada malformada se aceptaba en lugar de rechazarse en el límite, SigningKey.from_der() podía alcanzar más tarde una ruta de excepción interna y lanzar:
IndexError: index out of bounds on dimension 1
Eso importa porque no es el tipo de fallo que un llamador espera de una entrada malformada.
El comportamiento correcto es un rechazo limpio de análisis, como UnexpectedDER o ValueError.
Por lo tanto, la vulnerabilidad no era «existe IndexError» de forma aislada.
La vulnerabilidad real era esta:
Eso es una sola cadena de errores, no dos problemas no relacionados.
Que un analizador rechace entrada malformada no es una mejora cosmética. Es parte del modelo de seguridad.
La distinción importante aquí no es si la entrada era inválida. Por supuesto que era inválida.
La distinción importante es cómo se comportó la biblioteca ante una entrada inválida.
Hay una diferencia real entre:
Lo primero es un comportamiento robusto.
Lo segundo crea riesgo a nivel de aplicación si el software analiza DER no confiable y asume que los fallos de la biblioteca se mantienen dentro de los tipos de excepción esperados.
Por eso esto se clasificó adecuadamente como una vulnerabilidad y no meramente como un error de calidad del analizador.
Usé dos PoCs porque demostraban dos partes diferentes de la misma cadena de errores.
El primer PoC mostró que remove_octet_string() aceptaba DER truncado cuya longitud declarada superaba el búfer disponible.
Eso estableció el fallo de validación central:
El segundo PoC mostró el efecto posterior más importante:
el DER malformado suministrado a SigningKey.from_der() desencadenaba de forma determinista un IndexError interno antes de la corrección.
Eso estableció el impacto relevante para la seguridad:
Ese es un resultado mucho más sólido que «el analizador aceptó bytes raros».
Muestra un fallo de límite más una consecuencia operativa real.
El primer PoC prueba la causa raíz.
El segundo PoC prueba el impacto.
Esa división importa.
Muchos informes se detienen en:
«este analizador acepta datos malformados».
Eso es útil, pero no siempre es suficiente para mostrar por qué el error importa.
En este caso, el informe más sólido fue:
Eso hace que la historia de seguridad sea mucho más clara.
La corrección fue mínima y acertada.
El parche añadió la misma regla de seguridad faltante que ya se usaba en remove_sequence():
la longitud declarada debe caber dentro del búfer disponible
Esa comprobación se aplicó a:
remove_constructed()remove_implicit()remove_octet_string()Una vez añadidas esas comprobaciones de límites, el DER malformado/truncado se rechazaba de inmediato con:
UnexpectedDER: Length longer than the provided buffer
Y el PoC que anteriormente desencadenaba IndexError ya no alcanzaba la ruta de excepción interna.
Fallaba limpiamente durante el análisis, que es exactamente lo que debería haber ocurrido desde el principio.
Este es el tipo de corrección que quieres ver en una vulnerabilidad de analizador:
Sin rediseño. Sin ambigüedad. Solo validación correcta donde faltaba.
También añadí pruebas de regresión específicas para asegurarme de que esta clase exacta de DER malformado siga rechazándose.
Las nuevas pruebas cubren el rechazo por longitud truncada para:
remove_octet_stringremove_constructedremove_implicitEso era importante porque el error no se trataba de una ruta de ejecución extraña. Se trataba de una regla de validación que debía mantenerse de forma consistente en las funciones auxiliares DER relacionadas.
Después de añadir la corrección y las pruebas, la suite completa pasó localmente:
python -m pytest -q
# 2018 passed, 5 skipped
Eso importa en el trabajo real de divulgación.
Una corrección es mucho más sólida cuando viene acompañada de pruebas que aseguran el límite.
Este problema se clasificó razonablemente como Moderada.
El impacto clave aquí es la disponibilidad / robustez, no la confidencialidad ni la integridad.
La clasificación del aviso fue:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LEso tiene sentido.
La afirmación no es que el DER malformado permita a un atacante ejecutar código. La afirmación es que el DER malformado podría desencadenar excepciones internas inesperadas en software que analiza material DER no confiable usando esta biblioteca.
Ese es un error de análisis de estilo DoS real y defendible.
Este problema se informó de forma privada a través de GitHub Security Advisories.
El informe incluía:
IndexError posteriorEl mantenedor validó el problema, solicitó que la corrección se publicara con pruebas unitarias, y la corrección coordinada se llevó a cabo a través del flujo de trabajo de fork privado temporal de GHSA.
Durante la gestión del CVE, GitHub inicialmente se negó a asignarlo porque el texto del aviso parecía describir más de una vulnerabilidad. La aclaración fue simple:
esto era una vulnerabilidad única con una sola causa raíz — validación inadecuada de la longitud DER — y el IndexError de SigningKey.from_der() era una consecuencia posterior de esa misma aceptación de entrada malformada, no un problema separado e independientemente solucionable.
Esa aclaración fue suficiente, y al problema se le asignó:
CVE-2026-33936
La lección aquí no es «DER es complicado».
Todo el mundo ya sabe que DER es complicado.
La lección real es esta:
la entrada estructurada malformada debe rechazarse en el punto exacto donde el analizador sabe que es inválida.
Si fallas en ese límite, el código posterior termina operando sobre suposiciones que ya no son confiables.
Así es como los errores de análisis de bajo nivel se convierten en problemas de seguridad.
Este error también refuerza algo importante sobre los informes y el triaje:
«Entrada malformada aceptada» fue el comienzo de la historia.
«Entrada malformada aceptada y luego propagada a una ruta de excepción interna durante el análisis de claves» fue la historia completa.
Esa distinción ayudó a presentar el caso de forma clara y correcta.
Esta vulnerabilidad no trataba sobre criptografía exótica.
Se trataba de un analizador que confiaba en una entrada malformada durante más tiempo del que debería.
Un campo de longitud DER truncado cruzó el límite, sobrevivió a la validación cuando debería haber sido rechazado y, finalmente, causó un comportamiento con caídas en el análisis de claves.
Por eso esto se convirtió en CVE-2026-33936.
Corregido aplicando comprobaciones de límites de longitud DER adecuadas en los analizadores auxiliares afectados.
