Como Escanear Portas em um Website com Python
Tags: python, website, network, tutorial
Published: 2024-06-27 00:05:12

Você já deve ter ouvido falar do Nmap e de escaneamento de portas em servidores, bem, nesse script feito em Python vamos fazer algo bem semelhante, vamos verificar as portas abertas em websites. Vamos explorar um pouco de maneira simples e fácil de entender

## Introdução

Portas abertas em um servidor são como portas de entrada para diferentes serviços. Saber quais portas estão abertas pode ajudar você a entender melhor a segurança do seu site ou simplesmente satisfazer sua curiosidade sobre o funcionamento interno de um site. Vamos mergulhar em um script que escaneia essas portas usando Python.

## Código completo

```
import socket
import argparse
from concurrent.futures import ThreadPoolExecutor

# Função para verificar uma única porta
def scan_port(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)  # Define um timeout de 1 segundo
    try:
        sock.connect((host, port))
        return port, True
    except (socket.timeout, socket.error):
        return port, False
    finally:
        sock.close()

# Função para escanear uma lista de portas
def scan_ports(host, ports, max_workers=100):
    open_ports = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(scan_port, host, port) for port in ports]
        for future in futures:
            port, is_open = future.result()
            if is_open:
                open_ports.append(port)
    return open_ports

# Mapear portas para serviços comuns
port_service_map = {
    21: 'ftp', 22: 'ssh', 23: 'telnet', 25: 'smtp', 53: 'domain', 80: 'http',
    110: 'pop3', 123: 'ntp', 135: 'msrpc', 139: 'netbios-ssn', 143: 'imap',
    161: 'snmp', 194: 'irc', 389: 'ldap', 443: 'https', 445: 'microsoft-ds',
    465: 'smtps', 512: 'exec', 513: 'login', 514: 'shell', 587: 'submission',
    636: 'ldaps', 873: 'rsync', 990: 'ftps', 993: 'imaps', 995: 'pop3s',
    1080: 'socks', 1194: 'openvpn', 1433: 'ms-sql-s', 1434: 'ms-sql-m',
    1521: 'oracle', 1723: 'pptp', 3306: 'mysql', 3389: 'ms-wbt-server',
    5060: 'sip', 5432: 'postgresql', 5900: 'vnc', 5984: 'couchdb', 6379: 'redis',
    6667: 'irc', 8000: 'http-alt', 8080: 'http-proxy', 8443: 'https-alt',
    8888: 'sun-answerbook', 9000: 'cslistener', 9200: 'wap-wsp', 10000: 'webmin',
    11211: 'memcached', 27017: 'mongodb'
}

def main():
    # Configurar o parser de argumentos
    parser = argparse.ArgumentParser(description="Scan ports on a specified website")
    parser.add_argument("website", help="The website to scan, e.g., www.example.com")
    args = parser.parse_args()

    # Obter o site a partir dos argumentos
    website = args.website

    # Definir as portas a serem escaneadas (ports mais comuns escaneadas pelo nmap)
    ports_to_scan = list(port_service_map.keys())

    # Obter o endereço IP do site
    try:
        host = socket.gethostbyname(website)
    except socket.gaierror:
        print(f"Não foi possível resolver o hostname: {website}")
        return

    print(f"Iniciando escaneamento de {website} ({host})...")

    # Escanear as portas e exibir as portas abertas
    open_ports = scan_ports(host, ports_to_scan)

    print(f"PORTA     ESTADO       SERVIÇO")
    for port in ports_to_scan:
        state = "aberta" if port in open_ports else "fechada"
        service = port_service_map.get(port, "unknown")
        print(f"{port:<9} {state:<12} {service}")

    print("Escaneamento concluído.")

if __name__ == "__main__":
    main()
```

### Explicação do Script

Nosso script utiliza algumas bibliotecas essenciais do Python: `socket`, `argparse` e `concurrent.futures.ThreadPoolExecutor`. Aqui está um passo a passo do que cada parte do script faz:

**Importações e Configurações Iniciais**

```
import socket
import argparse
from concurrent.futures import ThreadPoolExecutor
```

Esses comandos importam os módulos necessários para o nosso script. `socket` é usado para criar conexões de rede, `argparse` para lidar com argumentos de linha de comando, e `concurrent.futures.ThreadPoolExecutor` para executar tarefas em paralelo, aumentando a eficiência do nosso escaneamento de portas.

---
**Função para Verificar uma Porta**

```
def scan_port(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)  # Define um timeout de 1 segundo
    try:
        sock.connect((host, port))
        return port, True
    except (socket.timeout, socket.error):
        return port, False
    finally:
        sock.close()
```
A função `scan_port` tenta conectar a uma porta específica em um host. Se a conexão for bem-sucedida, a porta está aberta; caso contrário, está fechada. O timeout de 1 segundo garante que a tentativa de conexão não demore muito.

---
**Função para Escanear Múltiplas Portas**

```
def scan_ports(host, ports, max_workers=100):
    open_ports = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(scan_port, host, port) for port in ports]
        for future in futures:
            port, is_open = future.result()
            if is_open:
                open_ports.append(port)
    return open_ports
```

