Conecta-se a portas para ler a versão do serviço (ex: Apache 2.4.41).

Contexto

Após identificar quais portas estão abertas (via Port Scanner), o próximo passo lógico de um Pentester é o Banner Grabbing. O objetivo é coletar a "assinatura" ou o "banner" que o serviço exibe ao receber uma conexão inicial.

Essa informação é ouro puro, pois geralmente revela:

  • Nome do Software: (ex: Apache, OpenSSH, Microsoft-IIS).
  • Versão Exata: (ex: 2.4.41).
  • Sistema Operacional: (ex: Ubuntu, Debian, Win64).

Com a versão exata em mãos, o Pentester pode buscar por exploits específicos em bases de dados como o CVE (Common Vulnerabilities and Exposures).

Possibilidades de Implementação

  • Socket Puro (Leitura Direta): Abre uma conexão e aguarda o servidor enviar o banner espontaneamente (comum em portas como 21/FTP e 22/SSH).
  • Requisição Forçada (HTTP/S): Envia uma string específica (como HEAD / HTTP/1.1) para forçar o servidor web a responder com o cabeçalho "Server".
  • Timeouts Agressivos: Como muitos serviços não enviam banners imediatamente, o uso de timeouts curtos evita que o script trave em portas silenciosas.

Código

Este script tenta se conectar a uma lista de portas e ler os primeiros bytes de resposta. Para portas web (80/443), ele envia uma pequena requisição para "provocar" a resposta.

import socket

def grab_banner(ip, port):
    """
    Tenta capturar o banner de um serviço em uma porta específica.
    """
    try:
        # Cria o socket TCP
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(3.0) # Espera até 3 segundos pela resposta

        # Tenta a conexão
        sock.connect((ip, port))

        # Para portas web, precisamos enviar algo para receber o banner no cabeçalho
        if port in [80, 443, 8080]:
            sock.send(b"HEAD / HTTP/1.1\r\nHost: " + ip.encode() + b"\r\n\r\n")

        # Recebe até 1024 bytes da resposta
        banner = sock.recv(1024)
        sock.close()

        # Decodifica e limpa a resposta para exibição
        return banner.decode().strip()
    except Exception:
        return None

def main():
    print("-" * 60)
    print("Módulo: Service Banner Grabber | Projeto: 30 Lessons in Python")
    print("-" * 60)

    alvo = "scanme.nmap.org"
    portas_comuns = [21, 22, 25, 80, 443, 3306]

    try:
        ip_alvo = socket.gethostbyname(alvo)
        print(f"[*] Alvo: {alvo} ({ip_alvo})\n")

        for porta in portas_comuns:
            print(f"[*] Tentando porta {porta}...", end="\r")
            banner = grab_banner(ip_alvo, porta)

            if banner:
                # Exibimos apenas a primeira linha do banner para manter a limpeza
                print(f"[+] PORTA {porta:5} | BANNER: {banner.splitlines()[0]}")
            else:
                print(f"[-] PORTA {porta:5} | NENHUM BANNER CAPTURADO")

    except socket.gaierror:
        print("[!] Erro: Não foi possível resolver o host.")

    print("\n[*] Fim da coleta.")

if __name__ == "__main__":
    main()

Observações

  • Banners Falsos: Administradores de sistemas experientes podem configurar serviços para exibirem versões falsas (obscurecimento) para enganar atacantes.
  • Codificação: Alguns banners podem conter caracteres binários ou usar codificações diferentes de UTF-8; por isso, o uso de .decode(errors='ignore') pode ser necessário em versões mais robustas.
  • Protocolos Diferentes: Alguns serviços exigem que você siga o protocolo (ex: enviar um comando AUTH no FTP) antes de liberarem o banner real.