Bootstrap

python实现——ARP攻击检测

介绍

最近发现火绒的IP黑名单功能不能防护ARP攻击,只能防护二层以上的网络行为,就花了点时间开发了一个自动化工具,用来检测自己是否遭受了ARP欺骗攻击。我这里开发了2个脚本,普通版和plus版,两者的区别就是后者支持了取证功能。

普通版

代码

脚本介绍:

  1. 脚本每隔一段时间(这里我是设置为30分钟)检查一次ARP表,如果发现存在相同的MAC地址,就意味着极有可能发生了ARP欺骗攻击
  2. 一旦检测到MAC地址重复,脚本就会查询重复mac地址对应的网卡名称,以弹窗的方式展示给用户
  3. 当用户关闭了弹窗,脚本会把可疑的ARP记录和所有的ARP记录,都存放到用户的桌面上,方便用户查看
  4. 我是使用了pyinstaller打包脚本来演示的:pyinstaller.exe -F .\反ARP欺骗.py -w
import re
from subprocess import PIPE, Popen
import tkinter.messagebox
import os
import time


# 根据获取到的IP网卡地址,获知对应的网卡名字
def ip_to_name(ip):
    command1 = r'ipconfig'
    interface_name1 = ''  # 获取到网卡的名字
    p = Popen(command1, stdout=PIPE, stderr=PIPE)
    stdout1, stderr1 = p.communicate()

    ipconfig = stdout1.decode('gbk')
    a = ipconfig.split('网适配器')
    del (a[0])
    for i1 in a:
        if '断开连接' not in i1 and ip in i1:
            interface_msg = i1.split('子网掩码')[0]  # 获取每个网卡的网络信息,断开连接的网卡不予展示
            interface_name1 = interface_msg.split('连接特定的 DNS 后缀')[0].replace(':', '').strip()  # 获取到网卡的名字
    return interface_name1


