DEV Community

Higor Diego
Higor Diego

Posted on

Criando decrypt hash com Golang

scanner

Nos sistemas atuais é comum ter a funcionalidade de autenticação de usuário, que trazem algumas formas de proteger as senhas cadastradas, se utilizando de tecnologia de criptografia para guardar a informação de forma segura. Essas informações são salvas em bancos de dados que se utilizam de algoritmos de criptografia como: md5, sha1, sha256, sha512, entre outros. Esses algoritmos permitem identificar se a senha fornecida corresponde a que está salva no sistema.
Logo abaixo temos um caso de uso para um sistema que autentica o seu usuário:

  1. Usuário informa o seu email e senha;
  2. O sistema verifica se o email está cadastrado;
  3. O sistema gera um hash da senha informada pelo o usuário;
  4. O sistema verifica o hash gerado no passo anterior é igual o hash cadastrado no banco;
  5. O sistema informa se o usuário foi encontrado ou não, com base na comparação dos dados.

O passo três é o que nos interessa nesse processo, pois varios sistemas geram hash para proteger as senhas e dados sensíveis de atos criminosos. Mas se o banco de dados for comprometido e exposto na internet o hash de senha dos usuários ficará aberto para consulta, e com isso poderemos analisar as listas disponíveis.

No passo quatro, geralmente usado nos sistemas atuais, é verificada a senha do usuário, fazendo a comparação das criptografias, caso sejam iguais, o mesmo libera o acesso com uma simples comparação de string.

Segue o caso de uso abaixo:

  • O usuário tem uma senha cadastrada no sistema, cuja o valor é "123456";
  • O hash dessa senha criptografada com algoritmo de MD5 "e10adc3949ba59abbe56e057f20f883e".

Analisando os passos acima, se a senha transformada em hash vazar temos um criptografia que não condiz com o valor de "123456", neste caso podemos fazer o inverso para identiticar qual senha é contida nesse hash. Para isso vamos realizar um ataque de força bruta para identificar se hash está contido na lista que iremos criar. Faremos uma lista que contém as seguintes senhas:


package main

import "fmt"

func main(){
  // lista de senha
  lista := []string{"123", "1234", "321", "4321", "12345", "54321", "123456", "654321"}
  fmt.Println(lista)
}
Enter fullscreen mode Exit fullscreen mode

Na lista acima as senha não estão criptografada e com isso partimos para a segunda parte que seria criptografar os dados, segue a abaixo:

package main

import (
    "fmt"
    // pacote para criptografia em md5
    "crypto/md5"
)

// função para criptografar o texto limpo
func createHash(key string) string {
  // iniciando o modulo de md5
    hasher := md5.New()
  // transformando a string para byte e escrevendo o hash
    hasher.Write([]byte(key))
  // retornando o hash em md5
    return hex.EncodeToString(hasher.Sum(nil))
}


