• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья ROPInjector: Using Return-Oriented Programming for Polymorphism and Antivirus Evasion

Я могу адменам там написать и попросить тебя активировать
Спасибо, но не вижу смысла - другие не попадут таким путём. На новом Васме сейчас тусит 5 человек, и мне посоветовали этот форум.

Ну и что бы совсем уж не флудить, по поводу условных переходов в байт-коде в принципе. Когда-то на reng.ru была тема про Haskell, детали уже не вспомню, но суть в том, что спецы там посмотрели код (сишный) после GHC и размышляли, как реверсить бинарники, когда такое пойдёт в массы. Реверсер в принципе мыслит императивно, поскольку процессор императивный. К счастью, Хаскель оказался слишком ленивый и не пошёл. Я пока не готов говорить предметно, а тем более подводить теоретическую базу, но Return в ROP намекает, что рекурсия на него должна ложиться. Ну и помимо Тьюринга и Чёрча, был такой Марков А.А. (младший). Вот он придумал т.н. нормальные алгоритмы - там нет ветвлений. На нижнем уровне переходы всё равно будут, но не каждый высокоуровневый алгоритм удобно трейсить в отладчике, даже имея сорцы.
 
st4s, что можешь сказать про ROP в качестве архитектурного обхода сигнатурного анализа и авер вирт машины? Тема для меня относительно новая, интересно мнение того, кто шарит, мож уже были мысли/пробовал подобное?
 
Ну я не шарю, у меня даже Виндоса принципиально нет и давно. :) Зато в соседнем окне рядом с браузером могу запустить эмулятор PS3. Почему гигабайтные игрушки для совершенно другого процессора STI Cell работают, а сотня байт для IA32 на IA32 нет? Кто-то схалтурил или допустил ошибку в эмуляторе, либо превышен какой-то лимит (кол-во выполненных инструкций, время). В случае ROP и примитивных песочниц может быть лимит на размер стека. Качественной же реализации не важно, используется ли вместо jmp пара push + ret, или финты с ошибками страниц - должно обрабатываться всё; если это не так, завтра исправят.

По поводу сигнатур. Как их составлять, что бы свести к минимуму пересечения с легитимным софтом? Есть такой документ 64-ia-32-architectures-optimization-manual.pdf и разработчик компилятора его обязательно читает. И есть фрагмент POC-a "Fuck VM" с предыдущей страницы:
Код:
; После исполнения инструкции восстановим регистр.
    bts D[edi][CONTEXT.rEFlags - CONTEXT.rEdi],8        ; EFLAGS_TF
    jmp Exec
...
; Рестарт инструкции.
Exec:
    xor eax,eax
Мнемоника bts - означает bit test and set. Используется ли проверка бита? Нет, после jmp Exec флаги сбрасываются xor-ом. Инструкция в данном паттерне устанавливает бит, т.е. эквивалентна or. Смотрим талмуд:
bts.png

bts вдвое медленнее or, а значит вменяемый компилятор её не сгенерирует. Вот такие удивительные вещи можно загнать в базу и делать по ней generic детект.

Теперь смотрим, что есть по теме:
push+jmp.png
То есть "кучу операций push <VAi>" + jmp можно добавлять как сигнатуру.

Но дальше там ещё интереснее, существует аппаратный механизм отслеживания несбалансированных ret:
call-ret.png
 
То есть "кучу операций push <VAi>" + jmp можно добавлять как сигнатуру.
Это если же ROP чейн присутствует в коде изначально. Моя небольшая адаптация/предложение - вынести доставку ROP чейн в уязвимый буфер на рантайме. Например, посредством сетевого запроса получать чейн с сервера бекконнектом, ну то есть так, как обычно боты стучат на сервер. Отправляем запрос, в ответ сервер возвращает либо junk + чейн(В таком случае процик начинает прыгать по адресам уже существующих инструкций) либо мусор(если необходимо «убить» данный билд и на стороне сервера было принято решение его инвалидировать таким образом и не слить полезную нагрузку, в этом случае, например, переполнения не происходит и сервер возвращает данные, которые помещаются в буфер и не перетирают адрес возврата = легитимная ветка исполнения).
Ты можешь спросить, мол к чему такие извраты, доставляй аналогично шеллкод с сервера и исполняй. Но тут-то прикол в том, что для загрузки шеллкода нужна как минимум исполняемая память(алокация новой или перезапись существующей) и во вторых - в коде имеется некий интерфейс, назовём это так, анализируя который, можно понять, что вот тут полученные данные с сети передаются в исполняемую память, какие данные пока не понятно, но сам факт этого - уже триггер. В случае переполнения буфера - заранее сказать сложно, переполнится ли он в принципе(в статике сложно - ибо переполнится или нет - это решает ответ сервера, seems like архитектурный обход статик анализа = вытеснение всем привычной идеологии крипта). То есть с виду весь интерфейс, и все, что имеется для анализа - это то, что размеры буфера не контролируются программистом, и то, это ещё нужно понять.. не контролировать размеры буфера тоже по разному можно. Используя опасные функции С, либо самому накостылять опасный примитив.
Соответственно, если продолжать мысль, вопрос, сможет ли это обработать эмулятор АВ, такой вот кейс. В принципе? И понять, что этот кейс нужно как то обработать. А даже если и сможет понять, сможет ли повторить алгоритм прыжков по адресам в исполняемом адресном пространстве импортированных dll, учитывая какое у эмулей системное окружение кастрированное?
 
