Jump to content

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


Guest gnoo

Recommended Posts

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.

Edited by gnoo
Link to post
Share on other sites
  • 1 year later...

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 to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...