João Paulo Andrade
Especialista em segurança

Construindo seu port scanner com bibliotecas internas do Python

Postado 16 Fevereiro 2020
Homem olhando janela

Esse pequeno artigo mostrará, como você pode construir a port scanner com bibliotecas internas do Python e sem a necessidade de instalar bibliotecas externas.

Muitas vezes quando nós estamos executando um troubleshooting ou um pentest, nós precisamos verificar se há conectividade entre dois hosts. Normalmente nesse processo nos utilizamos ferramentas tais como: nmap, NetCat, Telnet, Hping3 e etc. Companhias que tem uma política de segurança executam um processo chamado "Hardening". Esse processo consiste em remover todas as ferramentas desnecessárias para o propósito do servidor.

O script mostrado aqui te ajudará nessa tarefa, consequentemente se você está executando um pentest gerará menos ruído. Ele executará um scan do tipo Full Connection, ou seja, o three handshake irá completar. Posteriormente em outro artigo, eu mostrarei como nós podemos executar um scan do tipo Stealth, Xmas e outros.

A seguir, eu listo todas as bibliotecas utilizadas no script.

A versão do Python utilizada foi: 3.8.1

Vamos começar!

Classe Principal

import sys
import errno
import os
import argparse
import ipaddress

class MyPortScanner(object):

	def __init__(self, target, portlist):
	self.target = target
	if type(portlist) is str:
		self.portlist = [int(x) for x in portlist.split(',')]
	else:
		self.portlist = portlist

Nessa primeira parte, nós somente importamos as bibliotecas necessárias e definimos o construtor da classe. Ele receberá dois parâmetros: IP alvo e portas. Se o usuário passar uma lista de portas separadas por virgulas, nos geraremos uma lista de inteiros com essa lista de portas.

Função para verificar portas

def check_port_socket_v4_tcp(self):

        print('--------------------------------')
        print('[+] Iniciando o scan...')
        print('[i] Host alvo: {}'.format(self.target))
        print('[i] Portas: {}'.format(self.portlist))

        try:
            for port in self.portlist:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(3)
                result = s.connect_ex((str(self.target), port))

			if result == 0:
				print('[+] Port {}: Open'.format(port))
			else:
				print('[!] Porta {}: Fechada'.format(port))
				print('\t[-] Código de erro: {}'.format(errno.errorcode[result]))
				print('\t[-] Mensagem: {}'.format(os.strerror(result)))
			s.close()
		except socket.error as e:
			print(str(e))
			print('[-] Erro de conexão!')
		sys.exit()
		print('[+] Script finalizado.')

Aqui nosso script começa a ser construído. Nós definimos a função chamada "check_port_socket_v4_tcp" que espera um parâmetro: a instancia da classe. Após isso algumas informações são mostradas e se inicia um loop para testar todas as portas na lista portlist. Na próxima linha nós criamos um objeto da classe socket com os seguintes parâmetros: AF_INET == IPv4 e SOCK_STREAM == TCP. Após isso nós configuramos um timeout para 3 segundos e finalmente nós testamos a porta e savamos o resultado do teste na variável "result"

Por fim nós verificamos o resultado, se o resultado é 0 então a porta está Aberta, caso contrário estará fechada por váras razões.

Testando o script

joaopaulo@Joaos-MacBook-Air scanner % python3 sample_port_scanner.py -t 54.207.20.104 -p 80,443,445
--------------------------------
[+] Iniciando o scan...
[i] Host alvo: 54.207.20.104
[i] Portas: [80, 443, 445]
[+] Porta 80: Aberta
[+] Porta 443: Aberta
[!] Porta 445: Fechada
        [-] Código de erro: EAGAIN
        [-] Mensagem: Resource temporarily unavailable
[+] Script finalizado.

Para testar seu script, abra uma sessão do prompt de comando e passe 2 parâmetros: -t para definir o alvo e -p (não é obrigatório) para definir as portas a serem verificadas. Por exemplo: python3 sample_port_scanner.py -t 54.207.20.104 -p 80,443,445

Código completo

# Import modules
import socket
import sys
import errno
import os
import argparse
import ipaddress

# Main Class
class MyPortScanner(object):

    # Constructor, receive two parameters: target = IP that will be scanned and list of port that will be tested
    def __init__(self, target, portlist):
        self.target = target
        if type(portlist) is str:
            self.portlist = [int(x) for x in portlist.split(',')]
        else:
            self.portlist = portlist

    # Function that performs the scan on v4 family
    def check_port_socket_v4_tcp(self):

        print('--------------------------------')
        print('[+] Initializing scan...')
        print('[i] Target host: {}'.format(self.target))
        print('[i] Ports: {}'.format(self.portlist))

        try:
            for port in self.portlist:
                # Create the v4 socket, AF_INET == V4 Family, SOCK_STREAM == TCP
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                # Define the timeout of response to 3 seconds
                s.settimeout(3)
                result = s.connect_ex((str(self.target), port))
                # If the return code is 0 then the port is OPEN
                if result == 0:
                    print('[+] Port {}: Open'.format(port))
                # Otherwise, the port is closed
                else:
                    print('[!] Port {}: Closed'.format(port))
                    print('\t[-] Code error: {}'.format(errno.errorcode[result]))
                    print('\t[-] Message: {}'.format(os.strerror(result)))
                s.close()
        # If have any problem with connection, the scan will be aborted
        except socket.error as e:
            print(str(e))
            print('[-] Connection Error')
            sys.exit()

        print('[+] Script finished.')


# Performs the script
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Scan ports TCP\nVersion: 0.1')
    # Target parameter, accept just IPV4 Address
    parser.add_argument('-t', dest='target_host_v4', help='Target host IPv4', required=True, type=ipaddress.IPv4Address)
    # Port that will be scanned, if this parameter not been set then the default ports will be scanned
    parser.add_argument('-p', dest='ports', help='Ports separated by comma', type=str, default=[21, 22, 23, 53, 80, 443,
                                                                                                3389, 389, 3306, 1521,
                                                                                                8080, 8000])
    params = parser.parse_args()
    # Create an instance of MyPortScanner
    m = MyPortScanner(params.target_host_v4, params.ports)
    # Call the function check_port_socket_v4_tcp
    m.check_port_socket_v4_tcp()

Download do código no GitHub: Simples port scanner

Qualquer pergunta, dúvida, crítica ou sugestão, mande-me um email