Реверсирование с помощью IDA Pro и внедрение шелл-кода с помощью Python

Эй, ребята! По мере того, как я все больше и больше вовлекаюсь в разработку эксплойтов, я практикуюсь в различных уязвимых по умолчанию программах, одним из которых является VulnServer.

Короче говоря, VulnServer - это крошечный сервер, который предоставляет набор команд с несколькими ошибками, готовыми к эксплуатации.

Одна из его уязвимостей заключается в логике команды TRUN, и это именно то, на что я буду нацеливаться в этом сообщении в блоге.

Поскольку это программное обеспечение Windows x86, среда, над которой я буду работать для этой эксплуатации, - это Windows 10 с IDA Pro, отладчиком WinDbg и Python 2 для создания эксплойта.

Я постараюсь объяснить по ходу дела, так что не стесняйтесь следовать за мной.

Реверсивный

Хотя у меня есть полный исходный код сервера, я изменю логику команды TRUN, потому что это веселее! (И мы узнаем больше .. верно?) Единственное, что у меня есть здесь, это то, что в команде TRUN есть уязвимость. Тип и местонахождение уязвимости неизвестны!

Открыв vulnserver.exe в IDA, я могу быстро отследить создание сокетов и обработку соединений. Тем не менее, все это сейчас не имеет значения, поэтому я просматриваю строки двоичного файла, чтобы найти «TRUN».

Следуя инструкциям, нажмите ENTER, а затем Ctrl + X, чтобы отобразить все ссылки на строку.

Теперь снова нажмите ENTER, чтобы перейти к этой единственной ссылке в коде.

Судя по дизассемблированному коду, похоже, что программа проверяет буфер (возможно, вход), если он равен TRUN, вызывая _strncmp. Для удобства IDA сама выполняет интеллектуальное именование параметров для вызова _strncmp.

Но давайте поставим точку останова в начале блока кода, нажав F2, и проведем отладку кода, чтобы проверить, что на самом деле происходит с тестовыми значениями. Убедитесь, что WinDbg установлен и выбран в качестве отладчика в IDA, и нажмите «Выполнить».

Сервер запущен и теперь нам нужно что-то для отправки данных в сокет. Поскольку в ОС Windows по умолчанию netcat не установлен, загрузите его отсюда, распакуйте zip (пароль: nc) и скопируйте его в папку System32.

Используйте netcat для отправки на сервер команды TRUN с некоторыми тестовыми данными. (Порт по умолчанию - 9999)

Обходя первые несколько строк с помощью F8, мы видим, что программа действительно проверяет, равны ли первые 5 символов ввода TRUN. Если это так, он выделяет в куче 3000 байтов и устанавливает их в 0.

Затем есть интересная строка, в которой проверяется, является ли первый символ оставшегося ввода 2E, который при преобразовании в ASCII является символом «.». В то время как «.» символ не найден, программа перебирает буфер и завершает работу, если не найден. Давайте снова отладим, но теперь с вводом, который начинается с «.».

Теперь первая проверка проходит, и программа копирует ввод в выделенную ранее кучу. Если немного продвинуться дальше, вызывается интересная функция с именем «_Function3». Используя F7 для входа в функцию, мы видим, что функция копирует содержимое кучи в буфер в стеке, НО нет никаких проверок границ буфера, поэтому теоретически должна быть возможность перезаписать буфер и таким образом обратный адрес! (Обратите внимание, что выделенное пространство в куче составляло 3000 байтов, а весь буфер стека в Function3 - 2024 (7E8h) байта.)

Эксплуатация

Поскольку мы зашли так далеко и полностью изменили программу, давайте проявим еще смелость и выполним переполнение без фаззинга, а вместо этого найдем смещение, вычислив его. Учитывая, что адрес назначения, переданный в _strcpy, равен $ EBP-7D8h (7D8 - это 2008 байтов), нам нужно 2012 (+4 байта, чтобы заполнить адрес, на который в настоящее время указывает адресат) байтов, чтобы заполнить стек. до EBP, а затем следующее слово (4 байта) является адресом возврата!

Давай попробуем! Используйте следующий код Python для отправки полезной нагрузки на сервер:

Вернемся к IDA, это точно! RIP был перезаписан на 0x42424242!

Теперь следующая задача - создать некий шелл-код, вставить его в стек как часть нашей полезной нагрузки и перейти к нему. Однако мы не можем напрямую жестко закодировать адрес стека в EIP, так как каждый раз он будет отличаться. Таким образом, необходимо найти надежный гаджет, позволяющий перейти к шеллкоду. Инструкция «jmp esp» - это именно то, что нам нужно, поскольку она указывает сразу после перезаписанного адреса возврата.

IDA можно использовать для поиска инструкций таким же образом, как и для поиска текста. нажмите Alt + T и введите «jmp * esp». К сожалению, vulnserver.exe не имеет такой инструкции, но как насчет библиотеки (esfunc.dll), которая идет с ним? Действительно, в esfunc.dll есть инструкция «jmp esp» по адресу 0x625011AF.

Наконец, давайте создадим шелл-код, который покажет калькулятор (такой классический!), Который будет использоваться в качестве доказательства концепции. Поскольку для этого мы будем использовать msfvenom, убедитесь, что у вас установлен фреймворк metasploit. Выполните следующую команду, чтобы получить шелл-код.

msfvenom -p windows/exec CMD='calc.exe' exitfunc=thread -a x86 -f python -b '\x00'

Обратите внимание на параметр -b, который устанавливает недопустимые символы. Учитывая, что входной буфер копируется с использованием strcpy и strncpy даже до этого, мы не хотим, чтобы наш шелл-код содержал какие-либо нулевые байты, поскольку они прервут копирование на полпути.

Наконец, с этим у нас есть все необходимое для создания нашего доказательства концепции эксплойта.

Хотя возврат к «jmp esp» перескакивает прямо на наш шеллкод, он не сработает, если вы не добавите достаточно большие салазки NOP. Например, 8-байтовые салазки NOP не подойдут. Это связано с тем, что шелл-код нуждается в некотором заполнении для кодирования, и поэтому вам нужно предоставить ему некоторое пространство с помощью NOP.

Пришло время шоу! Запустите сервер, а затем эксплойт python.

Бум! Выскочил наш вредоносный калькулятор! 😈

До следующего раза, ребята.