Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
dsa — Análise do CVE-2016-3959 e um Ataque de Prova de Conceito contra um Servidor SSH em Go. | Kitploit
Ferramentas/GitHubGitHub/alexmullins/dsa
Análise de VulnerabilidadesExploraçãoCriptografiaTestes de PenetraçãoAprendizado e Educação
GitHubalexmullins/dsa

dsa

Análise do CVE-2016-3959 e um Ataque de Prova de Conceito contra um Servidor SSH em Go.

Ver Repositório
117há 10 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Um Resumo da Vulnerabilidade crypto/dsa do Go (CVE-2016-3959)

Alex Mullins

9 de abril de 2016


Introdução

Recentemente, foi descoberto um bug na biblioteca de criptografia do Algoritmo de Assinatura Digital (DSA) para a linguagem de programação Go. Neste artigo, abordaremos os detalhes do bug e como um atacante poderia explorá-lo para iniciar um ataque de negação de serviço contra um servidor SSH Go padrão que usa a biblioteca DSA subjacente para autenticar clientes.

A primeira menção desta vulnerabilidade apareceu em uma postagem na lista de discussão Open Source Security (oss-sec) em http://seclists.org/oss-sec/2016/q2/11.

Go tem um loop infinito em várias rotinas de inteiros grandes que torna os programas Go vulneráveis a ataques remotos de negação de serviço. Programas que usam autenticação de cliente HTTPS ou as bibliotecas do servidor ssh do Go estão ambos expostos a esta vulnerabilidade. Isto está sendo tratado no seguinte CL: https://golang.org/cl/21533

-- Jason Buberel

Em resumo, se esta vulnerabilidade fosse explorada, poderia levar a um loop infinito no código subjacente da biblioteca BigNum. Isso consumirá recursos do sistema em termos de CPU e memória e poderia eventualmente fazer com que o programa ou o próprio sistema se tornasse não responsivo.

A declaração acima diz que SSH juntamente com a autenticação de cliente HTTPS são afetados, mas depois de olhar para os pacotes crypto/tls e net/http do Go, isso parece estar incorreto. A autenticação de cliente HTTPS pode usar esquemas de assinatura RSA ou ECDSA, mas não DSA. Veja abaixo. Se eu estiver errado sobre isso, por favor me avise.

https://golang.org/pkg/crypto/tls/#Certificate

type Certificate struct {
        Certificate [][]byte
        // PrivateKey contains the private key corresponding to the public key
        // in Leaf. For a server, this must implement crypto.Signer and/or
        // crypto.Decrypter, with an RSA or ECDSA PublicKey. For a client
        // (performing client authentication), this must be a crypto.Signer
        // with an RSA or ECDSA PublicKey.
        PrivateKey crypto.PrivateKey

        ... other fields
}

Não muito tempo depois que essa postagem apareceu na lista de discussão oss-sec, um número CVE foi emitido: CVE-2016-3959. Os mantenedores do Go têm uma correção pronta para isso e aparecerá nas versões 1.5.4 e 1.6.1 que serão lançadas na quarta-feira, 13 de abril de 2016; https://groups.google.com/forum/#!topic/golang-nuts/MmSbFHLPo8g.

Para acompanhar os exemplos de código neste artigo, você precisará da versão Go 1.6 instalada. Siga as instruções em https://golang.org/doc/install. Se você quiser baixar este documento e os exemplos de código, precisará do Git instalado também. Siga as instruções em https://git-scm.com/book/en/v2/Getting-Started-Installing-Git. Para clonar o repositório, execute o seguinte comando em um terminal:

$ go get github.com/alexmullins/dsa

Isso irá clonar o repositório no seu workspace Go.

A próxima seção cobrirá os detalhes da vulnerabilidade.

A Falha

Então, o que exatamente está errado? Para responder a isso, é preciso voltar ao anúncio original na lista de discussão oss-sec. Não há muita informação lá além de uma explicação geral do problema e um link para a correção do código em https://golang.org/cl/21533. A mensagem de commit para essa alteração contém o seguinte:

crypto/dsa: eliminar PublicKey inválida precocemente

Para PublicKey.P == 0, Verify falhará. Nem tente.

