
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: