Construindo um Scanner de Portas com Python

PythonBeginner
Pratique Agora

Introdução

Neste projeto, desenvolveremos um scanner de portas de servidor utilizando Python para detectar portas abertas em um alvo específico. Esta ferramenta é indispensável tanto para administradores de sistemas, que precisam validar políticas de segurança, quanto para profissionais de segurança que buscam mapear serviços operacionais em um host. Ao longo deste guia, exploraremos os fundamentos do escaneamento de portas, sua metodologia e aplicações práticas. Criaremos um scanner funcional e eficiente, utilizando o poder do Python e implementando threads para otimizar o tempo de execução.

O escaneamento de portas (port scanning) consiste no envio de requisições a uma série de portas de um servidor para determinar quais estão acessíveis. Este processo é vital para a manutenção da segurança e para a identificação de serviços vulneráveis. Começaremos explorando os conceitos básicos e o impacto dessa técnica.

Conceitos-Chave:

  • Scanners de portas auxiliam na detecção de portas abertas em um servidor ou host.
  • São utilizados em auditorias de segurança e para identificar serviços ativos que possam estar expostos.
  • A forma mais simples de escaneamento envolve a tentativa de estabelecer conexões TCP em um intervalo de portas.

👀 Prévia

Aqui está uma demonstração da ferramenta que vamos construir:

python port_scanner.py 127.0.0.1 5000-9000

Saída esperada:

Opened Port: 8081
Scanning completed.

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como utilizar as capacidades de programação de sockets do Python para interagir com portas de rede.
  • Como implementar uma abordagem multi-threaded em Python para acelerar tarefas de varredura de rede.
  • Como desenvolver uma ferramenta de linha de comando em Python que aceita entradas do usuário para um escaneamento flexível.

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Usar a biblioteca socket do Python para criar conexões de rede, testar a disponibilidade de portas e tratar exceções de rede.
  • Compreender e aplicar multi-threading em Python para executar tarefas concorrentes, melhorando significativamente o desempenho em operações intensivas de rede.
  • Construir um scanner de portas prático via linha de comando, aprimorando suas habilidades de scripting e manipulação de argumentos.

Estabelecendo Testes de Conexão TCP

Nosso primeiro passo consiste em escrever uma função para testar se uma porta TCP está aberta no IP de destino. Esta função tentará se conectar a uma porta específica e determinará seu status.

Abra o arquivo chamado port_scanner.py no diretório /home/labex/project e adicione a seguinte função Python:

import argparse
from queue import Queue
from socket import AF_INET, gethostbyname, socket, SOCK_STREAM
import threading

def tcp_test(port: int, target_ip: str) -> None:
    with socket(AF_INET, SOCK_STREAM) as sock:
        sock.settimeout(1)
        result = sock.connect_ex((target_ip, port))
        if result == 0:
            print(f"Opened Port: {port}")

Este código define a função tcp_test, que tenta estabelecer uma conexão TCP com uma porta específica no endereço IP alvo. Definimos um tempo limite (timeout) para evitar esperas prolongadas em cada porta e utilizamos o método connect_ex para realizar a tentativa.

Se o connect_ex retornar 0, isso indica que a conexão foi bem-sucedida, o que significa que a porta está aberta. Nesse caso, a função exibe uma mensagem informando a porta encontrada.

✨ Verificar Solução e Praticar

Configurando Verificações de Portas Concorrentes

Em seguida, criaremos uma função worker que será utilizada por cada thread para processar as portas de uma fila (queue) e utilizar a função tcp_test para verificar o status de cada uma.

Adicione o seguinte código ao seu arquivo port_scanner.py:

def worker(target_ip: str, queue: Queue) -> None:
    while not queue.empty():
        port = queue.get()
        tcp_test(port, target_ip)
        queue.task_done()

A função worker foi projetada para ser executada por múltiplas threads simultaneamente. Ela recupera continuamente números de portas de uma fila e utiliza a função tcp_test definida anteriormente para checar se essas portas estão abertas no IP alvo.

Após testar uma porta, ela marca a tarefa como concluída na fila. Esta função permite o escaneamento concorrente, o que acelera drasticamente o processo de varredura.

✨ Verificar Solução e Praticar

Orquestrando o Processo de Escaneamento

A função main orquestra todo o processo, configurando a fila, inicializando as threads de trabalho e gerenciando a operação de varredura sobre o intervalo de portas especificado.

Continue adicionando a função main ao seu port_scanner.py:

def main(host: str, start_port: int, end_port: int) -> None:
    target_ip = gethostbyname(host)
    queue = Queue()
    for port in range(start_port, end_port + 1):
        queue.put(port)
    for _ in range(100):  ## Ajuste o número de threads se necessário
        t = threading.Thread(target=worker, args=(target_ip, queue,))
        t.daemon = True
        t.start()
    queue.join()
    print("Scanning completed.")

A função main é o coração do script.

Primeiro, ela resolve o nome do host para um endereço IP e cria uma fila para as portas que serão escaneadas. Ela preenche essa fila com todas as portas do intervalo definido e inicia múltiplas threads (100 neste exemplo) que processam a fila de forma concorrente. A função aguarda até que todas as portas tenham sido processadas (queue.join()) e então exibe uma mensagem indicando a conclusão do trabalho.

✨ Verificar Solução e Praticar

Executando o Scanner de Portas

Finalmente, é hora de colocar nosso scanner para funcionar. Vamos varrer um intervalo de portas no localhost para testar a ferramenta.

Adicione as seguintes linhas ao final do seu arquivo port_scanner.py para tornar o script executável via terminal:

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='TCP Port Scanner')
    parser.add_argument('host', help='Host to scan')
    parser.add_argument('ports', help='Port range to scan, formatted as start-end')
    args = parser.parse_args()

    start_port, end_port = map(int, args.ports.split('-'))
    main(args.host, start_port, end_port)

Este trecho utiliza a biblioteca argparse para lidar com argumentos de linha de comando, tornando o script versátil e fácil de usar.

  • O argumento host define o endereço ou nome do host alvo.
  • O argumento ports espera uma string no formato "início-fim" (ex: "5000-8000"), que o script divide em start_port e end_port usando o delimitador - e converte para inteiros.

Agora, execute o scanner rodando o seguinte comando no terminal:

python port_scanner.py 127.0.0.1 5000-9000

Saída:

Opened Port: 8081
Scanning completed.

Agora que você identificou uma porta aberta (8081), indicando que há um servidor web rodando nela, você pode explorar o que está sendo hospedado.

Para fazer isso, primeiro clique no botão de adicionar e selecione a opção "Web Service".

Selecionar opção Web Service

Em seguida, digite a porta 8081 e clique no botão "Access".

Digitar porta e acessar servidor web

Você verá o servidor web em execução no seu sistema, escutando na porta 8081.

Servidor web rodando na porta 8081
✨ Verificar Solução e Praticar

Resumo

Neste projeto, construímos um scanner de portas básico com Python, partindo da função fundamental tcp_test até uma implementação multi-threaded robusta. Dividimos o processo em etapas gerenciáveis, resultando em uma ferramenta funcional capaz de identificar portas abertas em um servidor. Através desta experiência prática, não apenas aprofundamos nosso conhecimento em segurança de rede, mas também aprimoramos nossas habilidades em programação Python e gerenciamento de concorrência.