Ir para conteúdo

Análise de pacotes na prática [ Parte 4 ] - "UDP" - Raw Sockets python


Visitante gnoo

Posts Recomendados

Saudações,

Este conteúdo está sujeito a erros de interpretação por parte da minha pessoa, se vires algum erro ou achas que tens algo a acrescentar deixa nos comentários para ser corrigido/adicionado...

Os conceitos de redes aqui apresentados foram retirados do livro:

  

 

neste tópico vamos ver como analisar um pacote UDP, este pacote vem encapsulado no payload / carga, do pacote IP e circula no protocolo 17.

468027269_encapudp.png.6dd64e659b4633cf0897f539918b0ac9.png

 

A análise do pacote UDP pode ser importante por várias razões, uma delas é porque é através dele que nós chegamos ao DNS que pode ser encontrado na carga deste pacote, e isso para os “hackers”  de serviço pode ser importante.

Antes de fazer a função para tratar os dados que vêm neste pacote vamos ver algumas características do ser formato.

ATENÇÃO: O conteúdo que segue deve ser complementado com a leitura de um livro da especialidade

Formato de mensagem UDP

Citar

Usamos o termo user datagram para descrever uma mensagem UDP; a ênfase no user destina-se a distinguir datagramas UDP de datagramas IP. Conceitualmente, um user datagram consiste de duas partes: um cabeçalho que contém metainformação, tal como números de porta de protocolo de origem e de destino, e uma área de carga que contém os dados que estão sendo enviados. A Figura 10.1 ilustra a organização.

848911624_Capturadeecr_2018-12-31_22-24-41.png.e4786f092d0275df16da84cbf25c3feb.png

Citar

O cabeçalho de um user datagram é extremamente pequeno: é composto por quatro campos que especificam a porta de protocolo a partir da qual a mensagem foi enviada, a porta de protocolo para a qual a mensagem é destinada, o tamanho da mensagem e um checksum UDP. Cada campo tem um tamanho de 16 bits, o que significa que todo o cabeçalho ocupa apenas um total de 8 octetos. A Figura 10.2 ilustra o formato do cabeçalho.

976802908_Capturadeecr_2018-12-31_22-25-54.png.4c4094a4fa68857a494f255a891b503c.png

 

Citar

O campo PORTA DE ORIGEM UDP contém um número de porta de protocolo de 16 bits usado pelo aplicativo de envio, e o campo de PORTA DE DESTINO UDP contém o número da porta UDP de 16 bits do aplicativo de recebimento. Em essência, o software de protocolo usa os números de porta para demultiplexar datagramas entre os aplicativos que estãoesperando para recebê-los. Curiosamente, a PORTA DE ORIGEM UDP é opcional. Nós pensamos nela como identificadora da porta à qual a resposta deve ser enviada. Em uma transferência de mão única, onde o receptor não envia uma resposta, a porta de origem não é necessária e pode ser ajustada para zero. O campo TAMANHO DA MENSAGEM UDP (UDP MESSAGE LENGTH) contém uma contagem de octetos no datagrama UDP, incluindo o cabeçalho UDP e os dados do usuário. Assim, o valor mínimo é oito, tamanho só do cabeçalho. O campo TAMANHO DA MENSAGEM UDP é composto por 16 bits, o que significa que o valor máximo que pode ser representado é 65.535. Como uma questão prática, no entanto, veremos que

uma mensagem UDP deve caber na área de carga útil de um datagrama IP. Por conseguinte, o tamanho máximo permitido depende do tamanho do(s) cabeçalho(s) de IP, o qual é consideravelmente maior em um datagrama IPv6 do que num datagrama Ipv4.

 

Função para tratar os dados do cabeçalho UDP

 

def dados_pacote_udp(carga):
    tupla_dados_udp = struct.unpack('! H H H H', carga[:8])
    porta_fonte = tupla_dados_udp[0]
    porta_destino = tupla_dados_udp[1]
    udp_len = tupla_dados_udp[2]
    udp_checksum = tupla_dados_udp[3]
    
    return porta_fonte, porta_destino, udp_len, udp_checksum, carga[8:]

 

Script completo…

#coding:utf-8  
#!/usr/bin/env python3

