Стеганография на базе стека протоколов TCP/IP. Часть 3

Стеганография на базе стека протоколов TCP/IP. Часть 3

Эта статья является последней в серии, посвященной стеганографии на базе стека протоколов TCP/IP, которая используется для организации скрытого шелла с целью удаленного выполнения кода.

Автор: John Torakis

Прежде, чем начать повествование, установим несколько пакетов.

  1. Алгоритм энтропии Шеннона, реализованный на С и предназначенный для байтовых строк:
  2. pip install entropy           

  3. Набор пакетов для сравнения информационных массивов с известными распределениями. В итоге возвращаются схожие соотношения:
  4. pip install fitter     
    pip install pandas     

  5. Пакет для построения графиков на основе полученных данных:

pip install matplotlib 

Тестовая среда: Windows 7 Ultimate 64 bit, установленная на виртуальной машине. Фаервол отключен (обязательное условие).

Эта статья является последней в серии, посвященной стеганографии на базе стека протоколов TCP/IP, которая используется для организации скрытого шелла с целью удаленного выполнения кода. В третьей части мы рассмотрим методы анализа трафика и раскрытия каналов, функционирующих на основе нашей и других похожих схем. Перед дальнейшим чтением рекомендуем ознакомиться с первой и второй статьей этого цикла.

Дискуссия об энтропии

Вся наша концепция о случайности, лежащая в основе схемы обхода систем безопасности и любых видов криптоанализа, базировалась на том, что мы пытались замаскировать создание пакетов от имени операционной системы (или сканера nmap). Однако вскоре выяснилось, что этот метод не так хорош, как кажется на первый взгляд (по крайней мере, в случае, если используется несколько полей в заголовках TCP/IP).

Хорошие новости

Содержимое идентификационных полей, используемых протоколом IP в SYN-пакетах, является практически случайным.

Проанализировав байты в передаваемых пакетах при запуске команды head /etc/shadow, я получил следующие результаты:

Энтропия устройства /dev/random - 0.836880. Объем выборки - 152 байта.
Энтропия содержимого поля ID у пакетов управляющего сервера - 0.836477.  Объем выборки - 152 байта.
Энтропия содержимого поля ID у пакетов агента, установленного в системе жертвы, - 0.849231.  Объем выборки - 152 байта.

Энтропия устройства /dev/random - 0.922406. Объем выборки -  304 байта.
Энтропия содержимого поля SEQ у пакетов управляющего сервера - 0.919504. Объем выборки - 304 байта.
Энтропия содержимого поля SEQ у пакетов агента, установленного в системе жертвы, - 0.906196 Объем выборки - 304 байта.

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

Плохие новости

Теперь проанализируем пакеты, создаваемые настоящим сканером nmap на Windows-машине.

Запускаем команду nmap 192.168.56.101.
Энтропия устройства /dev/random - 0.987001. Объем выборки - 1982 байта.
Энтропия содержимого поля ID у SYN-пакетов - 0.989606.  Объем выборки - 2150 байт.
Энтропия содержимого поля ID у RST-пакетов агента - 0.755026.  Объем выборки - 1982 байта.
Энтропия устройства /dev/random - 0.987001. Объем выборки - 3964 байта.
Энтропия содержимого поля SEQ у SYN-пакетов - 0.272816.  Объем выборки - 4300 байт.
Энтропия содержимого поля SEQ у RST-пакетов агента - 0.000000.  Объем выборки - 3964 байта.

По результатам анализа пакетов, формируемых при реальном сканировании, выясняется, что поле SEQ в RST-пакетах всегда равно 0. Смотрим выдержку из RFC 793 на странице 65:

If the state is CLOSED [...] then
[...]  The acknowledgment and sequence field values are selected
to make the reset sequence acceptable to the TCP that sent
the offending segment.

      If the ACK bit is off, sequence number zero is used,   # This is a
<SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>            # port scan reply

      If the ACK bit is on,
<SEQ=SEG.ACK><CTL=RST>

Если подытожить, что написано выше, то стеганография через поле SEQ у пакетов, отсылаемых агентом из системы жертвы, невозможна. Сей факт означает, что ответы будут иметь размер в 1 байт (+1 байта на опкод) и будут передаваться только через поле Identification протокола IP.

С другой стороны, удаленное выполнение кода вслепую будет работать, как и предполагалось изначально.

В общем, SYN-пакеты, отправляемые с управляющего сервера, скорее всего, смешиваются с пакетами операционной системы, поскольку ОС подставляет случайное содержимое в поле ID. Сканер nmap без параметров использует не поддельные пакеты, а системный вызов connect(), вследствие чего нет необходимости в правах суперпользователя.

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

figure_1
Рисунок 1: Распределение значений поля ID в SYN-пакетах

Диапазон возможных значений: 0-65535(2^16).

С другой стороны, RST-пакеты не перемешиваются настолько хорошо.

https://securosophy.files.wordpress.com/2016/09/rst_id_windows.png
Рисунок 2: Распределение значений поля ID в RST-пакетах в Windows 7