Последнее редактирование:
Но тут-то прикол в том, что для загрузки шеллкода нужна как минимум исполняемая память(алокация новой или перезапись существующей) и во вторых - в коде имеется некий интерфейс, назовём это так, анализируя который, можно понять, что вот тут полученные данные с сети передаются в исполняемую память, какие данные пока не понятно, но сам факт этого - уже триггер.
А поскольку всего этого нет, то приложение признано легитимным и выпущено из песочницы, остаётся общая для всех HIPS. Так? Потому я не смог привязать этот сценарий к предыдущему вопросу. Это примерно как сейчас нажать в браузере "обновить страницу" и прилетит зеродей, только существенно больше свобод.
 
А поскольку всего этого нет, то приложение признано легитимным и выпущено из песочницы, остаётся общая для всех HIPS. Так?
Ну получается, что так. Не утверждаю, а именно пытаюсь понять для себя, какие митигейшены предоставляет АВ для этого кейса. Крипт же в привычном понимании это проблема chicken-egg. В обычном крипте - имеем код, который занимается восстановлением скрытого контента, во время работы приложения, что само по себе является маркером для детекта. И этот код аналогично нужно прятать. В этом и есть проблема, которую пытаются решать полу мерами. По этому обычно этот код обфусцируют, вставляют различного рода атаки на эмулятор и в момент, когда, якобы, эмулятор был байпаснут по мнению программиста - идёт загрузка PE образа/иной полезной нагрузки. А в данном подходе - уходим от этой проблемы. Кода, который восстанавливает скрытое содержимое - его попросту нет, в вирте крутить нечего, добираясь до этого скрытого контента. Этого скрытого контента нет в том числе, даже в зашифрованном/закодированном виде. Нет алгоритма декодинга полезной нагрузки, который можно в принципе повторить в вирте, до запуска приложения на реальной системе. Нет этих опасных интерфейсов. То есть нельзя взять тот же дебаггер, и руками найти в нем функцию, которая принимает некие данные, алоцирует исполняемую память и записывает эти данные туда. Есть лишь неявная подмена адреса возврата, при переполнении буфера, которая происходит лишь после сетевого запроса. По идее это все можно потрейсить на реальной системе(при разработке эксплоитов используется ведь дебаггер, чтобы понять что там на стеке, что по адресам, где сейчас процик). А в искусственной среде, вроде эмулятора, разве можно потрейсить в полной мере? Учитывая, что там все кастрировано - просто кусок кода читается в буфер эмулятора без полной имитации системы. Что делать эмулятору, если алгоритм загрузчика PE образа/другой полезной нагрузки построен ROP чейном, на адресах, которые находятся вне какого либо конкретного куска кода - разбросаны по всему апп, в том числе и в импортируемых dll (зная базу dll и смещение до инструкции, можно построить ROP чейн на инструкциях из разных dll, kernel32, user32, etc).
 
Последнее редактирование:
Есть лишь неявная подмена адреса возврата, при переполнении буфера, которая происходит лишь после сетевого запроса.
Гипотетически, можно ловить вот этот момент. Если есть полный эмулятор процессора, в простейшем случае достаточно сохранить копию последнего адреса возврата и сравнивать с содержимым памяти стека. В чуть более сложном случае, когда перезаписывается произвольный адрес возврата, придётся дублировать адреса, что сохраняют все команды call. Если же адреса переходов хранятся в куче (привет, ООП и континуации), то "детектор вторжений", похоже, превращается в тыкву.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх