Ir para conteúdo

Scan ARP como funciona... Script com python raw sockets


Visitante gnoo

Posts Recomendados

Saudações,

eu algum tempo atrás fiz um script em python com o objetivo de fazer scan ARP na minha LAN para efeitos educacionais... e não só ?️ ...  E decidi partilhar aqui esse script para quem quer tirar umas ideias .... E aqueles que quiserem dar umas sugestões de melhoria do código podem comentar para trocar umas ideias, para que eu possa também aprender coisas novas.

O scan ARP pode ser interessante em várias situções, uma delas é quando queremos fazer um scan na nossa rede, e por alguma razão outros hosts ativos na rede podem estar a bloquear pacotes ICMP, não ter portas abertas no sistema para fazer scan através de handshake.

Acho que o mais interessante disto tudo é perceber como um scan ARP funciona...

Então nós sabemos que o nosso sistema para iniciar conversação com outro host na rede,  precisa do endereço físico desse host ( MAC ADDRESS ), é ai que entra o protocolo ARP, para fazer a resolução de endereço do host que nós queremos comunicar com base no  endereço de IP que temos, fazendo um broadcast ( pergunta de um para todos  ), para todos os elementos estão ligados à rede, depois é nos devolvido uma resposta em Unicast ( resposta de um para um ) do host com o IP com o qual nos queremos comunicar. 

1118974843_Capturadeecr_2019-04-03_20-33-31.png.d78a78dd57174d2b4cd90faa268097b0.png

138185038_Capturadeecr_2019-04-03_20-37-45.thumb.png.6a6964e1e79e9c63a99b9266921eee14.png601168277_Capturadeecr_2019-04-03_20-41-07.png.1fe39caff6163709b1d32735edeeb17a.png

 

Então um scan ARP consiste em criar uma sequência de ip's, percorrer essa sequência de ip's num ciclo de repetição, a cada iteração desse ciclo de repetição criar um frame ethernet, criar um pacote ARP, adicionar o pacote ARP à carga do frame ethernet e enviar esse frame.

No meu caso eu criei dois sockets um para enviar o pacote e outro para ficar em "escuta" de frames ethernet, analisando o seu tipo de ethernet... se o tipo for ARP analisamos a carga desse frame,

e tentamos perceber se o tipo de operação do pacote que chega é 2, se essa condição for verdadeira então analisamos o ip de quem envia e o respetivo endereço fisico (  MAC ADDRESS  ), e realizamos a saida de dados.

 

ATENÇÂO: o script que segue deve ser melhorado especialmente na função em  que é feita a iteração e o empacotamento uma vez que o seu tempo de execução está a demorar em média 8 a 10 segundos, deve ser aplicado um sistema de  threads nessa função. 

O código que segue percisa de configurações... é necessário configurar o nome da placa de rede no socket que envia pacotes, tal como dados necessário para o pacote ARP

SEGUE SCRIPT:

#!/usr/bin/env python3

from socket import *
import struct
import binascii
from ipaddress import IPv4Network
import signal
import sys
import threading


# Esta class serve para fazer o pacote ARP
class Pacote_Arp(object):
    def __init__(self):
        self.tipo_hardware = None
        self.tipo_protocolo = None
        self.len_hardware = None
        self.len_protocolo = None
        self.operacao = None
        self.mac_src = None
        self.src_ip = None
        self.mac_dest = None
        self.dest_ip = None
        self.protocolo = None