from socket import *
import struct
import binascii

# AF_PACKET Familia protocolo (everywere)
# ntohs(3) - Permite capturar todos os pacotes com frames ethernet

def ethernet_frame(raw_dados):
    
    mac_destino, mac_fonte, protocolo = struct.unpack('! 6s 6s H', raw_dados[:14])

    return byte_to_hex_mac(mac_destino), byte_to_hex_mac(mac_fonte), htons(protocolo), raw_dados[14:]




def byte_to_hex_mac(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 dados_pacote_ipv4(carga):
    tupla_dados_ipv4 = struct.unpack("!BBHHHBBH4s4s", carga[:20])
    versao = tupla_dados_ipv4[0]
    header_len = versao >> 4
    tipo_servico = tupla_dados_ipv4[1]
    tamanho_total = tupla_dados_ipv4[2]
    identificacao = tupla_dados_ipv4[3]
    offset_fragmento = tupla_dados_ipv4[4]
    tempo_vida_ttl = tupla_dados_ipv4[5]
    protocolos = tupla_dados_ipv4[6]
    checksum_cabecalho = tupla_dados_ipv4[7]
    ip_origem = inet_ntoa(tupla_dados_ipv4[8])
    ip_destino = inet_ntoa(tupla_dados_ipv4[9])

    # correção  
    tamanho_header_bytes = (versao & 15) * 4 # Cálculo Tamanho do header em bytes

    return versao, header_len, tipo_servico, + \
           tamanho_total, identificacao, offset_fragmento, + \
           tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga[tamanho_header_bytes:] 
    

def dados_pacote_tcp(carga):
    tupla_dados_tcp = struct.unpack('! HHLLBBHHH', carga[:20])
    porta_fonte = tupla_dados_tcp[0]
    porta_destino = tupla_dados_tcp[1]
    numero_sequencia = tupla_dados_tcp[2]
    numero_confirmacao = tupla_dados_tcp[3]

    # Esta linha recebe dois campos do cabeçalho ( HLEN & RESERVADO )
    hlen_e_reservado = tupla_dados_tcp[4]

    # Esta linha recebe campo HLEN 
    header_len = (hlen_e_reservado >> 4) * 4
    
    flags = tupla_dados_tcp[5]
    flag_FIN = flags & 1
    flag_SYN = (flags >> 1) & 1
    flag_RST = (flags >> 2) & 1
    flag_PSH = (flags >> 3) & 1
    flag_ACK = (flags >> 4) & 1
    flag_URG = (flags >> 5) & 1
    flag_ECE = (flags >> 6) & 1
    flag_CWR = (flags >> 7) & 1

    window_size = tupla_dados_tcp[6]
    checksum = tupla_dados_tcp[7]
    urgent_pointer = tupla_dados_tcp[8]
    
    return porta_fonte, porta_destino ,numero_sequencia, + \
           numero_confirmacao, hlen_e_reservado, header_len, + \
           flags, flag_FIN, flag_SYN, flag_RST, flag_PSH, flag_ACK, + \
           flag_URG, flag_ECE, flag_CWR, window_size, checksum, urgent_pointer , carga[:20]

def dados_pacote_udp(carga):
    tupla_dados_udp = struct.unpack('! H H H H', carga[:8])
    porta_fonte = tupla_dados_udp[0]
    porta_destino = tupla_dados_udp[1]
    udp_len = tupla_dados_udp[2]
    udp_checksum = tupla_dados_udp[3]
    
    return porta_fonte, porta_destino, udp_len, udp_checksum, carga[8:]

sock = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003))    

