
Vulnerabilità di Denial of Service in ecdsa (PyPI)
Vulnerabilità Denial of Service in ecdsa (PyPI)
Ho identificato e divulgato in modo responsabile una vulnerabilità di gravità Moderata in python-ecdsa, una libreria Python di crittografia ampiamente utilizzata con 47,8M di download nell'ultimo mese.
Ho trovato questo problema mentre esaminavo python-ecdsa con una domanda molto specifica in mente:
Cosa succede se un DER malformato mente sulla propria lunghezza e il parser si fida troppo?
In questo caso, quella domanda ha portato a un bug reale.
Gli helper di parsing DER in ecdsa.der accettavano dati troncati nei casi in cui la lunghezza codificata dichiarava più byte di quelli effettivamente presenti. Quel input malformato avrebbe dovuto essere rifiutato immediatamente. Invece, poteva passare più a fondo nella logica di parsing e alla fine innescare un IndexError interno durante il parsing delle chiavi.
Quel problema è diventato CVE-2026-33936.
Progetto: python-ecdsa su GitHub
Pacchetto: 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 è una libreria Python ampiamente utilizzata per la crittografia a curve ellittiche.
Tra le altre cose, gestisce:
Ciò significa che il suo codice di parsing si trova direttamente su un confine di sicurezza.
Ogni volta che una libreria accetta materiale di chiave fornito esternamente o input binario strutturato, la correttezza non è solo una questione di qualità. È una proprietà di sicurezza.
Se un input malformato viene accettato quando dovrebbe essere rifiutato, il codice a valle inizia a fare assunzioni basate su uno stato non valido. È lì che i bug smettono di essere "semplici errori di parsing" e iniziano a diventare vulnerabilità.
Il parsing DER è una di quelle aree in cui piccoli errori di validazione possono avere effetti sproporzionati.
La classe di bug è semplice:
È esattamente il tipo di fallimento di confine che vale la pena verificare in una revisione di sicurezza.
Non stavo cercando comportamenti crittografici strani qui. Stavo cercando fallimenti di fiducia nella gestione di input strutturati.
Era il posto giusto dove guardare.
Il problema alla radice era una validazione impropria dei campi di lunghezza DER durante il parsing di input malformati o troncati.
Nello specifico, ecdsa.der.remove_octet_string() accettava input in cui la lunghezza DER dichiarata superava il numero di byte effettivamente disponibili nel buffer.
Quindi, invece di rifiutare DER malformati come questo:
40963l'helper lo accettava e restituiva contenuto troncato come se fosse valido.
Questo è già un bug.
Ma l'impatto più forte si è manifestato a valle.
Poiché l'input malformato veniva accettato invece di essere rifiutato al confine, SigningKey.from_der() poteva successivamente raggiungere un percorso di eccezione interno e sollevare:
IndexError: index out of bounds on dimension 1
Questo è importante perché non è il tipo di errore che un chiamante si aspetta da un input malformato.
Il comportamento corretto è un rifiuto pulito del parsing, come UnexpectedDER o ValueError.
Quindi la vulnerabilità non era "esiste un IndexError" in isolamento.
La vera vulnerabilità era questa:
È una singola catena di bug, non due problemi non correlati.
Un parser che rifiuta input malformati non è un miglioramento estetico. È parte del modello di sicurezza.
La distinzione importante qui non è se l'input fosse non valido. Ovviamente lo era.
La distinzione importante è come la libreria si è comportata di fronte a un input non valido.
C'è una differenza reale tra:
Il primo è un comportamento robusto.
Il secondo crea un rischio a livello applicativo se il software analizza DER non attendibili e presume che i guasti della libreria rimangano entro i tipi di eccezione previsti.
Ecco perché è stato correttamente classificato come vulnerabilità piuttosto che semplicemente come un bug di qualità del parser.
Ho usato due PoC perché dimostravano due parti diverse della stessa catena di bug.
Il primo PoC ha mostrato che remove_octet_string() accettava DER troncati la cui lunghezza dichiarata superava il buffer disponibile.
Questo ha stabilito il fallimento di validazione centrale:
Il secondo PoC ha mostrato l'effetto a valle più importante:
DER malformati forniti a SigningKey.from_der() innescavano deterministicamente un IndexError interno prima della correzione.
Questo ha stabilito l'impatto rilevante per la sicurezza:
È un risultato molto più forte di "il parser ha accettato byte strani".
Mostra un fallimento di confine più una reale conseguenza operativa.
Il primo PoC dimostra la causa radice.
Il secondo PoC dimostra l'impatto.
Questa suddivisione è importante.
Molti report si fermano a:
"questo parser accetta dati malformati."
È utile, ma non sempre sufficiente per mostrare perché il bug sia importante.
In questo caso, il report più forte era:
Questo rende la storia di sicurezza molto più chiara.
La correzione è stata minima e corretta.
La patch ha aggiunto la stessa regola di sicurezza mancante già utilizzata in remove_sequence():
la lunghezza dichiarata deve rientrare nel buffer disponibile
Il controllo è stato applicato a:
remove_constructed()remove_implicit()remove_octet_string()Una volta aggiunti quei controlli dei limiti, i DER malformati/troncati venivano rifiutati immediatamente con:
UnexpectedDER: Length longer than the provided buffer
E il PoC che in precedenza innescava IndexError non raggiungeva più il percorso di eccezione interno.
Falliva in modo pulito durante il parsing, che è esattamente ciò che sarebbe dovuto accadere dall'inizio.
Questo è il tipo di correzione che si vuole vedere in una vulnerabilità di parsing: