Positive Technologies
PT Expert Security Center

LazyStealer: сложно не значит лучше

LazyStealer: сложно не значит лучше

Содержание

Введение

В первом квартале 2024 года специалисты экспертного центра безопасности Positive Technologies (PT Expert Security Center, PT ESC) обнаружили серию атак, направленных на государственные структуры России, Беларуси, Казахстана, Узбекистана, Кыргызстана, Таджикистана, Армении. Связей с уже известными группировками, использующими такие же техники, нам установить не удалось. Основной целью атаки была кража учетных записей от различных сервисов с компьютеров работников государственных структур. Эту группировку мы назвали Lazy Koala из-за простых техник и имени пользователя, который управлял телеграм-ботами с украденными данными. ВПО, которое использовала группа для своих атак, мы назвали LazyStealer из-за простоты реализации, но в тоже время атаки с его использованием оказались продуктивными. Точный вектор заражения нам установить не удалось, однако по всем признакам это был фишинг. Все жертвы были напрямую уведомлены нами о компрометации.

Анализ LazyStealer

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

Декомпилированный код после снятия PyInstaller
Рисунок 1. Декомпилированный код после снятия PyInstaller

Для снятия протектора нам понадобится скрипт bypass.py, установленный модуль Pyarmor, а также файл pytransform.py из этого модуля. Этот файл нужно разместить в той же папке, что и целевой файл с расширением .ру. После снятия протектора в одном из вариантов образцов мы встретили скрипт, все, что он делает, — это занимается подключением Python-модулей.

Декомпилированный код после снятия PyArmor
Рисунок 2. Декомпилированный код после снятия PyArmor

Выделенные на рисунке модули, которые в файловой системе имеют названия hello.cp39-win_amd64.pyd и pdfbyte.cp39-win_amd64.pyd, являются библиотеками DLL, собранными с помощью Cython. Они будут запущены при их импорте. При компиляции через Cython от первоначального скрипта сохраняются только строковые и числовые константы, а вся логика (например, циклы, операции присвоения, передача аргументов) будет реализована нативно, поэтому получить исходный скрипт не представляется возможным (как в случае с PyInstaller или Pyarmor), но его можно воссоздать близким к оригиналу.

Для начала возьмем pdfbyte.cp39-win_amd64.pyd. Вся логика будет происходить в единственной экспортируемой функции PyInit_pdfbyte. В этом случае постфикс pdfbyte обозначает имя модуля, скомпилированного из pdfbyte.pyx. В зависимости от названия модуля меняться будет и постфикс имени экспортируемой функции.

Экспортируемая функция PyInit_pdfbyte
Рисунок 3. Экспортируемая функция PyInit_pdfbyte

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

Значение глобальной переменной _pyx_moduledef
Рисунок 4. Значение глобальной переменной _pyx_moduledef

Аргумент pyx_moduledef будет иметь структуру, представленную ниже, где нас интересует поле -m_slots. Для того чтобы найти функцию, которая отвечает за инициализацию и исполнение кода скрипта, нужно перейти по адресу этого поля.

    
struct PyModuleDef { PyModuleDef_Base m_base; const char* m_name; const char* m_doc; __int64 m_size; PyMethodDef* m_methods; PyModuleDef_Slot* m_slots; int(__cdecl* m_traverse)(_object*, int(__cdecl*)(_object*, void*), void*); int(__cdecl* m_clear)(_object*); void(__cdecl* m_free)(void*); };

После перехода мы обнаружим массив структуры с PyModuleDef_Slot с двумя функциями.

Значение глобальной переменной _pyx_moduledef_slots
Рисунок 5. Значение глобальной переменной _pyx_moduledef_slots

Структура PyModuleDef_Slot представлена ниже, нас интересует поле value.

    
struct PyModuleDef_Slot { __int64 slot; void* value; };

Под нулевым индексом массива __pyx_moduledef_slots поле value — функция _pyx_py_mode_create, которая является конструктором модуля. А уже под первым индексом —_pyx_pymod_exec_pdf, которая и является искомой нами функцией. В ней с самого начала происходит инициализация служебных полей _pyx_mstate_global.

Инициализация служебных полей глобальной структуры _pyx_mstate
Рисунок 6. Инициализация служебных полей глобальной структуры _pyx_mstate

Глобальная переменная _pyx_mstate_global является менеджером всех объектов и имеет структуру, представленную ниже, где интересующие нас поля будут начинаться с индекса 6, обозначенными в структуре виде массива n полей _pyx_object.

    
struct __pyx_mstate { _object* __pyx_d; _object* __pyx_b; _object* __pyx_cython_runtime; _object* __pyx_empty_tuple; _object* __pyx_empty_bytes; _object* __pyx_empty_unicode; _object* __pyx_object[n]; };

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

Пример инициализации строковых констант в функции Pyx_CreateStringTabAndInitStrings представлен ниже.

Инициализация строковых констант
Рисунок 7. Инициализация строковых констант

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

    
struct __Pyx_StringTabEntry { _object** p; const char* s; const __int64 n; const char* encoding; const char is_unicode; const char is_str; const char intern; };

Пример подключения модулей.

Подключение модулей
Рисунок 8. Подключение модулей

Пример присвоения значения переменной.

Операция присвоения значения переменной
Рисунок 9. Операция присвоения значения переменной

Пример вызова метода из подключенного модуля с аргументом.

Операция вызова функции модуля с аргументом
Рисунок 10. Операция вызова функции модуля с аргументом

Зная все это, мы можем воспроизвести скрипт.

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

Выполнения этого кода приведет к открытию документа в браузере. Набор из четырех разных документов продемонстрирован ниже.

Документ, использовавшийся для атаки на Узбекистан
Рисунок 12. Документ, использовавшийся для атаки на Узбекистан
Документ, использовавшийся для атаки на Кыргызстан
Рисунок 13. Документ, использовавшийся для атаки на Кыргызстан
Документ, использовавшийся для атаки на Таджикистан
Рисунок 14. Документ, использовавшийся для атаки на Таджикистан
Документ, использовавшийся для атаки на Армению
Рисунок 15. Документ, использовавшийся для атаки на Армению

Далее мы рассмотрим нативные конструкции в hello.cp39-win_amd64.pyd, которых не было в pdfbyte.cp39-win_amd64.pyd ввиду простоты и краткости исходного скрипта.

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

Инициализация локальных переменных для функции
Рисунок 16. Инициализация локальных переменных для функции

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

Существующие функции
Рисунок 17. Существующие функции

Структура функции представлена ниже, нас интересуют поля:

  • ml_name — имя функции;
  • ml_meth — нативная реализация функции.
    
struct PyMethodDef { const char* ml_name; _object* (__cdecl* ml_meth)(_object*, _object*); int ml_flags; const char* ml_doc; };

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

Инициализация функции
Рисунок 18. Инициализация функции

Имея все это, мы можем воспроизвести скрипт.

Воспроизведенный скрипт для кражи учетных записей
Рисунок 19. Воспроизведенный скрипт для кражи учетных записей

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

Кроме того, был образец, в котором только логика показа документа была написана на Cython, а логика кражи паролей — на чистом Python. Различия заключаются в том, что переменные и функции называются по-разному, а также есть незначительные изменения в структуре программы.

Скрипт без использования Cython, логика получения учетных записей
Рисунок 20. Скрипт без использования Cython, логика получения учетных записей
Скрипт без использования Cython, основная логика
Рисунок 21. Скрипт без использования Cython, основная логика

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

Атрибуция

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

Граф связи между ботами и пользователем, построенный Telegram Bots Viewer
Рисунок 22. Граф связи между ботами и пользователем, построенный Telegram Bots Viewer

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

Жертвы

Жертвами группировки Lazy Koala стали государственные, финансовые, медицинские, образовательные отрасли России, Беларуси, Казахстана, Таджикистана, Кыргызстана, Армении и Узбекистана. Общее количество скомпрометированных учетных записей на момент обнаружения составило 867, из них уникальных — 321. Все жертвы были напрямую уведомлены нами о компрометации.

Вывод

Группировка Lazy Koala демонстрирует, что для успешных атак необязательно использовать сложные инструменты, тактики и техники. Сложно не значит лучше. Достаточно лишь убедить жертву в том, чтобы она запустила нужный файл. Основной их инструмент — примитивный стилер, защита которого позволяет избежать обнаружения, замедлить анализ, получить и отправить все украденные данные во все также набирающий год за годом популярность среди злоумышленников Телеграм. Судьба украденных данных — перепродажа или использование их для дальнейших атак уже на внутренние структуры компаний.

Автор: Владислав Лунин

Индикаторы компрометации