class Gestor_Pacotes(object):
    def __init__(self, socket_family, socket_type, protocolo, cabecalho_arp):
        self.socket_family = socket_family
        self.socket_type = socket_type
        self.protocolo = ntohs(protocolo)
        self.lista_ip = IPv4Network('192.168.1.0/24')
        self.cabecalho_arp = cabecalho_arp
        
    def Mac_unhexlify(self):
        mac_dest_byte_order = binascii.unhexlify(self.cabecalho_arp.mac_dest.replace(":", ""))
        mac_src_byte_order = binascii.unhexlify(self.cabecalho_arp.mac_src.replace(":", ""))
        return mac_dest_byte_order, mac_src_byte_order

    def Empacotamento_Frame_ArpHeader(self, ip):
        src_ip = inet_aton(self.cabecalho_arp.src_ip)
        self.cabecalho_arp.dest_ip = ip
        dest_ip = inet_aton(str(ip))
        mac_dest_byte_order, mac_src_byte_order = self.Mac_unhexlify()
        frame = struct.pack("!6s6sH", mac_dest_byte_order, mac_src_byte_order, self.cabecalho_arp.protocolo)
        arp_header = struct.pack("!HHBBH6s4s6s4s", self.cabecalho_arp.tipo_hardware,
                                     self.cabecalho_arp.tipo_protocolo,
                                     self.cabecalho_arp.len_hardware,
                                     self.cabecalho_arp.len_protocolo,
                                     self.cabecalho_arp.operacao,
                                     mac_src_byte_order,
                                     src_ip,
                                     mac_dest_byte_order,
                                     dest_ip)

        return frame + arp_header
    
    def desempacotamento_header_arp(self,payload):
        (tipo_hardware, tipo_protocolo, tamanho_endereco_hardware,
        tamanho_endereco_protocolo, operacao, mac_sender, ip_sender,
        mac_dest, ip_dest) = struct.unpack("!HHBBH6s4s6s4s", payload)
        return tipo_hardware, tipo_protocolo, tamanho_endereco_hardware, + \
            tamanho_endereco_protocolo, operacao, mac_sender, ip_sender, mac_dest, ip_dest
    
    def byte_to_hex_mac(self, mac_em_bytes):
        endereco = binascii.hexlify(mac_em_bytes).decode("ascii")
        return ":".join([endereco[i:i+2] for i in range(0,12,2)])


    
    def desempacotamento_ethernet_frame(self,raw_dados):
        mac_destino, mac_fonte, tipo_ethernet = struct.unpack('! 6s 6s H', raw_dados[:14])
        return tipo_ethernet, raw_dados[14:]
                
    def send_broadcast(self, pacote_broadcast):
        self.sock = socket(self.socket_family, self.socket_type, self.protocolo)
        self.sock.settimeout(0.3)
        self.sock.bind(("< PLACA DE REDE >",htons(0x0806)))
        self.sock.send(pacote_broadcast)
        self.sock.close()
    
    def recv_unicast(self):        
        self.sock2 = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003))
        try:
            while True:
                raw_dados, addr = self.sock2.recvfrom(65536)
                if raw_dados:
                    tipo_ethernet, payload = self.desempacotamento_ethernet_frame(raw_dados)
                    if tipo_ethernet == 2054:
                        (tipo_hardware, tipo_protocolo, tamanho_endereco_hardware,tamanho_endereco_protocolo,
                        operacao, mac_sender, ip_sender, mac_dest, ip_dest) = self.desempacotamento_header_arp(payload[:28])
                        if operacao == 2:
                            print("Host: {} MAC: {} Hostname: {}".format(inet_ntoa(ip_sender),
                            self.byte_to_hex_mac(mac_sender),getfqdn(inet_ntoa(ip_sender))))
                            
        except:
            pass

# Gera pacotes com um ip diferente a cada iteração enviando esse pacote
def for_loop_gera_pacote():
    for ip_em_lista in GestorPacotes.lista_ip:
        pacote = GestorPacotes.Empacotamento_Frame_ArpHeader(ip = ip_em_lista)
        GestorPacotes.send_broadcast(pacote)
        

def InterrompePrograma_CTRL_C(signal, frame):
    print("Programa interrompido pelo utilizador")
    
    sys.exit(0)
           
if __name__ == '__main__':

    ##### AQUI SÂO INSERIDOS OS DADOS DO PACOTE ###############
    
    Arp = Pacote_Arp()
    Arp.tipo_hardware = 1
    Arp.tipo_protocolo = 0x0800
    Arp.len_hardware = 6
    Arp.len_protocolo = 4
    Arp.operacao = 1
    Arp.mac_src = ''
    Arp.src_ip = ''
    Arp.mac_dest = 'ff:ff:ff:ff:ff:ff'
    Arp.protocolo = 0x0806

    #####################################################

    GestorPacotes = Gestor_Pacotes(socket_family = PF_PACKET, socket_type = SOCK_RAW , protocolo = 0x0806, cabecalho_arp = Arp)
    # Termina programa quando CTRL + C
    signal.signal(signal.SIGINT, InterrompePrograma_CTRL_C)
    
    # Esta thread
    """
     Permite a execução do metodo recv_unicast  da class Gestor_Pacotes, em
     simultaneo com a função for_loop_gera_pacote, o que nos permite que esse método fique em escuta enquanto
     empacotamos e fazemos o envio do pacotes em broadcast, podendo assim receber as respostas
    """
    processo = threading.Thread( target = GestorPacotes.recv_unicast)
    # É necessário para que seja possivel sair do programa após a função for_loop_gera_pacote terminar
    processo.daemon = True
    processo.start()
    
    for_loop_gera_pacote()
    sys.exit(0)

    

 

Abraço.

Link para o comentário
Compartilhar em outros sites

  • 1 ano depois...

Boa Noite a todos,

 

Estava procurando um script em Python que, a partir do MAC Address ou IP (ou os 2) pudesse me dar o Switch e a porta desse Switch onde estaria conectado fisicamente um determinado dispositivo ou vários deles, para uma rede LAN bem maior, em VLAN específica e guardar esses resultados em um .csv ou .xlx/.xlsx.

 

Até tenho um script que me dá em planilha um teste de ping, dizendo qual IP respondeu Ok e qual apresenta erro.

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...