• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Взлом Ham Radio: WinAPRS - часть 4

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
В четвертой части этой серии мы рассмотрим трехэтапную полезную нагрузку шеллкода, которая преодолевает проблемы, возникающие при попытке вызова Win32 API.

Ключевые моменты

1. Мы шаг за шагом рассмотрим шеллкод сборки
2. Мы объясним назначение каждого бита кода
3. Мы откроем интерактивную командную строку обратной оболочки по радиосвязи, используя TNC машины-жертвы.

В третьей части этой серии мы обнаружили и отследили ошибку повреждения памяти в WinAPRS с помощью IDA Pro и WinDbg. Мы обнаружили, что его можно использовать для получения контроля над регистром EIP процессора для удаленного выполнения кода. Мы обнаружили, что существуют ограничения на адрес, который может быть помещен в реестр EIP, и поэтому выбрали Windows XP, чтобы убедиться, что мы можем создать работающее доказательство концепции и применить на практике наши недавно полученные навыки разработки эксплойтов. В этом выпуске будет рассмотрена полезная нагрузка трехэтапного шеллкода, которая преодолевает больше ограничений из-за поврежденного стека, чтобы в конечном итоге порождать обратную оболочку по радиолюбителям.

Шеллкод

Уязвимость переполнения буфера перезаписывает большой объем памяти стека. Это привело к тому, что важные указатели были перезаписаны мусором. В результате я не мог вызывать многие API-интерфейсы Win32, такие как CreateProcessA, ShellExecuteA и многие другие. Вызовы этих API приводили к ошибкам кучи, которые приводили к сбою и сбою шеллкода. Я не смог найти ни одного пригодного для использования Win32 API, который мог бы выполнять команды оболочки. Создание функционирующей полезной нагрузки было длительным процессом тестирования различных API Win32, чтобы увидеть, какие из них работают, а затем определения комбинации, которая привела бы к желаемому результату: обратному шеллу через любительское радио. Он также должен был уместиться в 783 байта, которые были доступны в моем эксплойт-пакете.

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

Шеллкод первого этапа

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

1653898610500.png


Строки 38–108 — это функции, которые используются для поиска указателей Win32 API в памяти для последующего использования в шелл-коде. Это то, чему я научился, пройдя новый курс OSED от Offensive Security, который я настоятельно рекомендую, если вы новичок в подобных вещах и хотите узнать об этом больше. Я не буду рассматривать здесь весь код, но просто знайте, что вы можете жестко закодировать хеш-значение, представляющее метод Win32 API, и эти функции найдут адрес метода, чтобы ваш шелл-код мог использовать его позже.

Следующий раздел шеллкода девять раз вызывает функцию поиска для разрешения адресов девяти различных методов Win32 API, таких как OpenProcess и VirtualAllocEx. Все они используются позже в шелл-коде для внедрения этапа 2 в отдельный чистый процесс.

1653898645300.png


Следующий блок кода вызывает функцию Win32 CloseHandle, чтобы закрыть дескриптор файла на COM-порту. Это освобождает его для использования на втором этапе позже. В Windows XP WinAPRS, кажется, всегда имеет дескриптор COM-порта, доступного по определенному адресу памяти, который не перезаписывается при переполнении. Это означает, что шелл-код может каждый раз вытягивать дескриптор из этого места, чтобы закрыть COM-порт.

1653898672600.png


Шелл-код второго этапа необходимо внедрить в отдельный чистый процесс. Один процесс, который должен существовать всегда, — это explorer.exe. Поэтому на первом этапе необходим способ определения идентификатора процесса explorer.exe. Следующий блок кода делает именно это, используя три Win32 API.

Во-первых, он вызывает CreateToolhelp32Snapshot, чтобы сделать снимок всех запущенных процессов. Затем он вызывает Process32First для чтения первой записи в результирующем списке. Эта запись не будет explorer.exe, поэтому она будет проигнорирована. Затем шелл-код несколько раз вызывает Process32Next, пока не найдет процесс с именем «explorer.exe», а дальше,он считывает PID из возвращаемого значения и сохраняет.