while True:
    raw_dados, addr = sock.recvfrom(65536)
    mac_destino, mac_fonte, protocolo, carga_util = ethernet_frame(raw_dados)

    print("------- FRAME ETHERNET-----------")
    print("Endereço MAC destino : {}".format(mac_destino))
    print("Endereço MAC fonte : {}".format(mac_fonte))
    print("Protocolo : {}\n".format(protocolo))
    print("Carga útil -> {}\n".format(carga_util))

    # A variavel (protocolo) está a referir-se ao
    # tipo de frame
    # Se for 8 vem ai pacote IP \0/
    if protocolo == 8:
        """
        Para desempacotar os valores da função dados_pacote_ipv4
        com as variáveis aninhadas, necessáriamente elas têm que estar entre
        parênteses...
        """
        ( versao, header_len, tipo_servico,
        tamanho_total, identificacao, offset_fragmento,
        tempo_vida_ttl, protocolos, checksum_cabecalho,
         ip_origem, ip_destino, carga ) = dados_pacote_ipv4(carga_util)
        
        
        print("--------- HEADER IPV4----------")  
        print("Versão : {}".format(versao))
        print("Header_len : {}".format(header_len))
        print("Tipo Serviço : {}".format(tipo_servico))
        print("Tamanho Total : {}".format(tamanho_total))
        print("Identificação : {}".format(identificacao))
        print("Offset Fragmento : {}".format(offset_fragmento))
        print("Tempo vida TTL : {}".format(tempo_vida_ttl))
        print("Protocolo : {}".format(protocolos))
        print("checksum Cabeçalho : {}".format(checksum_cabecalho))
        print("IP Origem: {}".format(ip_origem))
        print("IP Destino: {}\n".format(ip_destino))
        print("Carga : {}\n".format(carga))

        if protocolos == 6:
            
            (porta_fonte, porta_destino, numero_sequencia, numero_confirmacao,
             hlen_e_reservado, header_len, flags, flag_FIN, flag_SYN, flag_RST,
             flag_PSH, flag_ACK, flag_URG, flag_ECE, flag_CWR, window_size, checksum,
             urgent_pointer, carga ) = dados_pacote_tcp(carga)

            print("--------- HEADER TCP----------")
            print("Porta Fonte : {}".format(porta_fonte))
            print("Porta Destino : {}".format(porta_destino))
            print("Número Sequência : {}".format(numero_sequencia))
            print("Número Confirmação : {}".format(numero_confirmacao))
            print("Hlen_e_reservado : {}".format(hlen_e_reservado))
            print("Header_len : {}".format(header_len))
            print("Flags : {}".format(flags))
            print("Flag FIN : {}".format(flag_FIN))
            print("Flag SYN : {}".format(flag_SYN))
            print("Flag RST : {}".format(flag_RST))
            print("Flag PSH : {}".format(flag_PSH))
            print("Flag ACK : {}".format(flag_ACK))
            print("Flag URG : {}".format(flag_URG))
            print("Flag ECE : {}".format(flag_ECE))
            print("Flag CWR : {}".format(flag_CWR))
            print("window size : {}".format(window_size))
            print("checksum : {}".format(checksum))
            print("urgent pointer : {}\n".format(urgent_pointer))
            print("carga : {}\n".format(carga))
        
        elif protocolos == 17:
            
            porta_fonte, porta_destino, udp_len, udp_checksum, carga = dados_pacote_udp(carga)
            
            print("--------- HEADER UDP ----------")
            print("Porta Fonte : {}".format(porta_fonte))
            print("Porta Destino : {}".format(porta_destino))
            print("UDP Length : {}".format(udp_len))
            print("UDP Checksum : {}\n".format(udp_checksum))
            print("Carga : {}\n".format(carga))
"""
socket.ntohs( x ) 
Converte inteiros positivos de 16 bits da rede para a ordem de bytes do host.
Nas máquinas em que a ordem de bytes do host é igual à ordem de bytes da rede,
isso é um não operacional; caso contrário, ele executará uma operação de troca
de 2 bytes.
------------------------------------------------------------------------------

socket.htons( x ) 
Converte inteiros positivos de 16 bits da ordem de bytes do host para a rede.
Nas máquinas em que a ordem de bytes do host é igual à ordem de bytes da rede,
isso é um não operacional; caso contrário, ele executará uma operação de troca
de 2 bytes.
"""

 

na função que fizemos para tratar os dados do pacote UDP, estamos a retornar o valor carga[8:], é nesses dados que vamos buscar o DNS, ao aceder ao site: 

https://www.mentebinaria.com.br

podemos ver o dominio e o DNS...

dns.thumb.png.6a8f2776e1c8abe78a886fde5c6c000d.png

 

são esses dados que temos que tratar...  

abraços.

 

 

 

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...