
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.
Também adicionei testes de regressão focados para garantir que essa classe exata de DER malformado continue sendo rejeitada.
Os novos testes cobrem a rejeição de comprimento truncado para:
remove_octet_stringremove_constructedremove_implicitIsso foi importante porque o bug não era sobre um caminho de execução estranho. Era sobre uma regra de validação que precisava se manter consistente entre os auxiliares DER relacionados.
Depois que a correção e os testes foram adicionados, toda a suíte passou localmente:
python -m pytest -q
# 2018 passed, 5 skipped
Isso importa no trabalho real de divulgação.
Uma correção é muito mais forte quando vem acompanhada de testes que fixam a fronteira.
Esta vulnerabilidade foi razoavelmente classificada como Moderada.
O impacto principal aqui é disponibilidade / robustez, não confidencialidade ou integridade.
A classificação do aviso foi:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LIsso faz sentido.
A alegação não é que DER malformado permite que um atacante execute código. A alegação é que DER malformado poderia acionar exceções internas inesperadas em software que analisa material DER não confiável usando esta biblioteca.
Esse é um bug de análise real e defensável, no estilo DoS.
Este problema foi relatado de forma privada por meio do GitHub Security Advisories.
O relatório incluía:
IndexError a jusanteO mantenedor validou o problema, solicitou que a correção fosse acompanhada de testes unitários, e a correção coordenada prosseguiu pelo fluxo de trabalho do fork privado temporário do GHSA.
Durante o processo do CVE, o GitHub inicialmente recusou a atribuição porque o texto do aviso parecia descrever mais de uma vulnerabilidade. O esclarecimento foi simples:
tratava-se de uma vulnerabilidade única com uma única causa raiz — validação incorreta do comprimento DER — e o IndexError em SigningKey.from_der() era uma consequência a jusante dessa mesma aceitação de entrada malformada, não um problema separado e independentemente corrigível.
Esse esclarecimento foi suficiente, e o problema foi atribuído:
CVE-2026-33936
A lição aqui não é “DER é complicado”.
Todo mundo já sabe que DER é complicado.
A lição real é esta:
a entrada estruturada malformada deve ser rejeitada exatamente no ponto em que o parser sabe que ela é inválida.
Se você errar essa fronteira, o código posterior acaba operando com suposições que não são mais confiáveis.
É assim que erros de análise de baixo nível se tornam problemas de segurança.
Este bug também reforça algo importante sobre relatórios e triagem:
“Entrada malformada aceita” foi o começo da história.
“Entrada malformada aceita, e depois propagada para um caminho de exceção interno durante a análise de chaves” foi a história completa.
Essa distinção ajudou a apresentar o caso de forma clara e correta.
Esta vulnerabilidade não era sobre criptografia exótica.
Era sobre um parser confiando em entrada malformada por mais tempo do que deveria.
Um campo de comprimento DER truncado cruzou a fronteira, passou pela validação quando deveria ter sido rejeitado e, eventualmente, causou travamentos na análise de chaves.
É por isso que isso se tornou CVE-2026-33936.
Corrigido ao aplicar verificações adequadas de limites de comprimento DER nos parsers auxiliares afetados.