1653898700700.png


Теперь, когда известен PID explorer.exe, шеллкод может получить дескриптор процесса. Это достигается с помощью Win32 OpenProcess API. PID используется в качестве аргумента функции. Полученный дескриптор сохраняется в стеке на потом.

1653898725900.png


Затем шелл-код использует Win32 VirtualAllocEx API для выделения новой порции доступной для записи и исполняемой памяти в удаленном процессе explorer.exe. VirtualAllocEx возвращает адрес памяти нового фрагмента, который сохраняется в стеке на будущее.

1653898791400.png


После выделения новой памяти в explorer.exe шелл-код вызывает API WriteProcessMemory, чтобы скопировать шелл-код второго этапа на новый адрес. Хотя он сначала перезаписывает несколько DWORD на втором этапе, из-за ограниченного пространства, доступного в первом пакете эксплойта, недостаточно места для включения функций поиска Win32 API как на первом, так и на втором этапе. Следовательно, второму этапу нужно будет каким-то другим способом узнать адреса требуемых функций Win32.

WinAPRS использует множество API-интерфейсов Win32. Адреса этих используемых API помещаются в таблицу импорта WinAPRS. Адреса таблиц импорта WinAPRS никогда не меняются между перезагрузками или даже между версиями ОС. Таким образом, даже если адреса Win32 API изменяются, их всегда можно найти по одному и тому же адресу в таблице импорта WinAPRS. В приведенном ниже примере адрес CreateFileA будет храниться в ячейке памяти 0x00760570.

1653898814700.png


Шелл-код первого этапа копирует адреса из таблицы импорта WinAPRS и записывает их в шелл-код второго этапа в определенных местах. Затем шелл-код второго этапа может использовать их позже без необходимости их поиска. Это экономит место, так как не нужно копировать множество строк кода функции поиска на втором этапе. Это станет ясно, когда мы рассмотрим шеллкод второго этапа.

1653898827300.png


Как только шелл-код второго этапа скопирован в память процесса explorer.exe, нам нужно его выполнить. Шеллкод первого этапа использует Win32 API CreateRemoteThread для выполнения этой задачи. Он передает дескриптор процесса explorer.exe и адрес памяти и сообщает процессу создать оттуда новый поток. В этот момент шелл-код второго этапа должен начать выполняться внутри процесса explorer.exe с чистой памятью.

1653898848700.png


Наконец, шеллкод первого этапа вызывает Win32 API TerminateProcess. Это изящно закрывает WinAPRS, а не вызывает сбой, что может быть более подозрительным.

1653898868400.png


Шеллкод второго этапа

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

Первое, что происходит, это корректировка стека, чтобы освободить место для переменных. Затем шесть DWORD перемещаются в известные места в стеке. Эти DWORD начинаются с 0xffffffff, но перезаписываются шеллкодом первого этапа с адресами памяти для различных функций Win32 API. Таким образом, остальная часть второго этапа может использовать эти API, не тратя много места на функции поиска.

1653898878100.png


Затем шелл-коду нужен способ получить шелл-код третьего этапа. Поскольку все это происходит через любительское радио, мы не можем предположить, что у машины-жертвы есть подключение к Интернету. Мы хотим отправить третий этап по радио. Это означает, что шелл-код должен открыть COM-порт, хотя мы не знаем, какой COM-порт правильный. Следующий фрагмент шелл-кода пытается открыть COM-порт с помощью Win32 CreateFileA API. Он начинается с COM1. Если это не удается, он пробует COM2 и так далее. Если он выйдет из строя после COM9, он просто продолжит работу в бесконечном цикле. Надеемся, что жертва TNC подключена к самому низкому доступному COM-порту.

