Самостоятельное распространение программ‑вымогателей в локальной сети в качестве сетевого червя давно уже не является чем‑то уникальным и новым. Программа‑вымогатель WannaCry в 2017 году распространялась в локальной сети, используя уязвимость SMBv1 MS17–010. Для этого создатели WannaCry использовали эксплойт EternalBlue и бэкдор DoublePulsar из публикации хакерской группировки The Shadow Brokers «Lost in Translation». Но стоит отметить, это всё же немного другая история, отличная от современных RaaS.
В настоящее время уязвимости такого плана и масштаба в арсенале вымогателей появляются не часто, но всегда есть возможность распространения с помощью доступных легитимных механизмов. Для реализации таких способов необходимо наличие корректных учетных данных, которые партнеры RaaS в большинстве случаев и так похищают на этапе постэксплуатации. Программы‑вымогатели LockBit используют групповые политики для своего распространения в локальной сети, а в образцах BlackCat (APLHV) для самораспространения используется содержащаяся в них в зашифрованном и сжатом виде утилита Sysinternals PsExec.
По‑видимому, авторы LockBit для совершенствования своего продукта, а также чтобы не отставать от конкурента, добавили опцию распространения “psexec_netspread”. Но сразу оговоримся, сама утилита PsExec в данном случае не применяется. Интересное мнение о неработоспособности данной функции высказали коллеги. Давайте разберемся в новом механизме распространения в локальной сети, реализованном в LockBit 3.0.
В сентябре 2022 года в публичный доступ «утек» билдер LockBit 3.0, используемый RaaS LockBit. Конечно, эта утечка неплохо помогла ресерчерам в их исследовании LockBit 3.0, но при этом и нельзя умолчать о громадном вреде, которые несут такие общедоступные публикации. По сути, это «оружие», которое получают все без разбора киберхулиганы и хактивисты различной политической окраски. Для атак на российские компании были использованы программы‑вымогатели, созданные в том числе и с помощью данного билдера.
Для анализа сэмплов LockBit 3.0 использовались разработанные скрипты, позволяющие значительно облегчить их анализ. Также среди этих скриптов вы можете найти и реализацию криптографических алгоритмов шифрования файлов в LockBit на Python.
Этап 1. Проверка указанных учетных данных
Распространение в локальной сети осуществляется при включенной в конфигурации LockBit опции «psexec_netspread» при запуске программы‑вымогателя в режиме полного шифрования системы или шифрования в безопасном режиме (safe mode). Но при этом, как видим (рис. 2), для самораспространения необходим еще и некоторый токен пользователя (g_hUserToken
).
Токен пользователя (g_hUserToken
) программа‑вымогатель получает ранее в функции, которую мы назвали Prepare
, при этом, разумеется, для этого должна быть включена опция конфигурации имперсонификации “impersonation” (рис. 3), а также указаны корректные администраторские учетные данные в поле “impers_accounts”.
В функции LogonAccounts
(рис. 4) определяется имя домена, а далее осуществляется последовательный перебор учетных записей, указанных в поле конфигурации “impers_accounts” и попытка аутентификации для каждой записи с помощью функции API LogonUserW
. В случае успешной аутентификации перебор учетных записей прекращается, из функции возвращается токен пользователя, а использованные для входа имя домена, имя пользователя и пароль шифруются и сохраняются в глобальные переменные g_pEncDomain
, g_pEncUser
и g_pEncPassword
соответственно. Впоследствии эти данные, как это показано на рис. 2, используются в качестве параметров функции RunNetSpread
.
Функции с именами IsRunningAsSvc
и RunAsSvc
рассмотрим позже, они будут использоваться на последнем этапе.
Для взаимодействия между процессами LockBit в функции RunNetSpread
создается канал IPC (Inter‑Process Communication) в виде именованного пайпа (PIPE1
) (рис. 5)
\\.\pipe\<PIPE1_NAME>
где PIPE1_NAME
— UUID‑подобная строка {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}
, полученная с помощью хеша MD5 от Unicode‑строки <DECRYPTION_ID>_IPC$
, где DECRYPTION_ID
— строка с первыми 8 байтами в шестнадцатеричном представлении модуля (n) публичного ключа RSA‑1024.
Пример:
DECRYPTION_ID: 4D30CBF764117970
PIPE1_NAME: {1B7F1036-41FE-3F89-169A-86DB06D0C609}
Далее программа‑вымогатель запускает себя в контексте безопасности учетной записи, полученной в LogonAccounts
, при запуске также добавляет параметр командной строки "‑psex". Если текущая учетная запись — LocalSystem, для запуска нового процесса программы используется функция API CreateProcessAsUserW(g_hUserToken, …)
, в ином случае — CreateProcessWithLogonW
с именем пользователя, именем домена и паролем, расшифрованных из g_pEncDomain
, g_pEncUser
, g_pEncPassword
.
После этого в канал IPC PIPE1
записываются строка с аргументами командной строки, использованными при первоначальном запуске программы‑вымогателя (рис. 6). Затем программа‑вымогатель продолжает обычное функционирование, в том числе и шифрование данных на хосте.
Функция RunNetSpread
возвращает дескриптор запущенного процесса, и вызывающая функция ожидает его завершения (рис. 2).
При самораспространении же с помощью групповых политик программа‑вымогатель запускается с параметром “‑gspd”, в остальном же все действия программы‑вымогателя идентичны на данном этапе.
Этап 2. Выполнение программы-вымогателя с аргументом "-psex"
На предыдущем этапе программа-вымогатель была запущена в контексте безопасности учетной записи, указанной в конфигурации, с добавлением аргумента командной строки "‑psex". Проверка аргументов командной строки в LockBit 3.0 осуществляется по контрольным суммам так же, как и в BlackMatter, впрочем, и как многое другое (рис. 7-8).
В функции PsexHandler
программа открывает ранее созданный основным процессом LockBit канал IPC \\.\pipe\<PIPE1_NAME>
, из которого получает строку с первоначальными аргументами командной строки, и затем в случае успеха вызывает функцию NetSpread
(рис. 9).
В функции NetSpread
(рис. 10) создается еще один канал IPC PIPE2
(функция CreateAndReadPipe2
)
\\.\pipe\<PIPE2_NAME>
где PIPE2_NAME
– UUID-подобная строка, полученная с помощью хеша MD5 от Unicode-строки в нижнем регистре
\\<COMP_NAME>.<DOMAIN_NAME>\_IPC$
COMP_NAME
– имя компьютера, DOMAIN_NAME
– имя домена.
Стоит отметить, что для приведения строки в нижний регистр в программе используется функция _wcslwr
, которая не учитывает локальных языковых настроек.
Пример:
DOMAIN_NAME: rootdomain.net
COMP_NAME: MYDC1
PIPE2_NAME: {ABCA3D07-9E6B-A1F0-443E-768D0BF31AE5}
COMP_NAME: MYPC1
PIPE2_NAME: {8A2A1C43-2D7E-ACCC-BD0E-98F7911AE5B7}
Дескриптор именованного канала PIPE2
дублируется с помощью NtDuplicateObject
для уже существующего процесса explorer.exe или lsass.exe, в результате чего будет закрыт впоследствии данным процессом.
Программа получает список компьютеров домена (функция GetADCompList
).
Получение списка компьютеров в GetADCompList
осуществляется двумя способами. Первый способ — перечисление контроллеров домена с помощью функций API DsGetDcOpenW
/ DsGetDcNextW
. Второй способ — перечисление собственно компьютеров Active Directory с помощью запросов LDAP, для перечисления используются функции API ADsOpenObject
, ADsBuildEnumerator
, ADsEnumerateNext
и интерфейсы IADs
, IADsContainer
.
Далее для каждого хоста из указанного списка создается поток (рис. 11), но одновременно может выполняться не более 64 потока. В функцию потока передается имя хоста в виде
\\<COMP_NAME>.<DOMAIN_NAME>\
где COMP_NAME
– имя компьютера, DOMAIN_NAME
– имя домена.
В функции NetConnAddOrCancel
с помощью функции API WNetAddConnection2W
осуществляется подключение к административным сетевым ресурсам компьютера (Admin Shares) ADMIN$
и IPC$
(рис. 12). Подключение к ресурсу IPC$
позволяет осуществлять удаленное взаимодействие между процессами LockBit.
Программа проверяет наличие канала IPC PIPE2 на хосте
\\<COMP_NAME>.<DOMAIN_NAME>\pipe\<PIPE2_NAME>
Если именованный пайп PIPE2
на хосте уже существует, дальнейших действий по распространению для данного компьютера не осуществляется.
Имя PIPE2_NAME
используется также в качестве имени службы, создаваемой удаленно на компьютере, и имени копируемого на этот компьютер файла.
Программа-вымогатель создает на другом компьютере каталог
\\<COMP_NAME>.<DOMAIN_NAME>\ADMIN$\Temp
и копирует в него свой исполняемый файл под именем <PIPE2_NAME>.exe
(рис. 13).
На компьютере удаленно создается и запускается служба с именем PIPE2_NAME
, для которой используется скопированный на компьютер исполняемый файл программы-вымогателя со следующими аргументами командной строки (рис. 14):
%%SystemRoot%%\Temp\<PIPE2_NAME>.exe -k LocalServiceNetworkRestricted
После успешного запуска в канал IPC PIPE2
записываются ранее полученные через канал IPC PIPE1
данные – строка с первоначальными аргументами командной строки. Далее служба удаляется и происходит отключение от административных сетевых ресурсов компьютера (Admin Shares) ADMIN$
и IPC$
(рис. 15).
Этап 3. Выполнение службы на компьютере
Ранее на 1 этапе мы упоминали функции с именами IsRunningAsSvc
и RunAsSvc
, которые вызывались в Prepare
(рис. 3). В функции IsRunningAsSvc
осуществляется проверка наличия аргумента командной строки "LocalServiceNetworkRestricted" с помощью контрольной суммы строки (рис. 16). Эта проверка осуществляется заранее, до проверки аргументов в функции Payload
(рис. 7).
Таким образом, если программа-вымогатель запущена с аргументом командной строки "LocalServiceNetworkRestricted", запускается функция RunAsSvc
, которая осуществляет запуск программы как системной службы (рис. 17).
В функции SvcMain
программа создает IPC канал с именем PIPE2_NAME
и читает из него данные в буфер (рис. 18). После этого служба завершает свою работу.
Далее после завершения службы выполняется следующий за StartServiceCtrlDispatcherW
код, а именно запускается функция RunMe
, которой в качестве параметра передаются данные, полученные из IPC канала PIPE2
. Функция RunMe
запускает исполняемый файл программы-вымогателя с аргументами командной строки, переданными первоначально программе на 1-м этапе. RunMe
определяет идентификатор сеанса физической консоли (GetActiveConsoleSessionID
) и устанавливает его для токена текущего процесса (процесса службы) (NtSetInformationToken
) и запускает процесс шифрования компьютера в контексте безопасности текущего процесса (CreateProcessAsUserW
).
Таким образом, запуск программы-вымогателя на одном из компьютеров домена при наличии корректных учетных данных администратора домена в конфигурационных данных программы-вымогателя приводит к шифрованию компьютеров (рис. 19) и контроллера домена (рис. 20).
Как видим, все работает...