На протяжении нескольких лет исследователи обнаруживали случаи заражения устройств на базе iOS целевым вредоносным ПО, таким как Pegasus, Predator, Reign и другим. Часто процесс заражения устройства включал в себя работу целой цепочки различных эксплойтов, например, для побега из песочницы iMessage при обработке вредоносного вложения, а также для получения root-привилегий через уязвимость в ядре. В силу этого обнаружение одного звена цепочки часто не приводит к раскрытию всех используемых в ходе атаки компонентов. Например, в 2021 году при помощи анализа резервных копий iTunes удалось получить вложение с эксплойтом FORCEDENTRY. Однако после эксплуатации уязвимости вредоносный код скачивал с удалённого сервера полезную нагрузку, которая была недоступна во время анализа. В результате этого исследователи потеряли «возможность исследования дальнейшей цепочки заражения».
Во время исследования «Операции Триангуляция» мы поставили цель получить как можно больше компонентов цепочки заражения. На достижение этой цели ушло около полугода, и после завершения сбора мы приступили к анализу всех обнаруженных компонентов. На данный момент мы завершили анализ шпионского импланта, о котором и пойдет речь в этой статье.
Имплант, который мы назвали TriangleDB, загружается на устройства после того, как атакующие получают root-права в результате успешной эксплуатации уязвимости в ядре iOS. Имплант работает только в оперативной памяти, следовательно, все его следы теряются при перезагрузке устройства. То есть, если жертва перезагружает устройство, то атакующим приходится перезаражать его: отправлять сообщение iMessage с вредоносным вложением и вновь инициировать всю цепочку заражения. Если же перезагрузки не происходит, то имплант работает на протяжении 30 дней и, если злоумышленники не продлевают этот срок, затем самоудаляется.
Знакомьтесь, TriangleDB
Имплант TriangleDB написан на Objective-C – языке программирования, который при компиляции сохраняет имена, которые разработчики присвоили методам и атрибутам классов. В исполняемом файле импланта имена методов необфусцированы, однако имена атрибутов классов представляют собой неинформативные аббревиатуры, что затрудняет понимание их предназначения:
Примеры имен методов классов | Примеры имен атрибутов классов |
-[CRConfig populateWithFieldsMacOSOnly] -[CRConfig populateWithSysInfo] -[CRConfig extendFor:] -[CRConfig getCInfoForDump] +[CRConfig sharedInstance] +[CRConfig unmungeHexString:] -[CRConfig init] -[CRConfig getBuildArchitecture] -[CRConfig cLS] -[CRConfig setVersion] -[CRConfig swapLpServerType] -[CRConfig setLpServerType:] |
NSString *pubKI; NSData *pubK; signed __int64 iDa; signed __int64 uD; NSString *deN; NSSTring *prT; NSString *seN; NSString *uDI; NSString *iME; NSString *meI; NSString *osV; CRPwrInfo *pwI; |
В некоторых случаях всё же можно догадаться, что значат сокращения. Например, osV — версия iOS, а iME — IMEI-идентификатор устройства.
Строки в импланте закодированы в шестнадцатеричном виде и зашифрованы алгоритмом Rolling XOR:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
id +[CRConfig unmungeHexString:](id a1, SEL a2, id stringToDecrypt) { // код удален while (1) { hexByte[0] = stringBytes[i]; hexByte[1] = stringBytes[i + 1]; encryptedByte = strtoul(hexByte, &__endptr, 16); if (__endptr == hexByte) break; i += 2LL; if (j) decryptedString[j] = encryptedByte ^ previousByte; else decryptedString[0] = encryptedByte; ++j; previousByte = encryptedByte; if (i >= stringLength) break; } decryptedString[j] = 0; // код удален } |
Код алгоритма Rolling XOR, применяемого для расшифровки строк
Общение с командным сервером
После запуска имплант начинает общаться с командным сервером, используя библиотеку Protobuf для обмена данными. Конфигурация импланта содержит два сервера: основной и резервный. В штатном режиме работы имплант использует основной сервер, а в случае возникновения ошибок соединения переключается на резервный, вызывая для этого метод -[CRConfig swapLpServerType:].
Также отправляемые и получаемые сообщения шифруются при помощи симметричной (3DES) и асимметричной (RSA) криптографии. Обмен сообщениями происходит при помощи протокола HTTPS: данные передаются в POST-запросах, содержащих cookie с именем g и значением, прописанным в параметре конфигурации pubKI, представляющем собой строку из цифр.
Имплант периодически отправляет на сервер heartbeat-сообщения, содержащие в том числе версию зловреда, идентификаторы устройства (IMEI, MEID, серийный номер и т.д.), а также конфигурацию службы обновлений (информация о том, включена ли на устройстве автоматическая загрузка и установка обновлений).
Команды TriangleDB
Сервер в ответ на heartbeat-сообщения от импланта отправляет команды, передаваемые как сообщения Protobuf, имена которых начинаются с префикса CRX. Имена этих команд имеют неоднозначный смысл – например, команда для получения списка файлов в директории, называется CRXShowTables (показать таблицы), а для изменения адресов командных серверов используется команда CRXConfigureDBServer (настроить сервер базы данных). Имплант реализует 24 команды, предназначенные для:
- Взаимодействия с файловой системой (создание, изменение, кража и удаление файлов);
- Взаимодействия с процессами (получение списка и их завершение);
- Извлечения данных из связки ключей (Keychain) устройства, содержащей пароли и учетные данные;
- Отслеживания геолокации жертвы;
- Запуска дополнительных модулей – Mach-O файлов, загружаемых имплантом. Эти файлы загружаются рефлективно и хранятся только в памяти.
Одна из интересных команд называется CRXPollRecords. Она отслеживает изменения в директориях, осуществляя поиск модифицированных файлов, имена которых соответствуют задаваемым регулярным выражениям. Для отслеживания изменений имплант получает файловый дескриптор директории и назначает ему обработчик событий vnode. Когда имплант оповещается об изменении, обработчик событий находит модифицированные файлы с подходящими именами. Эти файлы затем загружаются на командный сервер.
У команды имеются следующие параметры:
Имя параметра | Описание параметра |
p | Путь к директории |
m | Регулярное выражение для имен файлов |
sDC | Указывает, нужно ли загружать на командный сервер файлы, модифицированные до начала работы команды |
eWo | Указывает, нужно ли загружать файлы только через Wi-Fi |
Ниже приведено описание всех команд импланта. Там, где это возможно, вместе с числовыми идентификаторами команд указываются присвоенные разработчиками имена команд.
ID команды | Имя, присвоенное разработчиками импланта | Описание |
0xFEED | CRXBlank | Не выполняет никаких действий. |
0xF001 | N/A | Удаляет имплант, завершая его процесс. |
0xF301 | CRXPause | Останавливает работу импланта на указанное число секунд. |
0xFE01 | N/A | Останавливает работу импланта на псевдослучайное число секунд, определяемое параметрами конфигурации caS и caP. Время приостановки выбирается из промежутка от caP — caS до caP + caS. |
0xFB01 | CRXForward | Изменяет значение конфигурации caP для команды 0xFE01. |
0xFB02 | CRXFastForward | Изменяет значение конфигурации caS для команды 0xFE01. |
0xF201 | CRXConfigureDBServer | Изменяет адреса основного и резервного командных серверов. |
0xF403 | CRXUpdateConfigInfo | Изменяет параметры конфигурации импланта. Аргументы этой команды содержат идентификатор параметра и его новое значение. Интересно, что идентификаторы – это числа, написанные прописью, например, «nineteen» или «twentyone». |
0xF101 | CRXExtendTimeout | Продлевает время жизни импланта (равное по умолчанию 30 дням) на указанное количество секунд |
0xF601 | CRXQueryShowTables | Получает список файлов в указанной директории при помощи API fts. |
0xF801 | CRXFetchRecordInfo | Получает свойства (атрибуты, права доступа, размер, даты создания, изменения и последнего доступа) указанного файла. |
0xF501 | CRXFetchRecord | Получает содержимое указанного файла. |
0xFC10 | CRXPollRecords | Начинает отслеживать изменения файлов, имена которых соответствуют заданному регулярному выражению, в указанной директории. |
0xFC11 | CRXStopPollingRecords | Останавливает выполнение команды CRXPollRecords. |
0xFC01 | CRXFetchMatchingRecords | Получает список файлов, имена которых соответствуют заданному регулярному выражению |
0xF901 | CRXUpdateRecord | В зависимости от значения аргумента iM либо записывает данные в файл, либо устанавливает новый модуль. |
0xFA02 | CRXRunRecord | Запускает модуль с заданным именем, рефлективно загружая его исполняемый Mach-O файл. |
0xF902 | CRXUpdateRunRecord | Устанавливает новый модуль и запускает его. |
0xFA01 | CRXDeleteRecord | В зависимости от аргументов удаляет либо модуль, либо файл с заданным именем. |
0xF402 | CRXGetSchemas | Получает список запущенных процессов. |
0xFB44 | CRXPurgeRecord | Завершает работу процесса с заданным PID, используя, в зависимости от аргументов команды, один из сигналов – SIGKILL или SIGSTOP. |
0xFD01 | N/A | Получает информацию об установленных на устройство приложениях. |
0xFB03 | CRXGetIndexesV2 | Получает содержимое связки ключей (Keychain) устройства. Команда отслеживает, разблокирован ли экран устройства, и, когда экран разблокируется, извлекает данные из таблиц ключевой связки genp (с паролями общего назначения), inet (с паролями Интернет-приложений), keys и cert (с сертификатами и ключами) из базы данных /private/var/Keychains/keychain-2.db. Также интересно, что команда поддерживает работу с различными версиями связок ключей, начиная с используемых в iOS 4. |
0xF401 | N/A | Получает сведения о геолокации жертвы: координаты, высоту, направление движения и скорость. Интересно, что эта команда по умолчанию работает, только если экран устройства выключен. Однако это ограничение можно обойти при помощи изменения значения конфигурации. |
Необычные находки
Исследуя имплант TriangleDB, мы обнаружили множество деталей, показавшихся нам интересными:
- Разработчики называют расшифровку строк странным термином ‘unmunging’ (метод, осуществляющий расшифровку строк, назван +[CRConfig unmungeHexString:])
- В коде импланта различным сущностям даны имена из терминологии баз данных. Именно использование этой терминологии побудило нас назвать имплант TriangleDB:
Сущность Используемый разработчикам термин для сущности Директория Таблица (Table) Файл Запись (Record) Модуль Процесс Схема (Schema) Запись в связке ключей Индекс, строка (Index, row) C2-сервер Сервер базы данных (DB Server) Информация о геолокации Статус базы данных (DB Status) Heartbeat-сообщение Диагностические данные (Diagnostic data) Процесс обмена данными с сервером Транзакция (Transaction) Обращение к C2-серверу Запрос (Query) iOS-приложение Операция - Во время анализа мы обнаружили, что у класса CRConfig (хранящего конфигурацию импланта) есть метод populateWithFieldsMacOSOnly. Этот метод не вызывается в iOS-версии импланта, однако его существование может означать, что схожий имплант используется в атаках на macOS.
- Имплант запрашивает набор прав (англ. entitlements) у операционной системы. Некоторые из них не используются в коде, например, доступ к камере, микрофону, адресной книге или же взаимодействие через Bluetooth. Функции, доступ к которым позволяют получить эти права, могут быть использованы в дополнительных модулях.
Продолжение следует
Вот и все, что мы можем пока рассказать о TriangleDB, iOS-импланте с примечательными деталями. Но мы продолжаем анализировать кампанию «Операция Триангуляция», и будем делиться информацией об этой сложной атаке по мере продолжения исследования.
Индикаторы компрометации TriangleDB
MD5 063db86f015fe99fdd821b251f14446d
SHA-1 1a321b77be6a523ddde4661a5725043aba0f037f
SHA-256 fd9e97cfb55f9cfb5d3e1388f712edd952d902f23a583826ebe55e9e322f730f
Анализ TriangleDB, импланта «Операции Триангуляция»