ФАЙЛ MD5 SHA-1 SHA-256
33ms.exe 4f060c5c6813e269f01e6cba1d3ac4cd 4f0a1831d4d8c09f46e8f5fbe8b17b024daa6eee 9fd197b7402285ed2a75dac9a5ce3ef499a58342fd0dcefe1c40443a12bc6832
Recommendation.exe 641932b66490630005dde2aef405e5e9 9bad63eab92144b8a365428aa68531c80fc2da0f e419a8158c6fe326dc7ab16dbd5f3b2723dffe8c9561fe835bb16f62a8fa61f5
05-1254_Minzrav.exe 882d63c5ff749f232a3ce70a36c95b83 cd1f89f3d56df6a775d8694c1cbf588961dc7f06 a6e68f3066424daae4a54b2e0b01a4474a9a381469ae69daae6fef9a1626fa6d
- fe245cf57be8b3daf8cdb3882de99f35 40789ef406772e52a0dfc86509cc7617fa8b54a3 1db3d0ac68515b5c9876634605ba8492ba558f7df435bff2b20a74239107f3ec
test.pyc 8e233b0250d85ae63076af45ee829c55 ec14cf28fe8764d4f285b95ee7001af49ff0af68 5ecdf5efe2a74db93450f2b35e942b91ee6dd1b0f545c04810d2794b748b1dea
test.pyc 032a586d08e7f31e2aedbec61d5d0f62 51ad91409698d8f4017defbd0a382cce9e69ed6f 9fc75a6a17238ec3833dce0605b334c03fd84363f56313a5bf58d57ff286a9f9
1.pyc 8cb819b48958540fac07244188508156 1f204cfb02df849f935c296a5e4b2f120bfa563b 7d3733513e0645e66009e3d677af76653baa75c8ddf0d126aa0f270b56183272
test.pyc 2d51a6620c976e1d736448082338e0b1 755ade0ddaceaabe9577d22a240e0430375f502f 216f4e858f84269bee999fdc29dafbd79ec2270575e19a8626e25d5fe72a8f25
hello.cp39-win_amd64.pyd 763eb39787756744b4062336eb945750 7685dd23d64fa94bb8d2d54dd2e104fbe5379ec5 8246e66ff043374477c06a612602f6e8a2cb487a33d8b046357a6c4870648ed1
hello.cp39-win_amd64.pyd 5b84b516760773c538647bc6e4d26d37 3e497222f9bc13d43d6a3e5fbdcae3474b3d2d22 ef6fb63259eac9f7642e468726a042f5a29576bf9f846b96fa6ded8bf145b64c
hello.cp39-win_amd64.pyd 1dedf5772ea1126b79b5e22ca10cefd3 140968b7004aca9785a0a1f0a6712322db22fd6c f2a8088f1a634e62a2d0e5b2d6427d67fae640bf03dd04c8571006e1f31d7992
pdfbyte.cp39-win_amd64.pyd 0f5727bada96b3b62573bba51538e9e3 6f54d068423cee9b2cf5ef50b4348025f983e220 bfa3718f6492dd337c127ccdbd8033b503ca089699ddbff3ac5c45f5f95f01e8
pdfbyte.cp39-win_amd64.pyd c3242bce783d5fa0ab0ce645f1283c64 845be44fb0d663636e500187d7d394714e562e08 1549114ea6d86198d29f79a009218ca991aa17d215a84b90e3c91ef3268180e4
pdfbyte.cp39-win_amd64.pyd 1cff5f65c85d8cf614beedf8fd5112d7 c10637e35dfe326bd2c9a92f432d483f2f7591bd 864a38b028d5b9e41fa0d4eee7cfa3a284d0ab9874b42cc4d50f1e2b2e26e1e5
docpdf.cp39-win_amd64.pyd 98914403f428abeea89c94e0b7edaaa9 9866dfedbd311ed2f838ec56947cdf4ccabe8634 18e00bb5dee23815a89067258b11ef13d6327bcb3555d70596c906d4875ed8c2

Тактики и техники по матрице MITRE ATT&CK

ID Имя Описание

Execution

T1204.002 User Execution: Malicious File Группировка Lazy Koala выдает исполняемый файл за документ

Defense Evasion

T1140 Deobfuscate/Decode Files or Information Группировка Lazy Koala использует упаковщик PyInstaller, протектор Pyarmor и компилятор Cython

Credential Access

T1555.003 Credentials from Password Stores: Credentials from Web Browsers Группировка Lazy Koala получает учетные записи из браузера Google Chrome

Exfiltration

T1567 Exfiltration Over Web Service Группировка Lazy Koala отправляет полученные учетные записи телеграм-боту

Вердикты продуктов Positive Technologies

PT Sandbox

Suspicious:

  • Read.Window.Handle.Enumeration
  • Create.Process.Taskkill.TerminateProcess
  • Read.Thread.Info.AntiDebug, Write.Thread.Info.AntiDebug
  • Read.File.Browser.Credentials

Malware:

  • Trojan-Spy.Win32.LazyStealer.a
  • Trojan_PSW.Win32.Generic.a
  • Trojan.Win32.Generic.a

MaxPatrol SIEM

Run_Masquerading_Executable_File

Suspicious_Connection

Credential_Access_to_Passwords_Storage

PT NAD

tls.server_name == "api.telegram.org"