用python3发送伪造arp探针应答包

来自三线的随记

部分网络环境下交换机无法对src address 为0.0.0.0 的arp 报文(即ARP探针)做出应答

在这种情况下于另一个节点上执行脚本发送伪造arp response即可曲线救国


运行环境需要 pip3 install scapy

version 1

需要指定目标节点ip 网卡 网关ip地址

#!/usr/bin/python
#-*- coding: UTF-8 -*-

import os
import sys
import signal
from scapy.all import (
    get_if_hwaddr,   # 获取本机网络接口的函数
    getmacbyip,      # 通过IP地址获取其Mac地址的函数
    ARP,             # 构造ARP数据包
    Ether,           # 构造以太网数据包
    sendp            # 在第二层发送数据包
)
 
from optparse import OptionParser     #格式化用户输入的参数
 
def main():
 
    #自定义程序使用方法,当中的 %prog,optparse会以当前程序名的字符串来替代
    usage = 'Usage: %prog [-i interface] [--gateway gateway_ip] target'
 
    #创建一个 OptionParser 对象
    parser = OptionParser(usage)
    #add_option 来定义命令行参数
    parser.add_option('-i', dest='interface', help='Specify the interface to use')
    parser.add_option('--gateway',dest="gatewayip",help="gateway ip address")
 
    #调用optionparser的解析函数
    (options, args) = parser.parse_args()
 
    if len(args) != 1 or options.interface is None or options.gatewayip is None:
        parser.print_help()
        print("debug args:",len(args))
        print("debug ",options.interface)
        print("debug ",options.gatewayip)
        sys.exit(1)

    # For dce
    # get gateway mac address
    gw_mac = getmacbyip(options.gatewayip)
    print("gateway ip address is:{}, mac address is: {}".format(options.gatewayip, gw_mac))
    target_mac = getmacbyip(args[0])
    print("arp probe response to {} {}".format("0.0.0.0", target_mac))
    if target_mac is None:
       print("[-] Error: Could not resolve targets MAC address")
       sys.exit(1)
 
    #响应包
    def build_rep():
        pkt = Ether(src=gw_mac, dst=target_mac) / ARP(hwsrc=gw_mac, psrc=options.gatewayip, hwdst=target_mac, pdst="0.0.0.0", op=2)
        return pkt
 
    pkt = build_rep()

    def quit(signum, frame):
        print('\nYou choose to stop me.')
        exit()
    signal.signal(signal.SIGINT, quit)

    while True:
        #在两次发送数据包之间有一定的时间间隔,使用inter选项,表示每隔2秒发送一个数据包
        sendp(pkt, inter=0.5, iface=options.interface)
        print("arp response sent to {} {}".format("0.0.0.0", target_mac))
 
if __name__ == '__main__':
    main()
 


version 2

自动获取arp probe报文的source mac address进行回包

新手面向搜索引擎写bug

慎用

#!/usr/bin/env python3
#-*- coding: UTF-8 -*-
#
# Author: sanXian
# capute the arp probe packets and response automatically


import os
import sys
import signal
from scapy.all import (
    get_if_hwaddr,   # 获取本机网络接口的函数
    getmacbyip,      # 通过IP地址获取其Mac地址的函数
    ARP,             # 构造ARP数据包
    Ether,           # 构造以太网数据包
    sendp,           # 在第二层发送数据包
    sniff            # capture network traffic
)

from optparse import OptionParser     #格式化用户输入的参数

# build arp response package
def build_rep(src_ipaddr, src_mac, dst_ipaddr, dst_mac ):
   pkt = Ether(src=src_mac, dst=dst_mac) / ARP(hwsrc=src_mac, psrc=src_ipaddr, hwdst=dst_mac, pdst=dst_ipaddr, op=2)
   return pkt

def sniff_callback(package):
    global gw_mac
    print("get a arp probe package from {}".format(package.src))
    pkt = build_rep(src_ipaddr=package.pdst, src_mac=gw_mac, dst_ipaddr=package.psrc, dst_mac=package.hwsrc)
    sendp(pkt, inter=0, iface=options.interface)

def quit(signum, frame):
        print('\nYou choose to stop me.')
        exit()

