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

Один из возможных вариантов — использование атакующими техники DLL-Hijacking (Mitre T1574.001). Я встречал мнение, что техника (а если быть точным, то это подтехника для T1574: Hijack Execution Flow) DLL Hijacking — баян десятилетней давности и не может считаться актуальной угрозой для корпоративных Windows-сред. Затрудняюсь определить причину такого мнения, потому что атаки с использованием DLL-библиотек явно не ушли в прошлое — их по-прежнему упоминают в уважаемых отчетах по кибербезу — к примеру, здесь (Mandiant M-Trends 2024, стр.50) и здесь (Лаборатория Касперского, «Азиатские APT-группировки: тактики, техники и процедуры»). 

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

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

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

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

В этой статье мы: 

  • За 90 секунд освежим в памяти, что такое DLL, как работает, в чем фундаментальная уязвимость механизма загрузки. 

  • Осветим примеры атак с подменой DLL согласно их классификации.

  • Расскажем о защитных мерах для предотвращения атак этого типа.

  • Приведем рекомендации для SOC по обнаружению атаки (и объясним, почему цифровая подпись — не гарантия легитимности библиотеки). 

Введение: понятие и назначение DLL-библиотек, принцип работы и загрузки

Динамически подключаемая библиотека (Dynamic Link Library, DLL) — это компонент операционных систем семейства Microsoft Windows. Эти модули содержат код и данные, которые могут использоваться одновременно несколькими приложениями (процессами). Ключевая идея DLL заключается в разделении и повторном использовании кода — разработчики не встраивают в каждый EXE одинаковый код (на выполнение распространенных функций по типу работы с окнами, сетью или диском): вместо этого они вызывают DLL через таблицу импорта (Import Address Table, IAT). Например, все приложения, работающие с окнами интерфейса в Windows, обращаются к экземпляру соответствующей DLL в памяти, что значительно снижает общий объем ресурсов. DLL библиотеки могут как быть системными, так и предоставляться производителем стороннего программного обеспечения. Для разработчиков приложений использование динамических библиотек дает большую гибкость в процессе разработки и модульность в целевом приложении — проще говоря, для исправлении бага или добавлении новой функции в приложение может быть достаточно внести изменения в одну DLL, не пересобирая exe-файл и (или) все компоненты приложения.

Примером первого случая могут служить библиотеки типа kernel32.dll, user32.dll, gdi32.dll (все три расположены в C:\Windows\System32\). Они содержат код, необходимый большинству как системных приложений, так и сторонних. Kernel32.dll предоставляет базовые функции ядра: управление памятью, операции ввода-вывода, создание процессов, потоков и функции синхронизации. Advapi32.dll обеспечивает вызовы безопасности и функции для управления реестром Windows, user32.DLL реализует компонент Windows USER, который создает и управляет стандартными элементами пользовательского интерфейса Windows, такими как рабочий стол, окна и меню. Таким образом, он позволяет программам реализовывать графический пользовательский интерфейс (GUI). GDI32.DLL экспортирует функции интерфейса графических устройств (GDI), которые выполняют примитивные функции рисования для вывода на видеодисплеи и принтеры. Проиллюстрировать второй случай можно с помощью libvlc.dll, которая является частью VLC Media Player и устанавливается в системе вместе с .exe VLC. Забегая вперед, скажу, что наличие данной библиотеки может свидетельствовать о вредоносной активности в системе.

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

Как приложение загружает DLL? 

Выше я уже упомянул Import Address Table. Это способ, который используется для загрузки библиотеки во время компоновки (Load-Time). При компиляции программы в заголовочный файл записывается таблица импорта, которая содержит информацию, какую функцию из какой DLL-библиотеки нужно импортировать. При запуске программы загрузчик читает таблицу и загружает требуемые функции. 

Помимо статической загрузки, существует динамическая загрузка во время выполнения (Run-Time). Приложение может загружать DLL только тогда, когда они действительно нужны, уже после того, как загрузчик DLL прочитал IAT и отработал, то есть загрузил функции из библиотек по списку. Для  этого необходимо использовать вызовы LoadLibrary / LoadLibraryEx API Windows, подробнее об этом можно прочитать здесь

Также нелишне будет упомянуть, что как у файлов есть таблица импорта (IAT), так  у каждой DLL есть таблица экспорта (Export Address Table, EAT). Когда загрузчик или функция GetProcAddress, отрабатывающая при динамической загрузки после LoadLibrary, когда получен дескриптор, ищут адрес функции, они обращаются именно к EAT нужной DLL. EAT позволяет сопоставить имя функции (или ее порядковый номер) с ее реальным адресом в памяти после загрузки DLL.

В чем заключается проблема? Когда загрузчик DLL или функция LoadLibrary (LoadLibraryEx) получает задачу на поиск динамической библиотеки, в качестве аргумента можно задать имя целевой библиотеки. В этом случае поиск осуществляется в определенном порядке директорий до тех пор, пока не будет найден файл с таким названием. 

На иллюстрации — функция LoadLibrary, которая при выполнении будет искать файл user32.dll. Источник иллюстрации: securitycafe.ro
На иллюстрации — функция LoadLibrary, которая при выполнении будет искать файл user32.dll. Источник иллюстрации: securitycafe.ro

Порядок поиска представлен на диаграмме ниже. Справедливости ради стоит отметить, что перед началом поиска в директории приложения и далее выполняется поиск среди уже загруженных в память библиотек, а затем — в разделе реестра ОС под названием «Known DLLs». Но с точки зрения рассматриваемых вредоносных действий эти места можно считать безопасными. 

Так ищутся библиотеки при включенном SafeDllSearchMode (включен по умолчанию). Не судите строго, нарисовал за 5 секунд в Mermaid
Так ищутся библиотеки при включенном SafeDllSearchMode (включен по умолчанию). Не судите строго, нарисовал за 5 секунд в Mermaid

К чему приводит использование именно такого порядка поиска в контексте атак с вредоносными DLL? Предположим, на машине пользователя существует приложение translator.exe, в IAT которого задана библиотека translator.dll. При установке оба компонента оказываются в одной папке, предположим, C:\Program Files\Translator\. Динамическая библиотека «из коробки» абсолютно легитимна. При запуске translator.exe загрузчик DLL успешно найдет нужную библиотеку, поскольку директория приложения — это буквально первое место, в котором он будет ее искать. 

Но вот появляется атакующий. Он уже прошел стадию разведки, поэтому знает, что на машине установлено приложение Translator.exe с translator.dll. Он прошел стадию вооружения, поэтому у него наготове вредоносные dll-библиотеки с разным уровнем маскировки и нагрузки. Он прошел стадию доставки и заполучил некий доступ к машине с правами обычного пользователя. Что дальше?

Классификация атак с подменой DLL

Сценарий 1. Разовьем пример из предыдущего раздела. Предположим, что по какой-то причине директория C:\Program Files\Translator\ имеет чрезвычайно широкие разрешения — у группы «Все» (Everyone) есть разрешение на запись. Злоумышленник подменяет легитимную библиотеку своей вредоносной translator.dll. При следующем запуске Translator.exe загрузчик находит translator.dll первым же подходящим местом — в директории приложения (C:\Program Files\Translator\) и загружает вредоносную версию. Вредоносный код выполнится с правами легитимного процесса. Это — DLL Hijacking (Substitution)

Согласно определению Лаборатории Касперского: 

«Подмена DLL — атака, основанная на замене легитимного DLL-файла на вредоносную библиотеку. Доставка стороннего компонента может производиться как при помощи специального загрузчика, внедряемого в систему, так и через пользовательские файлы, обрабатываемые использующей библиотеку программой. Результатом подмены DLL является выполнение стороннего кода в среде скомпрометированного приложения.» 

Сценарий 2. Теперь немного модифицируем условия задачи. Если приложение запросит динамическую библиотеку, находящуюся в системной директории, но НЕ находящуюся в списке KnownDLLs, то, согласно порядку поиска, загрузчик сперва проверит папку приложения. Если атакующий поместит туда одноименную динамическую библиотеку с вредоносной нагрузкой, то она будет загружена вместо легитимной библиотеки, поскольку директория приложения имеет приоритет на системными в поиске. Это — DLL Search Order Hijacking.

Определение

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

Сценарий 3. На целевую машину любым доступным образом доставляют легитимное приложение (например, VLC.exe) и вредоносную libvlc.dll. При запуске VLC.exe загружается поддельная DLL из той же директории. Само по себе приложение vlc.exe абсолютно легитимно, имеет все необходимые цифровые подписи и даже, вполне возможно, выполняет свою функцию по проигрыванию видео. Вся вредоносная нагрузка содержится в библиотеке libvlc.dll и выполняется в контексте процесса vlc.exe, маскируя вредоносную активность. Это усложняет обнаружение для команд SOC, а попытки сдерживания угрозы могут приводить к забавным казусам. Я был свидетелем такой атаки, и в качестве меры противодействия было принято решение временно запретить запуск vlc.exe по хэш-сумме, так как активность фиксировалась от этого процесса. Это привело к массовым жалобам со стороны пользователей на неработоспособность экземпляров медиаплееров, по большей части тех, которые не содержали вредоносной библиотеки. Это — боковая загрузка DLL (DLL Side-loading) .

Определение

«DLL sideloading — атака на устройства под управлением Windows, при которой злоумышленники распространяют вредоносную библиотеку DLL вместе с легитимным приложением, которое ее выполняет. Некоторые легальные программы не проверяют библиотеки, которые подгружаются в их адресное пространство. Поэтому злоумышленники могут подложить вместо штатной библиотеки вредоносную с таким же наименованием, и легитимное приложение ее загрузит.»   

Сценарий 4. Предположим, на предприятии используется специализированное ПО для анализа данных – DataViewer.exe. Приложение используется редко и не проверяется на работоспособность, но иногда все-таки требуется для работы. Приложение зависит от сторонней библиотеки compress_engine.dll, которая не входит в системный список KnownDLLs, отсутствует в папке приложения (C:\Program Files\DataViewer\) из-за ошибки обновления (такое иногда случается даже при штатном обновлении), и загружается вызовом LoadLibrary("compress_engine.dll") без указания абсолютного пути. Когда приложение запустится, Windows проверит:

  • KnownDLLs (безуспешно, так как библиотека не системная);

  • Папку приложения (Program Files\DataViewer);

  • System32, SysWOW64 и путях%PATH%;

  • И дойдет до CWD.

Атакующий может действовать по следующей схеме: прислать жертве фишинговое письмо, содержащее архив с файлом data.dv, якобы предназначенное для просмотра с помощью DataViewer. В архиве, помимо файла.dv, содержится динамическая библиотека compress_engine.dll с вредоносной нагрузкой. После двойного щелчка по файлу директория присланного атакующим распакованного архива становится текущей рабочей директорией. Когда очередь проверки дойдет до нее, вызов LoadLibrary загрузит вредоносную dll, что повлечет за собой выполнение вредоносного кода в контексте приложения. Это — Phantom DLL Hijacking.

Как предотвратить?

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

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

  • Приложение должно содержать в таблице импорта и вызовах LoadLibrary не просто названия требуемых библиотек, а полные пути их расположения. В этом случае поиск динамической библиотеки по указанному выше порядку поиска производиться не будет, и это существенно повышает безопасность. Как альтернативу можно использовать вызов LoadLibraryEx() с флагом LOAD_LIBRARY_SEARCH_APPLICATION_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32 или иные флаги LOAD_LIBRARY_SEARCH, явно задающие директорию поиска библиотеки. 

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

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

  • Если архитектура приложения была изменена таким образом, что определенный dll-модуль больше не требуется для его работы, вызовы этого модуля также должны быть удалены, чтобы предотвратить атаки Phantom DLL Hijacking. Если модуль системный, от разработчиков требуется удалить только вызовы, если dll-модуль относится к приложению, то он также должен быть удален. 

Для Security инженеров, помимо традиционной рекомендации по проведению своевременного патч-менеджмента, можно порекомендовать следующее:

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

  • Используйте AppLocker, WDAC или схожие решения (например, Контроль приложений в KES) для контроля загрузки приложений и динамических библиотек. Учтите, что это может серьезно влиять на производительность. 

  • Запретите изменения переменной среды PATH для непривилегированных пользователей.

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

Как обнаружить?

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

— Раскатайте по хостам Sysmon и настройте отслеживание событий с ID 7. Это событие загрузки модуля в процесс. Вот пример такого события, взятый отсюда

Image loaded:
UtcTime: 2017-04-28 22:45:16.662
ProcessGuid: {a23eae89-c5fa-5903-0000-0010bf439000}
ProcessId: 12536
Image: C:\Windows\System32\notepad.exe
ImageLoaded: C:\Windows\System32\ole32.dll
Hashes: SHA1=B2A2BBCFB69B1F0982C4B82055DAD9BAE4384E4B
Signed: true
Signature: Microsoft Windows
SignatureStatus: Valid

Как видите, Sysmon предоставляет исключительно информативный ивент, содержащий таймкод, полные пути до файлов, хэш и сведения о подписи. Достойной, а обычно более предпочтительный источник информации — события «Module loaded» в EDR.

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

Пример несовпадения файла и подписи — процесс, маскирующийся под MSTeams, использует подозрительную подпись неизвестного издателя. Картинка с сайта redcanary.com.
Пример несовпадения файла и подписи — процесс, маскирующийся под MSTeams, использует подозрительную подпись неизвестного издателя. Картинка с сайта redcanary.com.

— В случае выявления подозрительного поведения от легитимного процесса изучите, какие dll‑библиотеки были в него загружены. Особое внимание уделяйте библиотекам, загружающимся из CWD,%PATH% и папки приложения. Это можно сделать как с помощью EDR, или с помощью Process Explorer из Sysinternals.

— Не стесняйтесь связаться с пользователем и администратором чтобы спросить, когда и для чего то или иное легитимное приложение было установлено на его компьютере. Если о происхождении файла ничего не известно, и зафиксирована вредоносная активность от его имени, вполне вероятно, мы имеем дело с DLL Side-loading, и легитимный файл был загружен атакующими для затруднения обнаружения. Косвенным признаком, указывающим на подобную процедуру, будет являться нетипичное расположение легитимного файла. Например, установочной директорией по умолчанию для видеоплеера VLC является C:\Program Files\VideoLAN\VLC. Описание реального случая подобной атаки, приведенный в отчете «Азиатские APT-группировки: тактики, техники и процедуры» Лаборатории Касперского, фигурирует путь C:\Program Files\Common Files\VLCMedia\vlc.exe, что хорошо иллюстрирует тезис. 

Заключение

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

Комментарии (0)