https://securosophy.files.wordpress.com/2016/09/rst_id_linux1.png
Рисунок 3: Распределение значений поля ID в RST-пакетах в Linux

Подобное происходит потому, что операционные системы используют значения Sequential ID в качестве ответов на запросы о сканировании портов вместо случайных значений. При анализе RST-пакетов, отсылаемых жертвой, получаем небольшие столбики, распределенные по всему диапазону. В то же время столбики, полученные при анализе RST-пакетов операционных систем интерпретируются как единичные и большие, поскольку значения находятся очень близко к друг другу.

По результатам анализа поля Identification подведем некоторые итоги. В RFC, в котором появилось обновление, касаемое переназначения поля ID (RFC 6864), говорится о проблеме скрытых каналов (раздел Security Considerations, страница 16). В качестве решения предлагается внедрение алгоритма перезаписи поля ID в пакетах при прохождении через роутеры и фаерволы с целью усиления незаметности и предотвращения идентификации операционной системы. В целом, новый RFC уже стал большой проблемой для любителей скрытых каналов, из-за всего лишь одного переопределения поля ID в протоколе IP (по сравнению с тем, что было в RFC 791).

Поле Sequence в протоколе TCP

После анализа содержимого этого поля становится ясно, что битва полностью проиграна.

Рассмотрим гистограммы распределения значений поля ISN в SYN-пакетах (максимальное значение 2^32 = 4.294.967.296 ~ 4,294 * 10^-9).
RST_SEQ_Windows.png
Рисунок 4: Распределение значений поля SEQ в SYN-пакетах

Из рисунка выше видно, что nmap использует один и тот же номер последовательности при сканировании всех портов. Сей факт является очень странным. С одной стороны, команда nmap –sS запускается от имени суперпользователя, и здесь вполне могут формироваться одни и те же пакеты (без изменения полей ISN и Destination Port). Однако почему формируются одни и те же значения полей в других случаях? Разве стек операционной системы не должен менять значение поля ISN?

Смотрим подробнее содержимое поля, где хранится исходный порт (Source Port):

SYN_SPORT_Linux.png
Рисунок 5: Распределение значений поля SPORT в SYN-пакетах

Из рисунка выше видно, что nmap использует одно и то же значение в поле Source Port при сканировании всех портов, и поэтому в поле ISN значение также не меняется. В итоге получается, что мы не можем замаскироваться под nmap. Игра окончена.

Если сравнить распределение значений, используемых в нашей схеме, со значениями, используемыми netcat, получаем такую картину:
SYN_SEQ_netcat.png
Рисунок 6: Сравнение распределений значений поля SEQ в SYN-пакетах в нашей схеме и netcat

Netcat использует различные исходные порты.
SYN_SPORT_netcat.png
Рисунок 6: Распределение значений поля SPORT в SYN-пакетах в нашей схеме и netcat

Из рисунка выше видно, что в нашей схеме используется более узкий диапазон портов с большими значениями (механизм формирования портов заложен в алгоритме ширования). Netcat используется намного более случайную последовательность портов. Но даже несмотря на то, что видны различия, я не считаю, что распределение портов в более узкой части диапазона является доказательством использования скрытого канала.

Анализ RST-пакетов

rst_seq_linux
Рисунок 6: Распределение значений поля SEQ в RST-пакетах при использовании netcat

Значения, используемые в нашей схеме, настолько разбросаны, что на графике не видны.

В итоге выясняется, что мы нарушаем правила протокола TCP. Номер последовательности в RST-пакетах равен нулю до тех пор, пока система жертвы не будет скомпрометирована. Подобное нельзя скрыть. По крайней мере, следующее поколение фаерволов должно уметь распознавать эту аномалию.

Другие отклонения

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

Распределение значений временного интервал между пакетами, исходящими от одного и того же источника, имеет определенную структуру. Сканеры masscan, zmap, nmap и netcat отличаются по этому параметру.

RST_TIME_Linux.png
Рисунок 7: Распределение значений поля TIME в RST-пакетах в различных сканерах

Графика - красноречивее тысячи слов. Netcat медленнее, чем nmap. Возможно потому, что каждый раз меняет значение поле Source Port.

В нашей схеме временной интервал между пакетами еще больше, поскольку перед отправкой и после получения выполняется множество операций: вычисление 4 хешей SHA512 для каждого пакета, операция XOR, разделение на части, поиск опкодов по словарю. Если у netcat интервал между пакетами около 0.0005 секунд, то в нашей схеме около 0.1-0.2 секунды. Различие на несколько порядков.

Тестирование фаерволов

До этого момента я лишь голословно утверждал, что в логах фаервола не отражается полезной информации, которая позволит выявить скрытый канал. Настало время проверить эту гипотезу на практике.

Настройка тестовой среды

Будут использоваться две виртуальные 32-битные машины с установленным дистрибутивом Ubuntu 12.04.5 без полного комплекта патчей (не у всех есть время и желание заниматься обновлениями). Одна система выступает в роли жертвы, другая – в роли управляющего сервера. Эти две машины будут находиться по разные стороны фаервола.