def main():
 
    #自定义程序使用方法,当中的 %prog,optparse会以当前程序名的字符串来替代
    usage = 'Usage: %prog [-i interface] [--gateway gateway_ip]'
 
    #创建一个 OptionParser 对象
    parser = OptionParser(usage)
    #add_option 来定义命令行参数
    parser.add_option('-i', dest='interface', default="dce-br", help='Specify the interface to use')
    parser.add_option('--gateway',dest="gatewayip",help="gateway ip address")
 
    global options
    (options, args) = parser.parse_args()

    signal.signal(signal.SIGINT, quit)
 
    if options.interface is None or options.gatewayip is None:
        parser.print_help()
        print("[debug]interface value is  ",options.interface)
        print("[debug]gateway ip value is ",options.gatewayip)
        sys.exit(1)

    # get gateway mac address
    global gw_mac
    gw_mac = getmacbyip(options.gatewayip)
    print("gateway ip address is:{}, mac address is: {}".format(options.gatewayip, gw_mac)) 
    if gw_mac is None:
        print("[x]Fatal Error: can not get the mac address")
        sys.exit(1)

    sniff_filter = "arp and src 0.0.0.0 and dst " + options.gatewayip
    sniff(iface=options.interface, filter=sniff_filter, prn=sniff_callback)
     
if __name__ == '__main__':
    main()  
 


version3

适配vlan(trunk网卡)

#!/usr/bin/env python3
#-*- coding: UTF-8 -*-
#
# 自动获取arp probe报文的source mac address进行回包
# capute the arp probe packets and response automatically
# Options: -i 指定监听网卡  --gateway 指定监听的探针报文target地址(arp probe target ip)  --vlan 指定vlan id[可选]
# Author: sanXian
#
# Attention: 在跨vlan的情况下,代码层面不能简易获取得到跨vlan的网关mac地址
#            遂会使用一个假的mac地址构造arp probe response
### 新手面向搜索引擎写bug
### 慎用
 
import os
import sys
import signal
from scapy.all import (
    get_if_hwaddr,   # 获取本机网络接口的函数
    getmacbyip,      # 通过IP地址获取其Mac地址的函数
    ARP,             # 构造ARP数据包
    Ether,           # 构造以太网数据包
    Dot1Q,           # vlan
    sendp,           # 在第二层发送数据包
    sniff            # capture network traffic
)
 
from optparse import OptionParser     #格式化用户输入的参数
 
# build arp response package
def build_rep(src_ipaddr, src_mac, dst_ipaddr, dst_mac , vlan_id):
    if vlan_id >= 0 and vlan_id <= 4095:
        pkt = Ether(src=src_mac, dst=dst_mac) / Dot1Q(vlan=vlan_id) / ARP(hwsrc=src_mac, psrc=src_ipaddr, hwdst=dst_mac, pdst=dst_ipaddr, op=2)
    else:
        pkt = Ether(src=src_mac, dst=dst_mac) / ARP(hwsrc=src_mac, psrc=src_ipaddr, hwdst=dst_mac, pdst=dst_ipaddr, op=2)
    return pkt
   
def sniff_callback(package):
    global gw_mac
    print("get a arp probe package from {}".format(package.src))
    pkt = build_rep(src_ipaddr=package.pdst, src_mac=gw_mac, dst_ipaddr=package.psrc, dst_mac=package.hwsrc, vlan_id=options.vlan)
    sendp(pkt, inter=0, iface=options.interface)
 
def quit(signum, frame):
        print('\nYou choose to stop me.')
        exit()
 
def main():
  
    #自定义程序使用方法,当中的 %prog,optparse会以当前程序名的字符串来替代
    usage = 'Usage: %prog [-i interface] [--gateway gateway_ip]'
  
    #创建一个 OptionParser 对象
    parser = OptionParser(usage)
    #add_option 来定义命令行参数
    parser.add_option('-i', dest='interface', default="dce-br", help='Specify the interface to use')
    parser.add_option('--gateway',dest="gatewayip",help="gateway ip address")
    parser.add_option('--vlan',default="-1", dest="vlan",help="vlan id")
  
    global options
    (options, args) = parser.parse_args()
    options.vlan = int(options.vlan)
 
    signal.signal(signal.SIGINT, quit)
  
    if options.interface is None or options.gatewayip is None:
        parser.print_help()
        print("[debug]interface value is  ",options.interface)
        print("[debug]gateway ip value is ",options.gatewayip)
        print("[debug]vlan id value is ",options.vlan)
        sys.exit(1)
 
    # get gateway mac address
    global gw_mac
    gw_mac = getmacbyip(options.gatewayip)
    if gw_mac is None:
        print("[x]Fatal Error: can not get the mac address")
        sys.exit(1)
    print("gateway ip address is:{}, mac address is: {}".format(options.gatewayip, gw_mac))
    sniff_filter = "arp and src 0.0.0.0 and dst " + options.gatewayip
 
    if options.vlan >= 0 and options.vlan <= 4095:
        print("vlan mode: {}".format(options.vlan))
        sniff_filter = "vlan " + str(options.vlan) + " and " +  sniff_filter
 
    sniff(iface=options.interface, filter=sniff_filter, prn=sniff_callback)
      
if __name__ == '__main__':
    main()