DHCP 枯竭攻击

1. DHCP概念

DHCP 全称为动态主机配置协议(Dynamic Host Configuration Protocol), 这个协议定义了一个服务器/客户端模型, 让 DHCP 客户端(如 PC 等终端设备)可以从网络中的 DHCP 服务器那里获得能够自行完成配置的信息, 包括 IP 地址、默认网关地址、域名服务器地址和一些特定平台的信息。
DHCP 服务器使用的 UDP 端口是67, DHCP 客户使用的 UDP 端口是 68。

2. DHCP工作过程

1

为了简单而有效地描述 DHCP 的工作过程, 假设网络中有2台 DHCP 服务器和1台用户主机。

当启用主机的 DHCP 后, DHCP 客户将广播发送 DHCP 发现报文, 封装该报文的IP 数据报的 源IP 地址为0.0.0.0, 这是因为主机目前还未分配到 IP 地址, 因此使用该地址来代替。目的 IP 地址为广播地址255.255.255.255, 之所以进行广播发送, 是因为主机现在并不知道网络中有哪几个 DHCP 服务器, 它们的 IP 地址各是什么。

由于是广播的 IP 数据报, 因此网络中的所有设备都会收到该 IP 数据报, 并对其层层解封, 解封出封装有 DHCP 发现报文的 UDP 用户数据报。
对于 DHCP 客户, 其应用层没有监听该 UDP 用户数据报目的端口67 的进程也就是 DHCP 服务器进程, 因此无法交付 DHCP 发现报文, 只能丢弃;
而对于 DHCP 服务器, 其应用层始终运行着 DHCP 服务器进程, 因此会接受该 DHCP 发现报文并作出响应
DHCP 报文的格式比较复杂, 对于 DHCP 发现报文, 我们只需知道其内部封装有事物 ID 和 DHCP 客户端硬件地址(该地址属于网络层的范畴)即可。
DHCP 服务器收到 DHCP 发现报文后, 根据其中封装的 DHCP 客户端的 MAC 地址来查找自己的数据库, 看是否有针对该 MAC 地址的配置信息。如果有, 则使用这些配置信息来构建并发送 DHCP 提供文;如果没有, 则采用默认配置信息来构建并发送DHCP 提供报文, 封装该报文的 IP 数据报的源 IP 地址为 DHCP 服务器的 IP 地址, 目的 IP 地址仍为广播地址。仍然使用广播地址的原因是, 主机目前还没有配置 IP 地址, 为了使主机可以收到, 只能发送广播。

这样一来, 网络中的所有设备都会收到该 IP 数据报, 并对其层层解封, 解封出封装有DHCP 提供报文的 UDP 用户数据报。对于 DHCP 服务器, 其应用层没有监听该 UDP 用户数据报目的端口 68 的进程, 也就是 DHCP 客户进程, 因此无法交付 DHCP 提供报文, 只能丢弃。
对于 DHCP 客户, 其应用层运行着 DHCP 客户进程, 因此会接受该 DHCP 提供报文并作出相应处理。DHCP 客户会根据DHCP 提供报文中的事物ID 来判断该报文是否使自己所请求的报文。换句话说, 如果该事物 ID 与自己之前发送的 DHCP 发现报文中封装的事物 ID 相等, 就表明这是自己所请求的报文, 就可以接受该报文;否则就丢弃该报文。
DHCP 提供报文中还封装有配置信息, 例如 IP 地址、子网掩码、地址租期、默认网关、DNS 服务器等。
需要注意的是, DHCP 服务器从自己的 IP 地址池中挑选待租用给主机的 IP 地址时, 会使用免费 ARP 来确保所选 IP 地址未被网络中其他主机占用

