Ir para conteúdo
  • Cadastre-se
Entre para seguir isso  
gnoo

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

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.

 

 

 

Editado por gnoo
  • l33t 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
Entre para seguir isso  

  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.

×