1653898895800.png


Затем нам нужно убедиться, что COM-порт настроен на правильную скорость передачи данных. Это будет зависеть от каждой машины-жертвы, но шеллкод жестко запрограммирован для настройки COM-порта на 9600 бод, что и использует мой TNC. Теоретически этот шаг не должен быть необходим. COM-порт должен поддерживать любые настройки WinAPRS. На практике я обнаружил, что иногда операции COM-порта не всегда работали, пока я не выполнил этот шаг, поэтому он включен для надежности. Вызывается Win32 GetCommState API, который возвращает структуру, содержащую текущую конфигурацию COM-порта. Параметр DWORD, содержащий настройку скорости передачи данных, перезаписывается. Затем вызывается SetCommState для обновления конфигурации правильными параметрами.

1653898912100.png


Теперь, когда COM-порт настроен правильно, шелл-код использует Win32 ReadFile API для чтения по одному байту из COM-порта. Он продолжает чтение, пока не обнаружит два символа 0xC0. Это управляющие символы KISS, обозначающие начало и конец пакета KISS. Обнаружив два символа 0xC0, шелл-код знает, что он получил весь пакет.

1653898924700.png


Затем шелл-код закрывает дескриптор COM-порта, чтобы освободить COM-порт для третьего этапа.

1653898936700.png


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

1653898946900.png


Шеллкод третьего этапа

Третий этап полезной нагрузки содержит реальный обратный шелл-код. Этот код слишком длинный для второго этапа, поэтому второй этап просто читает третий этап и переходит к нему. Со всем дополнительным пространством третий этап начинается с тех же функций поиска Win32, что и первый этап. Затем он выполняет собственный поиск API-интерфейсов Win32, которые он будет использовать позже. Затем он создает именованные каналы, которые позже будут использоваться с cmd.exe для чтения вывода с терминала и записи в него пользовательского ввода. Это делается с помощью API CreatePipe.

1653898959400.png


Затем шелл-код должен вызвать API CreateProcessA, хотя одним из аргументов этой функции является структура STARTUPINFOA. Поэтому шелл-код строит эту структуру в стеке, который включает в себя дескрипторы двух только что созданных каналов. Этот вызов API запускает новый процесс cmd.exe и отправляет ввод и вывод в созданные каналы.

1653898984700.png


Далее два ненужных дескриптора закрываются с помощью Win32 CloseHandle API.

1653899003500.png


Шелл-коду нужно место для хранения ввода и вывода, поэтому для выделения некоторой памяти вызывается VirtualAlloc.

1653899015900.png


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

1653899037500.png


Далее происходит основной цикл удаленной оболочки. Шелл-код вызывает Win32 Sleep API для ожидания в течение одной секунды завершения команды оболочки. Затем он считывает данные из выходного канала cmd.exe в буфер.

1653899063300.png


Управляющие символы KISS добавляются в буфер, и последний пакет KISS записывается в последовательный порт с помощью API WriteFile. На этом этапе выходные данные cmd.exe должны быть переданы по радиоволнам обратно злоумышленнику.

1653899081100.png


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

1653899100500.png


Затем шелл-код добавляет к командам злоумышленника символы возврата каретки и записывает их во входной канал процесса cmd.exe. Это позволяет злоумышленнику выполнять команды оболочки, как если бы он сидел перед компьютером (хотя и с некоторыми ограничениями).

1653899116800.png


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

Следующие шаги

На данный момент у нас есть эксплуатируемая уязвимость повреждения памяти. У нас есть уязвимая целевая система под управлением Windows XP SP3. У нас есть шелл-код, который, как мы надеемся, создаст обратный шелл по любительскому радио, используя TNC машины-жертвы. Последним шагом является включение шелл-кода в окончательный сценарий эксплойта для запуска на нашей атакующей машине. В следующем выпуске мы сделаем именно это.

Перевод этой статьи
Like
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх