;

se você gosta de ir ao WordCamps como eu, provavelmente já ouviu isso: “o hash de senha do WordPress não é Seguro” ou na versão mais técnica: “…porque é baseado em md5”.Verdadeiro ou não, um forte hash de senha é crucial para um grande ecossistema como o WordPress, que sempre foi um alvo interessante para hackers. Então, decidi dar uma olhada mais de perto no sistema de hashing e tentar quebrar os hashes do WordPress do zero!

Entendendo os hashes de senha do WordPress

comecei a fazer algumas pesquisas no Google e descobri que a maioria das informações por aí é genérica e confusa. Muitas referências às bibliotecas PHP usadas (hash portátil do phpass), mas nada realmente Concreto.

decidi adotar uma abordagem diferente a partir de uma suposição:

hash é um processo unidirecional, mas o WordPress é já capaz de autenticar usuários correspondentes a sua senha de entrada com o hash armazenado no banco de dados

a Partir daí, comecei a verificar o código e encontrou a primeira função interessante: wp_check_password($password,$hash) o que compara com a senha de texto sem formatação com o condensado e retorna true se eles combinam.

1// presume the new style phpass portable hash.
2if ( empty( $wp_hasher ) ) {
3 require_once ABSPATH . WPINC . '/class-phpass.php';
4 // By default, use the portable hash from phpass.
5 $wp_hasher = new PasswordHash( 8, true );
6}
7
8$check = $wp_hasher->CheckPassword( $password, $hash );
9
10/** This filter is documented in wp-includes/pluggable.php */
11return apply_filters( 'check_password', $check, $password, $hash, $user_id );

Indo através do código, logo nos trazer a CheckPassword, crypt_private e encode64 que, basicamente, é onde a mágica acontece.

longa história curta, crypt_private($password, $stored_hash) re-hashes a senha antes de ser comparado com o hash armazenado. Se corresponderem, a senha está correta e a autenticação continua. O que significa que também podemos usar essa função para quebrar o hash.

Hash anatomia

Este é um hash do WordPress:

1$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1

Por simplicidade, vamos assumir que o site usa PHP>5 e o mais recente phpass portátil de hash, que é o mais comum de instalação.

os primeiros 3 caracteres $P$ são um ID, informando ao sistema que tipo Oh hash temos.

o caractere número 3 (contando de 0) é usado para determinar quantas vezes o md5 () tem que processar a string de entrada.

Caracteres de 4 a 12 nPVO4gP9 são o sal, que é uma string aleatória anexada à senha antes do hash, para dar mais aleatoriedade. Por exemplo, se sua senha for admin, ela será transformada em nPVO4gP9admin e depois em hash.

a parte restante do hash JUMSAM1WlLTHPdH6EDj4e1 é a aleatoriedade real, gerada pela senha salt+passada em uma função encode64 não documentada, que executa algumas operações bit a bit na string de entrada e retorna uma saída de 22 caracteres.

não está claro, uh? Fica comigo.

até agora sabemos:

  • a primeira parte do hash é uma fixo id
  • o segundo é um único char usado como contador, também fixa
  • o terceiro é o sal, amarrado a palavra-passe
  • o último é aleatório, gerado por “sal+pass” processado por encode64 função
1

Assim, podemos re-escrever a lógica de salt+hash de senha X vezes e passou em encode64 – para a realização de um dicionário ou de força-bruta ataque, e obter o mesmo “última parte” do hash e que seria um sucesso de hash crack!

em um cenário da vida real, e os hackers estariam mais interessados em encontrar senhas fracas porque são mais propensas a serem reutilizadas, em vez de aleatórias que geralmente são geradas por um gerenciador de senhas e, portanto, específicas do site.

reescrevendo encode64 usando golang

eu decidi ir com golang porque é muito rápido, e precisamos de toda essa velocidade para calcular grandes dicionários de senha.

assim como no procedimento de criptografia do WordPress, o script pega o hash e isola o salt, então ele compõe a senha (salt+pass) tentando cada senha no dicionário e md5-hashing x número de vezes, e vimos como X é determinado.

1itoa64 := "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2hashloop := 1 << strings.Index(itoa64, string(hash)); // char n. 4 in the hash

Feito isso, ele executa uma série de operação bit a bit em bytes obtidos usando ord() em PHP, o que não precisamos no golang como já temos os valores de byte:

1itoa64 & 0x3f]

neste ponto, eu percebi que nós não precisa mesmo de encontrar toda a cadeia. Basta combinar alguns caracteres para combinar com toda a senha e tornar o script mais eficiente! Então, devido à natureza do processo de hash, se char n. 0 / 4 / 8 … corresponde, a senha passada como entrada está correta.

E BOOM, rachou o hash:

1wphashcrash '$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1' Dev/wphashcrash/dict.txt
2$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1 admin
32020/05/30 12:44:15 Executed in 0.000221

O código é, como de costume no GitHub – https://github.com/francescocarlucci/wphashcrash – e, no momento, o script é um POC e recebe apenas uma única senha hash como entrada, e o caminho para a palavra-passe de dicionário.

já descrevi no readme algumas melhorias que seriam boas de ter e provavelmente adicionarei no futuro.

se você realmente quer tentar cortar uma lista de hashes, você pode bifurcá-lo ou apenas embrulhar o script em um bash para loop.

considerações Secuirty

neste ponto, acho que é claro o suficiente que, se um invasor obtém acesso ao banco de dados em um site WordPress, ele/ela pode basicamente quebrar cada senha fraca.

este infeliz, especialmente porque:

  • Injeção de SQL não é raro em plugins WordPress
  • RCE também irá provavelmente conceder acesso ao DB
  • WordPress ecossistema é um enorme território para o freelancer e o trabalho, é muito comum encontrar antigo/não utilizados wp-admin e contas de FTP, criados para necessidades temporárias

Este último ponto, combinado com a enumeração de usuário que é um plugin problema comum, deixa a porta aberta para a obtenção de acesso ao DB usando roubado/penhorado credenciais de administrador.

além disso, muitos sites não impõem senhas fortes para não prejudicar a experiência do Usuário, e tenho experiência direta em qualquer um dos pontos mencionados.

uma vez que um site é comprometido, ter hashes fracos armazenados permite que os invasores comprometam outras contas de usuário em outros sites, se eles tendem a reutilizar senhas e sabemos que sim.

temos soluções?

minha pesquisa não foi tão longe, eu só queria ver como quebrar os hashes do WordPress.

Bcrypt é conhecido como um forte hash do método comparado ao md5, e existe um plugin que usa bcrypt e substitui todas as principais funções necessárias para lidar com senhas:

  • wp_check_password()
  • wp_hash_password()
  • wp_set_password()

E, claro, você pode escrever sua própria solução, como um desenvolvedor, mas acredito que este é um problema para correção no nível central para apontar para uma grande adoção.

nota Final

Existem muitas ferramentas para quebrar hashes, como Hashcat e John The Ripper, que pode ser ainda mais alto desempenho, mas, novamente, o escopo desta pesquisa é compreender o WordPress hash estrutura e rachaduras a partir do zero.

obrigado pela leitura.

frenxi

Write a Comment

O seu endereço de email não será publicado.