def work():
    desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')  # 获取桌面路径
    path_result = os.path.join(desktop_path, 'evil_arp.txt')  # 用来存储终端中输出的内容到一个文本中,方便用户使用数据,此为文本路径
    path_result1 = os.path.join(desktop_path, 'arp.txt')  # 用来存储终端中输出的内容到一个文本中,方便用户使用数据,此为文本路径
    evil_arp = ''  # 用来记录所有的可疑arp表项
    flag = 0
    command = r'arp -a'
    p = Popen(command, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()

    arp_form = stdout.decode('gbk')

    arp_list = arp_form.split('接口:')
    for i2 in arp_list:  # 把ARP表按照网卡进行切分
        ip_and_mac = {}  # 用来存放arp小表中的IP和MAC地址
        mac_list = []  # 用来存储一个ARP小表中的所有mac,借此检查有无重复的mac,进而得知ARP攻击

        interface_arp = i2.split('---')[0].strip()  # 获取每个ARP小表的网卡地址

        # 从每一个ARP小表中提取出IP地址和MAC地址,将其绑定为字典
        obj = re.compile(r"(?P<ip>(\d+\x2e){3}\d+)\s+(?P<mac>(\w\w-){5}\w\w)", re.S)
        result = obj.finditer(i2)
        for i3 in result:
            # print(i3.group('ip'), i3.group('mac'), sep=' ')
            ip_and_mac[i3.group('ip')] = i3.group('mac')

        # 判断有误重复的MAC地址
        for i4 in ip_and_mac.values():
            if i4 == 'ff-ff-ff-ff-ff-ff':
                pass
            else:
                if i4 not in mac_list:
                    mac_list.append(i4)
                else:
                    flag = 1
                    interface_name = ip_to_name(interface_arp)  # 获知网卡名字
                    repeat_ip = [k for k, v in ip_and_mac.items() if v == i4]  # 获知重复MAC对应的IP
                    msg = ''
                    for ri in repeat_ip:
                        msg += ri + '\t' + i4 + '\n'

                    alert_msg = interface_name + '网卡 发现ARP攻击!\n' + msg
                    evil_arp += alert_msg + '\n'
                    tkinter.messagebox.showerror('ARP攻击!ARP记录已存放至桌面!', alert_msg)

    # 导出结果
    if flag:
        # 导出所有的ARP记录
        a = open(path_result, 'w', encoding='utf8')
        for r in evil_arp:
            a.write(r)
        a.close()

        # 导出可疑的ARP记录
        arp_record = r'arp -a > ' + path_result1
        os.system(arp_record)


if __name__ == '__main__':
    '''
    脚本功能:每隔一段时间查询一次ARP表,一旦发现ARP异常,就会导出结果到桌面
    '''
    while True:
        work()
        time.sleep(30*60)  # 每隔30分钟运行一次

GIF演示

在这里插入图片描述

plus版

代码

脚本介绍:这个脚本相较于普通版,就是添加了一个取证的功能,一旦检测到ARP欺骗,脚本会调用tshark开启抓包20秒,数据包的名字就是出现ARP欺骗的网卡的名字。(tshark在我之前的文章中已经多次运用介绍过,这里不再赘述)
打包成exe的命令:pyinstaller.exe -F .\反ARP欺骗PLUS版.py -w

import re
from subprocess import PIPE, Popen
import tkinter.messagebox
import os
import time


# 根据获取到的IP网卡地址,获知对应的网卡名字
def ip_to_name(ip):
    command1 = r'ipconfig'
    interface_name1 = ''  # 获取到网卡的名字
    p = Popen(command1, stdout=PIPE, stderr=PIPE)
    stdout1, stderr1 = p.communicate()

    ipconfig = stdout1.decode('gbk')
    a = ipconfig.split('网适配器')
    del (a[0])
    for i1 in a:
        if '断开连接' not in i1 and ip in i1:
            interface_msg = i1.split('子网掩码')[0]  # 获取每个网卡的网络信息,断开连接的网卡不予展示
            interface_name1 = interface_msg.split('连接特定的 DNS 后缀')[0].replace(':', '').strip()  # 获取到网卡的名字
    return interface_name1


# 开启取证抓包
def qu_zheng(interface_name):
    desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')  # 获取桌面路径
    pcap_name = interface_name + '.pcap'
    path_pcap = os.path.join(desktop_path, pcap_name)
    path_pcap = '"' + path_pcap + '"'

    command = r'tshark.exe -D'
    p = Popen(command, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()
    tshark = stdout.decode('utf-8')
    tshark = tshark.split(')')
    for i in tshark:
        if interface_name in i:
            interface_code = i.split('(')[0].split('.')[-1].strip()  # 网卡对应的接口名称
            os.system('tshark.exe -i ' + interface_code + ' -f arp -a duration:20 -w ' + path_pcap)  # 抓包20秒


# 检测ARP攻击
def work():
    desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')  # 获取桌面路径
    path_result = os.path.join(desktop_path, 'evil_arp.txt')  # 用来存储终端中输出的内容到一个文本中,方便用户使用数据,此为文本路径
    path_result1 = os.path.join(desktop_path, 'arp.txt')  # 用来存储终端中输出的内容到一个文本中,方便用户使用数据,此为文本路径
    evil_arp = ''  # 用来记录所有的可疑arp表项
    flag = 0
    interface_name = ''  # 网卡的名字
    evil_interface_name = []  # 用来存储所有出现ARP欺骗问题的网卡
    command = r'arp -a'
    p = Popen(command, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()

    arp_form = stdout.decode('gbk')

    arp_list = arp_form.split('接口:')
    for i2 in arp_list:  # 把ARP表按照网卡进行切分
        ip_and_mac = {}  # 用来存放arp小表中的IP和MAC地址
        mac_list = []  # 用来存储一个ARP小表中的所有mac,借此检查有无重复的mac,进而得知ARP攻击

        interface_arp = i2.split('---')[0].strip()  # 获取每个ARP小表的网卡地址

        # 从每一个ARP小表中提取出IP地址和MAC地址,将其绑定为字典
        obj = re.compile(r"(?P<ip>(\d+\x2e){3}\d+)\s+(?P<mac>(\w\w-){5}\w\w)", re.S)
        result = obj.finditer(i2)
        for i3 in result:
            # print(i3.group('ip'), i3.group('mac'), sep=' ')
            ip_and_mac[i3.group('ip')] = i3.group('mac')

        # 判断有误重复的MAC地址
        for i4 in ip_and_mac.values():
            if i4 == 'ff-ff-ff-ff-ff-ff':
                pass
            else:
                if i4 not in mac_list:
                    mac_list.append(i4)
                else:
                    flag = 1
                    interface_name = ip_to_name(interface_arp)  # 获知网卡名字
                    evil_interface_name.append(interface_name)
                    repeat_ip = [k for k, v in ip_and_mac.items() if v == i4]  # 获知重复MAC对应的IP
                    msg = ''
                    for ri in repeat_ip:
                        msg += ri + '\t' + i4 + '\n'

                    alert_msg = interface_name + '网卡 发现ARP攻击!\n' + msg
                    evil_arp += alert_msg + '\n'
                    tkinter.messagebox.showerror('ARP攻击!ARP记录已存放至桌面!', alert_msg)

    # 导出结果
    if flag:
        # 导出所有的ARP记录
        a = open(path_result, 'w', encoding='utf8')
        for r in evil_arp:
            a.write(r)
        a.close()

        # 导出可疑的ARP记录
        arp_record = r'arp -a > ' + path_result1
        os.system(arp_record)

        # 这里走个形式,网络上一般就一个网卡会存在ARP欺骗,为了满足实验的需求,这里检查所有的网卡
        for i in evil_interface_name:
            qu_zheng(i)


if __name__ == '__main__':
    '''
    脚本功能:每隔一段时间查询一次ARP表,一旦发现ARP异常,就会导出结果到桌面,并且抓包取证
    '''
    while True:
        work()
        time.sleep(30*60)

GIF演示

在这里插入图片描述

更多

由于打包成exe的时候,使用了-w,因此工具会一直运行在后台,打开任务管理器才能关闭工具。
在这里插入图片描述

防御

在开发脚本的时候我想了很多防御的姿势,但最终结果证明,个人电脑最好的防御措施,要么是采用"ARP双向绑定"的方法,要么是使用电脑管家(自带ARP防火墙的那种),别的找到什么好的办法。如果读者有好的思路的话,欢迎交流分享。

电脑管家

测试发现电脑管家的能力也各不相同,如下图
腾讯电脑管家
在这里插入图片描述
360
在这里插入图片描述

;