Criando decrypt hash com Golang
Tags: go, hash, decrypt, tutorial
Published: 2022-01-17 16:42:05

![scanner](https://higordiego.com.br/posts/img/golang/crypt-golang.png)

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:

```go

package main

import "fmt"

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


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:

```go
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
    }
  }
}
```


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](https://md5hashing.net/hash), [hashes](https://hashes.com/en/decrypt/hash), [dcode](https://www.dcode.fr/hash-function) e [hashtoolkit](https://hashtoolkit.com/). 

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:

   - [SecList](https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt)
   - [Weakpass](https://weakpass.com/wordlist)
   - [kagle](https://www.kaggle.com/wjburns/common-password-list-rockyoutxt)
   - [crackstation](https://crackstation.net/crackstation-wordlist-password-cracking-dictionary.htm)


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:

```go

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()

}
```


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:


```go

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)
}
```


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