Esteganografia em pacotes IP

Uma vez, antes de defender outro trabalho de laboratório, eles me fizeram uma pergunta: quais campos de um pacote IP podem ser usados ​​para acolchoados? Eu não sabia e apenas dei de ombros. Mas logo ainda decidi estudar esse assunto.

Abaixo, você encontrará o estudo de cabeçalhos de pacotes IP, o próprio utilitário de ping do Python e várias maneiras de transferir dados sem chamar a atenção.

Conteúdo


  1. Estrutura de pacotes IP
  2. Configuração do ambiente
  3. Ping: opção fácil
  4. Ping: opção difícil
  5. Melhorias?

Estrutura de pacotes IPv4




Selecione os campos, cuja alteração não afetará muito o pacote:

O DIH pode variar de 5 a 15.
O campo ToS é usado para priorizar as notificações de tráfego e congestionamento sem descartar pacotes. Na maioria das vezes, esse campo é 0. Teoricamente, ele pode ser usado para transmitir um byte inteiro de informações.
O comprimento do pacote é um excelente campo para transmitir números de 20 a 65535.
TTL pode transmitir até 7 bits de informação. Você precisa saber o número de saltos para o host e levar isso em consideração.

Configuração do ambiente


Para repetir o experimento, você precisará de duas máquinas com Python e a estrutura scapy.

Você pode instalá-lo seguindo as instruções da documentação . No meu caso, foram duas gotículas no DO com a rede local ativada. Para testar a operacionalidade do estegano, foram escolhidas duas rotas: através da rede local para 1 salto e via Internet para 2 hop.

Ping: opção fácil


Primeiro, implementamos o sender.py, que enviará pacotes ICMP sem mensagens ocultas.

from scapy.all import * #    10.0.0.2  icmp-type 8 (echo-request) pkt = IP(src="10.0.0.1", dst="10.0.0.2") / ICMP(type = 8) #      sr1(pkt) 

O Scapy preencherá os campos restantes com os valores padrão antes do envio e calculará a soma de verificação.

No lado do recebimento, escreva listener.py, que escutará e exibirá todos os pacotes ICMP recebidos.

 from scapy.all import * #    # filter --  icmp # timeout --   10  # count --    100  # iface --    eth1 packets = sniff(filter = "icmp", timeout = 10, count = 100, iface = "eth1") #      for pkt in packets: #     echo-request if pkt[ICMP].type != 8: continue #    pkt.show() 

Saída do ouvinte
 ###[ Ethernet ]### dst = hh:hh:hh:hh:hh:hh src = gg:gg:gg:gg:gg:gg type = 0x800 ###[ IP ]### version = 4 ihl = 5 tos = 0x0 len = 28 id = 24923 flags = frag = 0 ttl = 64 proto = icmp chksum = 0x4364 src = 10.0.0.1 dst = 10.0.0.2 \options \ ###[ ICMP ]### type = echo-request code = 0 chksum = 0xf7ff id = 0x0 seq = 0x0 ###[ Padding ]### load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 


O cabeçalho do pacote IP possui um campo identificador. Preencha com os símbolos "A" e "B":

 payload = ord("A") * 0x100 + ord("B") pkt = IP(src="10.0.0.1", dst="10.0.0.2", id = payload) / ICMP(type = 8) 

Além disso, no cabeçalho do ICMP, existe exatamente o mesmo campo no qual dois bytes também podem ser carregados.

Mude o ouvinte para exibir os dados recebidos:

 from scapy.all import * import sys packets = sniff(filter="icmp", timeout = 10, count = 100, iface="eth0") for pkt in packets: if pkt[ICMP].type != 8: continue #    a, b = divmod(pkt[IP].id, 0x100) sys.stdout.write(chr(a)) sys.stdout.write(chr(b)) sys.stdout.flush() 

Na imagem e na semelhança, você pode preencher praticamente qualquer campo que foi anotado anteriormente como adequado para acolchoado.

Ping: opção difícil


A transferência de dados do parágrafo anterior não foi a mais óbvia, mas podemos torná-la ainda mais óbvia. Você pode ocultar os dados no campo da soma de verificação. De acordo com a RFC1071, uma soma de verificação é (de repente!) Uma inversão bit a bit de uma soma aritmética um pouco mais complexa.

Explicação com um exemplo
Suponha que tenhamos um cabeçalho para o qual queremos calcular a soma de verificação. No momento do cálculo, o campo de soma de verificação é redefinido.

 4500 003c 000a 0000 8001 [checksum] c0a8 000d c0a8 000d 

1. Adicione todas as palavras de 16 bits, lembrando a transferência da ordem superior:

 4500 + 003c + 000a + 0000 + 8001 + [checksum=0000] + c0a8 + 000d + c0a8 + 000e = = (2) 46b2 

2. Adicione o resultado com transferências:

 46b2 + 2 = 46b4 

3. Inverter:

 ~(46b4) = b94b 

b94b é a soma de verificação que estamos procurando . Para verificação, você pode substituir no cabeçalho e executar as etapas 1 e 2. Se você obtiver FFFF, a quantidade encontrada estará correta.

Verificação:

 1. 4500 + 003c + 000a + 0000 + 8001 + [checksum=b94b] + c0a8 + 000d + c0a8 + 000e = = (2) FFFD 2. FFFD + 2 = FFFF 


Sabemos que a soma de verificação de um pacote muda à medida que os nós passam pela rede, à medida que o TTL muda. Além disso, ao passar pelo NAT no pacote, o "endereço de origem" é substituído, o que também afeta a soma de verificação. E quanto TTL diminuirá ao alcançar nosso ouvinte ... A cereja no bolo é que a testemunha do "identificador" coincide com a testemunha da soma de verificação. Esse fato nos permite influenciar a soma de verificação e alterá-la para qualquer valor da área de definição. Como a soma de verificação (carga útil) será calculada apenas ao passar o último nó na rota, é importante levar em consideração tudo o que pode ser alterado no pacote durante a rota durante o cálculo.

O algoritmo para encontrar o "identificador", que nos dará a soma de verificação desejada:

  1. Configuramos o pacote como quando passamos pelo último nó (IP, TTL, etc)
  2. No "identificador", escreva a carga
  3. Nós calculamos a soma de verificação
  4. O resultado deve ser escrito no "identificador" do pacote enviado

Escreveremos uma função que formará um pacote pelo número de esperanças, IPs atrás do NAT e dois bytes de carga útil.

 # src -   # src_nat -    NAT # dst -   # dttl -       # a, b --      def send_stegano(src, src_nat, dst, dttl, a, b): #       payload = ord(a)*0x100 + ord(b) #         pkt = IP(dst=dst, src=src_nat, ttl=64-dttl, id = payload) / ICMP(type=8) #  Scapy  chksum pkt = IP(raw(pkt)) #     pkt[IP].src = src pkt[IP].ttl = 64 pkt[IP].id = pkt[IP].chksum #   chksum,  Scapy   del pkt[IP].chksum # Scapy      pkt = IP(raw(pkt)) #      sr1(pkt) 

Melhorias?


  • Os campos chksum, seq, id no cabeçalho do protocolo ICMP também podem ser usados ​​para transmitir dados
  • O ToS pode ser usado para identificar pacotes "próprios" e ignorar a solicitação de eco de outras pessoas.

Source: https://habr.com/ru/post/pt413851/


All Articles