--- Robert Griesemer

e o código corrigido:

https://github.com/golang/go/blob/master/src/crypto/dsa/dsa.go#L247

// Verify verifies the signature in r, s of hash using the public key, pub. It
// reports whether the signature is valid.
//
// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
// to the byte-length of the subgroup. This function does not perform that
// truncation itself.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
    // FIPS 186-3, section 4.7

    // Code fix added to check if the key parameters are sensible.
    if pub.P.Sign() == 0 {
        return false
    }

    if r.Sign() < 1 || r.Cmp(pub.Q) >= 0 {
        return false
    }
    if s.Sign() < 1 || s.Cmp(pub.Q) >= 0 {
        return false
    }

    w := new(big.Int).ModInverse(s, pub.Q)

    n := pub.Q.BitLen()
    if n&7 != 0 {
        return false
    }
    z := new(big.Int).SetBytes(hash)

    u1 := new(big.Int).Mul(z, w)
    u1.Mod(u1, pub.Q)
    u2 := w.Mul(r, w)
    u2.Mod(u2, pub.Q)
    v := u1.Exp(pub.G, u1, pub.P)
    u2.Exp(pub.Y, u2, pub.P)
    v.Mul(v, u2)
    v.Mod(v, pub.P)
    v.Mod(v, pub.Q)

    return v.Cmp(r) == 0
}

Para resumir a mensagem de commit e a correção de código acima: no Go 1.6 e versões anteriores, há um bug na função Verify do pacote crypto/dsa. Se alguém chamar Verify com o parâmetro de chave pública P definido como 0, isso causará um loop infinito em uma das declarações mais adiante na função Verify.

Uma breve pausa para explicar o DSA. DSA é um algoritmo de assinatura digital que usa criptografia assimétrica para assinar uma mensagem que posteriormente pode ser usada para garantir que a mensagem foi de fato enviada pelo remetente/detentor da chave privada. Um exemplo simples: Alice envia uma mensagem para Bob dizendo onde e quando eles devem se encontrar para o almoço. Bob, no entanto, quer ter certeza de que Alice é realmente quem lhe enviou a mensagem e não outra pessoa. Para que isso funcione, Alice assinará a mensagem com sua chave privada e Bob poderá verificar a assinatura de Alice com a chave pública que ele conhece. Ninguém além do detentor da chave privada pode assinar uma mensagem que possa então ser verificada pela chave pública correspondente (essa é a ideia, pelo menos).

Para funcionar, o DSA precisa de 5 números grandes. Os primeiros 3 números são conhecidos como parâmetros DSA P, Q e G. Estes definem o grupo subjacente e o gerador do grupo. Esses números podem ser criados adequadamente com uma chamada a dsa.GenerateParameters().

type Parameters struct {
        P, Q, G *big.Int
}

Os dois últimos números necessários para o DSA são a chave privada X e a chave pública correspondente Y. Esses números também podem ser criados com uma chamada a dsa.GenerateKey().

type PrivateKey struct {
        PublicKey
        X *big.Int
}

type PublicKey struct {
        Parameters
        Y *big.Int
}

Isso é tudo que você precisa saber sobre DSA para acompanhar. Para mais informações, veja o padrão NIST: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf ou a página da wikipedia: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm.

Voltando ao tópico; onde está o loop infinito na função Verify? Com um pouco mais de investigação, você descobrirá que o código trava em:

v := u1.Exp(pub.G, u1, pub.P)

Este é o comentário para o método Exp():

// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y.
// See Knuth, volume 2, section 4.6.3.
func (z *Int) Exp(x, y, m *Int) *Int {

A partir do acima, você pode ver que o comentário especifica que quando m == 0, ele irá exponenciar sem redução modular. Quando você pega um número grande e o exponencia com outro número grande, o resultado também será um número REALMENTE GRANDE. Não estou muito familiarizado com como math/big funciona, mas acho que é isso que está acontecendo aqui. Exp() está processando esta exponenciação que levará um tempo muito, muito longo para ser concluída (pode muito bem ser infinito).

Aqui estão alguns números de exemplo sendo usados em uma chamada dsa.Verify() para Exp() coletados do código de teste abaixo:

Baixar ferramenta