В качестве фаервола будет использоваться pfSense с плагином Suricata (IDS/IPS). В плагине Suricata активированы все стандартные правила в режиме полной записи логов. Также будем использовать правила приложения Snort.
Управляющий сервер будет генерировать пару SSH-ключей, пересылать публичный ключ на другую сторону (возможно, в папку /root/.ssh/authorized_keys) и авторизироваться в системе жертвы под именем суперпользователя.

Результаты анализа

Вначале я включил слушатель syslog и перенаправил все выходные данные в файл (syslog.log). Далее я стал смотреть содержимое этого файла на предмет изменений (утилиты sed и grep используются только с целью форматирования).
Затем я показываю, что два поддельных RST-пакета, созданных scapy, спровоцировали появление двух предупреждений. Первое – от плагина Suricata IDS, второе – от самого фаервола. Демонстрация проводится с целью показать, что запись логов работает корректно.

Далее я запустил клиента на управляющем сервере и агента в системе жертвы (порядок запуска не имеет значения), создал директорию .ssh/ в папке /root/ и добавил публичный SSH-ключ в файл authorized_keys. К сожалению, после подключения через SSH программа аварийно завершила работу, поэтому данный этап не показан.

На видео ниже демонстрируется вся схема (левый верхний угол – управляющий сервер, левый нижний угол – scapy, правый верхний угол – агент в системе жертвы, правая центральная область – журнал от pfSense, правый нижний угол – слушатель netcat):

Pozzo & Lucky -VS- pfSense from John Torakis on Vimeo.

Как видно на видео, логи, связанные со сканированием портов, отсутствуют, если не включены специальные правила. Даже если были бы логи, эта информация была бы бесполезной. Аномальные TCP-логи на базе сообщений из encoder.alerts в плагине Suricata могли бы быть более пригодными. В итоге в первом раунде тестирования наша схема оказалась устойчивой к обнаружению.

Новый поворот

После 2-3 кликов в мыши в настройках фаервола pfSense наша схема оказывается полностью недееспособной. На видео ниже показаны конкретные настройки, которые позволяют защититься от скрытых каналов на базе стеганографии:

Pozzo & Lucky -VS- pfSense (Round 2) from John Torakis on Vimeo.

Мне всегда было интересно, читает ли кто-нибудь RFC’ы. Теперь я получил ответ на свой вопрос. RFC’ы читают разработчики фаерволов и, надо признать, читают вдумчиво!

На видео выше pfSense полностью нокаутирует скрытый канал между двумя хостами при помощи изменения случайных битов в заголовке пакета. И эта идея также не является новой.

Смотрим выдержку из RFC 6864, на странице 16:

[...] (IP Identification) field can more easily be used as a covert channel.
For some atomic datagrams it is now possible, and may be desirable,
to rewrite the IPv4 ID field to avoid its use as such a channel.

Перевод цитаты выше: поле Identification в протоколе IP очень просто приспосабливается для создания скрытых каналов. В случае с некоторыми элементарными дейтаграммами на данный момент возможна (или даже желательна) перезапись поля ID в протоколе IPv4 с целью предотвращения функционирования подобного канала.

Второй раунд выиграл pfSense.

Будет ли третий раунд?

Не будет. Оба оппонента не готовы к третьему раунду. В pfSense, кажется, отсутствует система более тщательной фильтрации пакетов. На эту мысль наводит факт попустительства при злоупотреблении правилами протокола TCP. В свою очередь, наша схема не настолько умна, чтобы работать в условиях, когда пакеты подрезаются.

Немного щемит сердце после того, как теряется шелл с правами суперпользователя, полученный с таким трудом. Может быть в другой раз.

Заключение

В процессе путешествия в дебрях протоколов TCP/IP было изучено много всего интересного. Например, я узнал о слабостях в сетевом стеке хостов в Linux (RFC1122, страница 63), о проблеме замещения в стеке ядра (какой пакет уходит первым: из ядра или scapy?) и других проблемах, связанных с сетевыми протоколами.

Практическая реализация концепции

Когда инструмент станет достаточно стабильным, я загружу большинство своих наработок github страницу. Часть, связанная с запуском команд, загружена не будет, но будет добавлен код, отвечающий за передачу данных. Любой, кто знаком с Python, сможет самостоятельно реализовать блок, связанный с выполнением команд. Лично я придержу эту часть у себя, поскольку считаю, что иногда люди становятся безответственными, когда получают нечто полностью на халяву.    

Кроме того, я загружу аналитические скрипты, используемые мной в этой статье. Все гистограммы были получены при помощи нажатия одной кнопки, и я считаю эти разработки достаточно ценными и достойными, чтобы поделиться с общественностью. Выложу после того, как немного отполирую код.

Ваш провайдер знает о вас больше, чем ваша девушка?

Присоединяйтесь и узнайте, как это остановить!