func main() {
  // senha que gostaria de identificar
  senha := "e10adc3949ba59abbe56e057f20f883e"
  // lista de senhas
  lista := []string{"123", "1234", "321", "4321", "12345", "54321", "123456", "654321"}
  // iterando nas senhas
  for _, v := range lista {
    // verificando se o hash da senha é igual o que estou querendo descobrir
    if createHash(v) == senha {
      // caso o hash seja valido
      fmt.Println("senha é: ", v)
      // parando a iteração
      break
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Analisando o código acima temos um problema, que é ter uma lista bem definida para que possamos achar a senha informada. E sabemos que isso é bem complicado porque existem inumeras combinações de senhas que teriamos que gerar para comparar com o nosso hash. Pensando nessa problemática temos varios sites que fazem esse trabalho, como: md5hashing, hashes, dcode e hashtoolkit.

Mas a intenção não é usar uma ferramenta que já existe, e sim criar uma do zero. E para isso temos que elaborar um arquivo que tenha uma lista composta com várias senhas, e essas senhas devem ser mais assertiva e não randômicas, pois é mais demorado e oneroso.

Para compor esse arquivo existem senhas comuns, ou que foram expostas na internet, e com isso podemos reutiliza-las aqui para identificar o nosso hash. Esses são alguns sites que podemos encontrar senhas vazadas na internet em um arquivo de texto,segue os links abaixo:

Analisando a lista acima podemos iniciar o nosso software para encontrar a senha que está em hash de md5 para fazer a comparação. Neste caso, temos o seguinte código abaixo:


package main

import (
    "bufio"
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "os"
)

func createHash(key string) string {
    // iniciando o modulo de md5
    hasher := md5.New()
    // transformando a string para byte e escrevendo o hash
    hasher.Write([]byte(key))
    // retornando o hash em md5
    return hex.EncodeToString(hasher.Sum(nil))
}

func main() {
    // lendo o diciário das senhas
    file, err := os.Open("dictionary.txt")

    // a senha para comparação
    password := "e10adc3949ba59abbe56e057f20f883e"

    // comparação se houver erro na leitura do dicionário
    if err != nil {
        panic(err)
    }

    // lendo o arquivo em
    fileScanner := bufio.NewScanner(file)

    // iterando
    for fileScanner.Scan() {
        // comparando a senha com o hash gerado
        if createHash(fileScanner.Text()) == password {
            // caso caia aqui senha encontrada
            fmt.Println("a senha é: ", fileScanner.Text())
        }
    }

    // fechando a conexão com o arquivo.
    defer file.Close()

}
Enter fullscreen mode Exit fullscreen mode

Esse código lê o arquivo chamado de dictionary.text com todas as senhas baixadas dos sites aqui informados, esse arquivo pode ser gigante e assim teremos uma nova problemática de performance. Neste caso podemos dividir esse arquivo em varios outros e fazer a leitura usando concorrencia com goroutine e canais, como no exemplo abaixo:


package main

import (
    "bufio"
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "os"
)

func createHash(key string) string {
    // iniciando o modulo de md5
    hasher := md5.New()
    // transformando a string para byte e escrevendo o hash
    hasher.Write([]byte(key))
    // retornando o hash em md5
    return hex.EncodeToString(hasher.Sum(nil))
}

func goRoutines(password, v string, passwordFound chan<- string) {

    // abrindo o arquivo informado
    file, err := os.Open(v)

    // caso não consiga encontrar o arquivo
    if err != nil {
        // informando o erro
        panic(err)
    }

    // lendo o arquivo aberto
    fileScanner := bufio.NewScanner(file)

    // iterando as linhas dos arquivos
    for fileScanner.Scan() {
        // validando o hash é igual a senha passado
        if createHash(fileScanner.Text()) == password {
            // pegando a senha encontrada e passando para o canal
            passwordFound <- fileScanner.Text()
            // parando a iteração
            break
        }
    }
    // fechando o arquivo
    defer file.Close()

}

func main() {
    // a senha para comparação
    password := "e10adc3949ba59abbe56e057f20f883e"

    // lista de arquivos
    listFiles := []string{"dictionary1.txt", "dictionary2.txt"}

    // criando um canal caso a senha seja encontrada
    passwordFound := make(chan string)

    // iterando na lista de arquivos
    for _, v := range listFiles {
        // criando goroutines para comparação de cada lista
        go goRoutines(password, v, passwordFound)
    }

    // informando a senha que está valida.
    fmt.Println("Senha encontrada: ", <-passwordFound)
}
Enter fullscreen mode Exit fullscreen mode

No código acima temos uma hash decrypt com algoritmo de MD5 que verifica cada item da nossa lista de forma concorrente, com o propósito de identificar a senha. E para melhorias futuras podemos utilizar outras criptografias e até descobrir qual criptografia está no hash.

Espero ter ajudado, até próxima!

Referências

https://www.linkedin.com/pulse/goroutines-e-concorr%C3%AAncia-jefferson-otoni-lima/?originalSubdomain=pt

https://pkg.go.dev/crypto/md5

Top comments (0)