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:
- Usuário informa o seu email e senha;
- O sistema verifica se o email está cadastrado;
- O sistema gera um hash da senha informada pelo o usuário;
- O sistema verifica o hash gerado no passo anterior é igual o hash cadastrado no banco;
- 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)
}
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
}
}
}
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()
}
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)
}
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
Top comments (0)