A função `scan_ports` utiliza `ThreadPoolExecutor` para escanear várias portas ao mesmo tempo. Ela cria uma lista de tarefas, cada uma verificando uma porta diferente, e armazena as portas abertas em uma lista.

---
**Mapeamento de Portas para Serviços Comuns**

```
port_service_map = {
    21: 'ftp', 22: 'ssh', 23: 'telnet', 25: 'smtp', 53: 'domain', 80: 'http',
    110: 'pop3', 123: 'ntp', 135: 'msrpc', 139: 'netbios-ssn', 143: 'imap',
    161: 'snmp', 194: 'irc', 389: 'ldap', 443: 'https', 445: 'microsoft-ds',
    465: 'smtps', 512: 'exec', 513: 'login', 514: 'shell', 587: 'submission',
    636: 'ldaps', 873: 'rsync', 990: 'ftps', 993: 'imaps', 995: 'pop3s',
    1080: 'socks', 1194: 'openvpn', 1433: 'ms-sql-s', 1434: 'ms-sql-m',
    1521: 'oracle', 1723: 'pptp', 3306: 'mysql', 3389: 'ms-wbt-server',
    5060: 'sip', 5432: 'postgresql', 5900: 'vnc', 5984: 'couchdb', 6379: 'redis',
    6667: 'irc', 8000: 'http-alt', 8080: 'http-proxy', 8443: 'https-alt',
    8888: 'sun-answerbook', 9000: 'cslistener', 9200: 'wap-wsp', 10000: 'webmin',
    11211: 'memcached', 27017: 'mongodb'
}
```

Aqui, temos um dicionário que mapeia números de portas para seus serviços comuns, como FTP, SSH e HTTP. Isso ajuda a identificar rapidamente quais serviços estão rodando nas portas abertas.

---
**Função Principal**

```
def main():
    # Configurar o parser de argumentos
    parser = argparse.ArgumentParser(description="Scan ports on a specified website")
    parser.add_argument("website", help="The website to scan, e.g., www.example.com")
    args = parser.parse_args()

    # Obter o site a partir dos argumentos
    website = args.website

    # Definir as portas a serem escaneadas (ports mais comuns escaneadas pelo nmap)
    ports_to_scan = list(port_service_map.keys())

    # Obter o endereço IP do site
    try:
        host = socket.gethostbyname(website)
    except socket.gaierror:
        print(f"Não foi possível resolver o hostname: {website}")
        return

    print(f"Iniciando escaneamento de {website} ({host})...")

    # Escanear as portas e exibir as portas abertas
    open_ports = scan_ports(host, ports_to_scan)

    print(f"PORTA     ESTADO       SERVIÇO")
    for port in ports_to_scan:
        state = "aberta" if port in open_ports else "fechada"
        service = port_service_map.get(port, "unknown")
        print(f"{port:<9} {state:<12} {service}")

    print("Escaneamento concluído.")

if __name__ == "__main__":
    main()
```

A função `main` faz todo o trabalho de configurar o escaneamento. Primeiro, ela define os argumentos de linha de comando e obtém o website a ser escaneado. Depois, ela resolve o nome do host para um endereço IP e começa a escanear as portas mais comuns. Finalmente, ela exibe o estado de cada porta (aberta ou fechada) junto com o serviço correspondente.

### Exemplo:

```
[user@hostname ]$ python scan.py www.google.com  
Iniciando escaneamento de www.google.com (142.250.79.4)...  
PORTA     ESTADO       SERVIÇO  
21        fechada      ftp  
22        fechada      ssh  
23        fechada      telnet  
25        fechada      smtp  
53        fechada      domain  
80        aberta       http  
110       fechada      pop3  
123       fechada      ntp  
135       fechada      msrpc  
139       fechada      netbios-ssn  
143       fechada      imap  
161       fechada      snmp  
194       fechada      irc  
389       fechada      ldap  
443       aberta       https  
445       fechada      microsoft-ds  
465       fechada      smtps  
512       fechada      exec  
513       fechada      login  
514       fechada      shell  
587       fechada      submission  
636       fechada      ldaps  
873       fechada      rsync  
990       fechada      ftps  
993       fechada      imaps  
995       fechada      pop3s  
1080      fechada      socks  
1194      fechada      openvpn  
1433      fechada      ms-sql-s  
1434      fechada      ms-sql-m  
1521      fechada      oracle  
1723      fechada      pptp  
3306      fechada      mysql  
3389      fechada      ms-wbt-server  
5060      fechada      sip  
5432      fechada      postgresql  
5900      fechada      vnc  
5984      fechada      couchdb  
6379      fechada      redis  
6667      fechada      irc  
8000      fechada      http-alt  
8080      fechada      http-proxy  
8443      fechada      https-alt  
8888      fechada      sun-answerbook  
9000      fechada      cslistener  
9200      fechada      wap-wsp  
10000     fechada      webmin  
11211     fechada      memcached  
27017     fechada      mongodb  
Escaneamento concluído.  
[user@hostname ]$
```

### Considerações finais

Este script em Python é uma maneira simples e prática de verificar quais portas estão abertas em um site, o que pode ser útil para fins de segurança ou simples curiosidade.. Sinta-se à vontade para personalizar e expandir este script conforme suas necessidades e busque aprender mais sobre o mundo da programação de redes.