Недавно я пытался проэксплуатировать уязвимость (CVE-2008-0532, http://www.securitylab.ru/vulnerability/348518.php, от FX) и у меня возникли проблемы с отладкой исполняемого CGI файла в месте расположения уязвимой функции
Недавно я пытался проэксплуатировать уязвимость (CVE-2008-0532, http://www.securitylab.ru/vulnerability/348518.php , от FX) и у меня возникли проблемы с отладкой исполняемого CGI файла в месте расположения уязвимой функции.
Вот в чем заключалась проблема: исполняемый CGI файл CSUserCGI.exe является дочерним процессом IIS, и может возникнуть только при вызове пользователя. Исполняемый сценарий, выполнив свою задачу, быстро закрывается… и мы не успеваем присоединиться отладчиком. И как же нам осуществить отладку? Поможет ли нам JIT_отладка (Just In Time)?
При вызове CGI сценария через HTTP, открытие и закрытие осуществляется очень быстро.
Пробуем:
Не вышло! Как же мне осуществить отладку? Я был уверен, что способ существовать должен.
В то время sinn3r, один из участников нашей группы Corelan, закончил работу с HP NNM модулями, в ходе которой он столкнулся с несколькими схожими проблемами. Идея заключалась в том, чтобы поместить дочерний процесс в спящий режим и выиграть время для присоединения отладчика. Для этого был использован следующий код:
// (pseudo code):
>while (IsDebuggerPresent == false) {
sleep(1);
}
// Repair the prologue of the entry point you hijacked,
Я открыл выполняемый файл в Immunity и выбрал подходящую для перехвата функцию, (0×00401010). Мне хотелось пропустить часть изначальных вызовов ядра и среды, переместившись сразу к main().
Далее мне требовалось найти область в .text, которая не использовалась исполняемым файлом, и не повредила бы любую другую функцию, а также не помешала бы работе исполняемого файла. Моя цель заключалась в том, чтобы использовать существующую инструкцию вызова (в данном случае вызова 0×0040101) и изменить значение смещения вызова, для того, чтобы осуществить переход в область, в которой будет размещена моя процедура.
Я обнаружил отличное место в 0×00415362, я отредактирую вызов в 0x00414CD6 таким образом, чтобы он указывал на эту область.
Сейчас мне необходимо найти области kernel32.Sleep и kernel32.IsDebuggerPresent. Поскольку это всего лишь патч на локальном компьютере, мне не требуется осуществлять поиск общих функций, и можно просто найти эти области в Immunity(Executable->Names). На моей системе Windows 2003 SP2 необходимыми адресами являются 0x77e424de и 0x77e5da00. На вашей системе они скорее всего будут другими.
Теперь осуществим перехват функции. Отметим, что для данного отдельно взятого эксплоита мне не нужно было перемещаться назад к следующей инструкции после исходного (уже обновленного) вызова, но я все равно сделал это.
Вначале я изменил смещение вызова (чтобы осуществить переход к моему коду, который будет размещен по адресу 0×00415362).
00414CD6 E8 87060000 CALL CSUserCG.00415362 ; jmp to custom routine
Инструкцию я разместил в 0×00415362
00415362 6A 01 PUSH 1
00415364 E8 75D1A277 CALL kernel32.Sleep
00415369 E8 9286A477 CALL kernel32.IsDebuggerPresent
0041536E 83F8 01 CMP EAX,1
00415371 ^75 EF JNZ SHORT CSuserCG.00415362
00415373 CC INT3
00415374 83C4 04 ADD ESP,4
00415377 E8 94BCFEFF CALL CSuserCG.00401010 ;go back
0041537C ^E9 5AF9FFFF JMP CSuserCG.00414CDB
В Immunity мы теперь можем нажать правой кнопкой мыши и сохранить изменения под новым именем. Я решил назвать новый файл NEWCSUserCGI.exe и поместить его в наш каталог CGI сценариев (C:\Inetpub\wwwroot\securecgi-bin).
Затем я переименовал исходный исполняемый файл в OLDCSUserCGI.exe и изменил NEWCSUserCGI.exe на исходное название файла CSUserCGI.exe.
На этот раз я запустил CGI сценарий с PoC кодом в URL и вуаля, сценарий продолжает работать!
Теперь пора проверить работоспособность публично доступного эксплоита, который должен вызывать переполнение буфера. В уведомлении FX говорится, что с помощью достаточно длинной строки после Logout+ можно вызвать переполнение буфера и получить контроль над EIP.
Уязвимая функция является подрограммой в функции, к которой мы прикрепились (0×00401010). Вначале мы видим, что создался буфер фиксированного размера 0×60 для аргумента Logout.
Происходит вызов msvcrt.strtok и поиск первой строки, которая оканчивается на «.»
Затем строка копируется в стек и вызывается переполнение буфера.
Код для вызова подобного переполнения буфера в стеке можно найти по адресу:
https://github.com/rapid7/metasploit-framework/blob/unstable/unstable-modules/exploits/untested/cisco_acs_ucp.rb
Я подумал, что у меня появилась отличная возможность создать сценарий, который автоматизировал бы процесс, или разработать его альтернативу. В конце рабочей недели я представил данную концепцию другим участникам своей группы и отправился домой с готовностью посвятить реализации этой идеи свои выходные. Как и следовало ожидать, я совсем забыл, что что у corelanc0d3r (автор mona.py) есть 5000 строк «кунг-фу к Python» для Immunity, так что он выполнил эту задачу раньше, чем я успел добраться домой, и все заслуги за указанный ниже сценарий принадлежат ему.
Данный автоматизированный сценарий без использования вызовов API kernel32 поместит приложение в режим сна и позволит прикрепить отладчик.
Вся работа corelanc0d3r заключалась в том, чтобы воспользоваться pefile, модулем python, который позволяет выполнять чтение и работать с PE (Portable Executable) файлами. (просто чтобы вы знали, данный модуль устанавливается автоматически вместе с BackTrack и Immunity Debugger).
Его сценарий загружает выполняемый файл и получает изначальную точку ввода модуля.
Далее сценарий осуществит поиск области в файле, которая содержит 12 последовательных нулевых байтов (при необходимости их можно заменить, например, на NOPS).
В данной области будет помещен ый код. Этот код очистит EAX, сравнит его с 0, а затем, если значение условия будет равно «true», вернется к выполнению кода. Таким образом мы получим зацикливание, пока не присоединим отладчик. После условного перехода (который обеспечивает бесконечный цикл), размещается вызов изначального положения входной точки. И наконец, происходит обновление точки входа RVA в PE файле, и теперь она указывает на наш код.
Другими словами, при запуске выполняемого файла, он просто зависнет (уйдет в бесконечный цикл). Во время прикрепления отладчика процесс приостановится. Затем необходимо найти область, в которую был вставлен asm код (адрес на самом деле находится сразу после обновленной точки входа), и либо заменить cmp инструкцию на NOP, либо просто заменить cmp eax,0 на cmp eax,1. Если не останавливать процесс, исполняемый файл просто выполнит то, что должен. Также можно приостановить работающее приложение, и оно остановиться на cmp, или на инструкции перехода внутри нашего кода.
Давайте запустим наш сценарий из командной строки и посмотрим, как все автоматически сработает.
После того, как сценарий найдет безопасную для вызова рекурсии область и выполнит запись, он создаст новый файл с именем _patched.exe.
Запустив его, можно будет посмотреть, как он работает и узнать, что он делает то же самое, что мы делали вручную.
Сейчас, если мы переименуем наш измененный файл и еще раз запустим PoC код, нам удастся запустить отладчик и нажать «pause» для выхода из цикла. Затем мы выходим из цикла, после чего будет осуществлен переход к точке входа.
Все работает нормально. В этой небольшой статье я изложил проблемы, с которыми я столкнулся и которые преодолел. Обратите внимание на то, что данная техника, скорее всего, не сработает с запакованными / зашифрованными исполняемыми файлами.
То же самое можно проделать и с помощью других отладчиков. Сама процедура будет такой же, однако вы должны знать, как отредактировать инструкцию в памяти таким образом, чтобы разорвать петлю.
Допустим, вы хотите изменить CMP инструкцию на CMP EAX,1. Для этого нам необходимо заменить один байт в памяти (в данном случае по адресу 0×00415337).
Прикрепив данный отладчик (и приостановив процесс), просто запустите следующую команду:
eb 0x00415337 0x01
eb означает edit byte. Существуют также такие команды, как ew (edit word) и ed (edit dword).
После внесения изменений нажмите F5 (или введите «g») для того, чтобы процесс вышел из рекурсии и вернулся к исходной точке ввода.
После публикации данной статьи мне сообщили, что можно использовать \xeb\xfe, как шаблон исправления (который будет перемещаться сам к себе). Благодарности пользователям @fdfalcon и @pa_kt.