在本例中, DHCP 客户会收到两个 DHCP 服务器发来的 DHCP 提供报文, DHCP 客户从中选择一个, 一般来说, 选择先到的那个。并向所选择的DHCP 服务器发送 DHCP 请求报文。 封装该报文的 IP 数据报的源 IP 地址仍为0.0.0.0, 因为此时 DHCP 客户才从多个DHCP 服务器中挑选一个作为自己的DHCP 服务器, 它首先需要征得该服务器的同意, 之后才能正式使用向该 DHCP 服务器租用的IP 地址;目的IP 地址仍为广播地址, 这样做的目的是, 不用向网络中的每一个 DHCP 服务器单播发送 DHCP 请求报文, 来告知它们是否请求它们作为自己的 DHCP 服务器。DHCP 请求报文中封装有事物 ID, DHCP 客户端的 MAC 地址, 接受的租约中的 IP 地址, 提供此租约的 DHCP 服务器段的 IP 地址等信息。在本例中, 假设 DHCP 客户选择 DHCP 服务器1 作为自己的 DHCP 服务器, 并且 DHCP 服务器1 接受该请求。

于是DHCP 服务器1 给DHCP 客户发送DHCP 确认报文, 封装该报文的IP 数据报的源IP 地址为 DHCP 服务器1的 IP 地址目的 IP 地址仍为广播地址。DHCP 客户收到该确认报文后, 就可以使用所租用到的IP 地址了。需要注意的是, 在使用租用到的IP 地址之前, 主机还会使用ARP 检测该IP 地址是否已被网络中的其他主机占用。若被占用, DHCP 客户会给 DHCP 服务器发送 DHCP 谢绝报文, 来谢绝IP 地址租约, 并重新发送 DHCP 发现报文;若未被占用, 则可以使用租约中的 IP 地址与网络中的其他主机通信了。

租用期过了一半时, DHCP 客户会向 DHCP 服务器发送 DHCP 请求报文来请求更新租用期。封装该报文的IP 数据报的源IP 地址为 DHCP 客户之前租用到的 IP 地址, 目的IP 地址为 DHCP 服务器1的地址。DHCP 服务器若同意, 则发回 DHCP 确认报文。这样, DHCP 客户就得到了新的租用期。DHCP 服务器若不同意, 则发回 DHCP 否认报文。这时, DHCP 客户必须立即停止使用之前租用的 IP 地址, 并重新发送 DHCP 发现报文来重新申请 IP 地址。

DHCP 服务器若未做出响应, 则在租用期过了87.5%时, DHCP 客户必须重新发送 DHCP 请求报文, 然后继续等待 DHCP 服务器可能作出的反应。若 DHCP 服务器未做出响应, 则当租用期到期后, DHCP 客户必须立即停用之前租用的 IP 地址, 并重新发送 DHCP 发现报文来重新申请 IP 地址。DHCP 客户可以随时提前终止 DHCP 服务器所提供的租用期, 这时只
需向DHCP 服务器发送DHCP 释放报文段即可。

DHCP中继

网络中的主机广播发送 DHCP 发现报文, 但该广播报文不会被路由器转发, 而是丢弃!
解决方式就是给该路由器配置 DHCP 服务器的IP 地址并使之成为 DHCP 中继代理。这样, 该网络中的各主机就可以通过 DHCP 来自动获取到网络配置信息了。
当该路由器收到广播的 DHCP 发现报文后, 会将其单播转发给 DHCP 服务器
DHCP 服务器会检查路由器的 IP 地址是否在 DHCP 中继服务器列表中, 是的话就会响应, 否则丢弃。
使用 DHCP 中继代理的主要原因是我们并不愿意在每一个网络上都设置一个 DHCP 服务器, 因为这样会使 DHCP 服务器的数量太多, 不便于管理。

3. DHCP枯竭攻击

3.1 DHCP DoS

攻击者通过发送大量源自不同 MAC 地址的 DHCP DISCOVER 报文向 DHCP 服务器请求 IP 地址, 造成 DHCP 服务器中的地址池迅速枯竭, 使得其无法为客户端分配IP地址, 这种行为称为 DHCP DoS 攻击DHCP 枯竭攻击

3.1.1 扫描DHCP服务器地址

使用下面的程序创建并发送一个DHCP发现(Discover)报文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 导入所需的模块
from scapy.all import *
import binascii
import random

# 生成一个随机的事务ID
xid_random = random.randint(1, 900000000)

# 生成一个随机的MAC地址, 并将其保存为字符串
mac_random = str(RandMAC())

# 将MAC地址从16进制字符串格式转换为二进制格式
client_mac_id = binascii.unhexlify(mac_random.replace(':', ''))

# 打印生成的随机MAC地址
print(mac_random)

# 创建一个DHCP Discover数据包
# Ether部分包含了源MAC地址和目标MAC地址, 源MAC地址为之前随机生成的MAC地址, 目标MAC地址为广播MAC地址ff:ff:ff:ff:ff:ff
# IP部分包含了源IP地址和目标IP地址, 源IP地址为0.0.0.0, 目标IP地址为广播IP地址255.255.255.255
# UDP部分包含了源端口和目标端口, DHCP客户端通常使用68作为源端口, DHCP服务端通常使用67作为目标端口
# BOOTP部分包含了客户端硬件地址和事务ID
# DHCP部分定义了这个报文是一个DHCP Discover报文
dhcp_discover = Ether(src=mac_random, dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=client_mac_id, xid=xid_random) / DHCP(options=[("message-type", "discover"), "end"])

# 使用Scapy的sendp函数发送DHCP Discover数据包
sendp(dhcp_discover)

这时打开Wireshark, 并将过滤器设置为 UDP, 然后执行上面的这个程序, 可以捕获到 DHCP发现和提供报文:

alt text

分析得到的数据包, 可以看出网络中有一台DHCP服务器, 地址为10.0.0.254。

3.1.2攻击DHCP服务器

使用 Yersinia 工具进行 DHCP 攻击。我使用的环境是 Ubuntu 22.04 Desktop, 使用包管理即可安装最新版的 Yersinia。kali最新版没有内置Yersinia, 并且手动安装的 Yersinia 无法启动 GUI, 解决方案点这里

  1. 安装 Yersinia
1
2
sudo apt update
sudo apt install yersinia
  1. 启动 Yersinia 的 GUI(若弹出 alpha 版本警告, 忽略即可)
1
sudo yersinia -G

alt text

  1. 选择 DHCP 攻击

alt text

单击 Lauch attack 选择攻击方式, 在 Choose attack 窗口中, 选择 DHCP 协议, 可以看到基于DHCP的攻击中一共提供了4种发包模式, 它们的含义如下:

  • sending RAW packet:发送原始数据包。
  • sending DISCOVER packet:发送请求获取IP地址的数据包, 通过占用所有的 IP 地址而使得地址池迅速枯竭造成拒绝服务
  • creating DHCP rogue server:创建虚假DHCP服务器, 让用户连接, 真正的 DHCP 服务器无法工作。
  • sending RELEASE packet:发送释放IP地址请求到 DHCP 服务器, 致使正在使用的 IP 地址全部失效。

其中 sending DISCOVER packet 形式默认采用拒绝服务攻击(后面的DoS复选框中显示被选中状态)。选中这种方式之后单击 OK 按钮, 即可开始攻击:

alt text

执行攻击后, 中间部分显示的就是发送出去的攻击数据包, 如果希望查看某一个数据包的具体内容, 可以单击该数据包, 下面显示的就是这个数据包的详细内容。可以看到这个工具不断地向外发送广播数据包。

4. DHCP 防护

  1. 开启交换机的 DHCP Snooping 功能, 开启后交换机默认会将所有端口设为非信任端口, 只有被信任的端口才能发送 DHCP 提供报文, 防止 DHCP 服务器欺骗, 同时可以限制 DHCP 报文的发送速率。

  2. 开启交换机的 MAC 地址验证功能(包含在 DHCP Snooping功能中), 开启后交换机会比较非信任端口的客户端发送的 DHCP 报文的源 MAC 地址(数据链路层)与是否与 DHCP 报文中的客户端硬件地址(HADDR, DHCP报文中的可选字段)相同, 相同则允许发送 DHCP 报文, 否则丢弃。

  3. 开启交换机的端口安全功能, 限制端口能够学习的MAC地址数量, 防止攻击者通过发送大量同时伪装了客户端物理地址和源 MAC 地址的 DHCP 报文导致地址池枯竭从而拒绝服务。

参考书籍:

  1. https://yd.qq.com/web/bookDetail/3b932370811e7cef3g011ec9

  2. https://book.douban.com/subject/35298615/

  3. https://support.huawei.com/enterprise/zh/doc/EDOC1100323034/5d03eabb