
Vulnerabilidade de Negação de Serviço em ecdsa (PyPI)
Vulnerabilidade de Negação de Serviço em ecdsa (PyPI)
Identifiquei e divulguei de forma responsável uma vulnerabilidade de severidade Moderada no python-ecdsa, uma biblioteca de criptografia Python amplamente utilizada, com 47,8 milhões de downloads no último mês.
Encontrei este problema ao revisar o python-ecdsa com uma pergunta muito específica em mente:
O que acontece se um DER malformado mentir sobre seu comprimento e o parser confiar nele além do devido?
Nesse caso, essa pergunta levou a um bug real.
Os auxiliares de análise DER em ecdsa.der aceitavam dados truncados nos casos em que o comprimento codificado declarava mais bytes do que os realmente presentes. Essa entrada malformada deveria ter sido rejeitada imediatamente. Em vez disso, ela podia avançar mais fundo na lógica de análise e, eventualmente, acionar um IndexError interno durante a análise de chaves.
Esse problema se tornou CVE-2026-33936.
Projeto: python-ecdsa no GitHub
Pacote: 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 é uma biblioteca Python amplamente utilizada para criptografia de curva elíptica.
Entre outras coisas, ela lida com:
Isso significa que seu código de análise fica diretamente sobre uma fronteira de segurança.
Sempre que uma biblioteca aceita material de chave fornecido externamente ou entrada binária estruturada, a correção não é apenas uma questão de qualidade. É uma propriedade de segurança.
Se uma entrada malformada é aceita quando deveria ser rejeitada, o código a jusante começa a fazer suposições sobre um estado inválido. É aí que os bugs deixam de ser “apenas erros de análise” e começam a se tornar vulnerabilidades.
A análise DER é uma daquelas áreas em que pequenos erros de validação podem ter efeitos desproporcionais.
A classe de bug é direta:
Esse é exatamente o tipo de falha de fronteira que vale a pena verificar em uma revisão de segurança.
Eu não estava procurando um comportamento criptográfico estranho aqui. Eu estava procurando falhas de confiança no tratamento de entradas estruturadas.
Esse era o lugar certo para procurar.
O problema raiz era a validação inadequada dos campos de comprimento DER ao analisar entradas malformadas ou truncadas.
Especificamente, ecdsa.der.remove_octet_string() aceitava entradas em que o comprimento DER declarado excedia o número de bytes realmente disponíveis no buffer.
Então, em vez de rejeitar um DER malformado como este:
40963o auxiliar aceitava a entrada e retornava o conteúdo truncado como se fosse válido.
Isso já é um bug.
Mas o impacto mais forte aparecia a jusante.
Como a entrada malformada era aceita em vez de rejeitada na fronteira, SigningKey.from_der() podia mais tarde alcançar um caminho de exceção interno e lançar:
IndexError: index out of bounds on dimension 1
Isso é relevante porque não é o tipo de falha que um chamador espera de uma entrada malformada.
O comportamento correto é uma rejeição limpa de análise, como UnexpectedDER ou ValueError.
Portanto, a vulnerabilidade não era “IndexError existe” isoladamente.
A vulnerabilidade real era esta:
Isso é uma única cadeia de bug, não dois problemas não relacionados.
Um parser rejeitar uma entrada malformada não é uma melhoria cosmética. É parte do modelo de segurança.
A distinção importante aqui não é se a entrada era inválida. É claro que era inválida.
A distinção importante é como a biblioteca se comportou diante de uma entrada inválida.
Há uma diferença real entre:
O primeiro é um comportamento robusto.
O segundo cria risco no nível da aplicação se o software analisa DER não confiável e presume que as falhas da biblioteca permanecem dentro dos tipos de exceção esperados.
É por isso que isso foi corretamente classificado como uma vulnerabilidade, em vez de meramente um bug de qualidade de parser.
Usei duas PoCs porque elas demonstraram duas partes diferentes da mesma cadeia de bug.
A primeira PoC mostrou que remove_octet_string() aceitava DER truncado cujo comprimento declarado excedia o buffer disponível.
Isso estabeleceu a falha central de validação:
A segunda PoC mostrou o efeito a jusante mais importante:
DER malformado fornecido a SigningKey.from_der() acionava deterministicamente um IndexError interno antes da correção.
Isso estabeleceu o impacto relevante para a segurança:
Esse é um resultado muito mais forte do que “o parser aceitou bytes estranhos”.
Mostra uma falha de fronteira além de uma consequência operacional real.
A primeira PoC prova a causa raiz.
A segunda PoC prova o impacto.
Essa divisão é importante.
Muitos relatórios param em:
“este parser aceita dados malformados.”
Isso é útil, mas nem sempre suficiente para mostrar por que o bug é importante.
Neste caso, o relatório mais forte era:
Isso torna a narrativa de segurança muito mais clara.
A correção foi mínima e correta.
O patch adicionou a mesma regra de segurança ausente já usada em remove_sequence():
o comprimento declarado deve caber no buffer disponível
Essa verificação foi aplicada a:
remove_constructed()remove_implicit()remove_octet_string()Assim que essas verificações de limites foram adicionadas, DER malformado/truncado era rejeitado imediatamente com:
UnexpectedDER: Length longer than the provided buffer
E a PoC que anteriormente acionava IndexError não alcançava mais o caminho de exceção interno.
Ela falhava de forma limpa durante a análise, que é exatamente o que deveria ter acontecido desde o início.
Este é o tipo de correção que você quer ver em uma vulnerabilidade de parser:
Sem redesenho. Sem ambiguidade. Apenas validação correta onde ela estava ausente.