Upload
liangtong
View
53
Download
2
Embed Size (px)
Citation preview
Задачи
• Code Coverage – Построение карты покрытия кода: какие инструкции и
сколько раз исполнялись в процессе работы программы
• Taint Analysis
– Сопоставление данных с конкретными участками кода, которые были исполнены при их обработке
• Symbolic Execution – Контроль над исполнением кода для генерации входных
данных, удовлетворяющих конкретным условиям
Code coverage
• Позволяет установить, какие инструкции исполнялись в процессе запуска программы
• Может служить в качестве объективной оценки качества тестирования (фаззинга) программы
• Помогает изучать execution flow в процессе реверс-инжиниринга
• Используется в системах honeypot для детектирования факта эксплуатации каких-либо уязвимостей (в том числе, не известных ранее)
Taint Analysis
• Позволяет связать трассу исполнения программы с данными, которые обрабатывались ней в процессе этого исполнения
• Помогает дать ответ на вопрос о том, как именно программа обрабатывала те или иные входные данные
• Используется для анализа тестовых кейсов, приводящих к аварийному завершению работы исследуемой программы
• Является необходимым фундаментом для реализации Symbolic Execution
Taint Analysis: тезисы
• Данные получают дополнительную характеристику, относительно которой они могут пребывать в двух состояниях: TAINTED и UNTAINTED
• В качестве источника или приёмника дынных в процессе исполнения программы выступают память и регистры процессора
• Тейнтинг передаётся от источника к приёмнику данных при их обработке программой (Taint Propagation)
Taint Propagation
mov eax, in_tainted mov ecx, in_untainted add ecx, eax ; ecx is TAINTED ---------------------------------------------------------- mov eax, in_tainted mov ecx, in_untainted mov ax, cx ; ax is UNTAINTED, eax is TAINTED ---------------------------------------------------------- mov eax, in_tainted xor eax, eax ; eax is UNTAINTED ---------------------------------------------------------- push in_tainted pop eax ; eax is TAINTED, dword[esp + 4] is TAINTED ---------------------------------------------------------- xor eax, eax cmp eax, in_tainted ; AF, CF, OF, PF, SF, ZF is TAINTED
Taint Analysis и уязвимости
• В качестве начального источника TAINTED данных указываются те данные, которые может контролировать потенциальный атакующий (файлы, сетевые пакеты, параметры командной строки, итд.)
• Если в ходе анализа обнаружилось, что eip – TAINTED, то это является показателем факта успешной эксплуатации уязвимости с передачей управления на контролируемый атакующим код (шеллкод)
Taint Analysis: проблемы и решения
• Taint Analysis по своей природе требует детального анализа каждой исполняемой инструкции
• x86 и x86_64 – CISC архитектуры с огромной системой команд и большим количеством расширений, написать код, который бы корректно анализировал все существующие инструкции – сложно
• Так же существуют и другие популярные архитектуры (ARM, MIPS, PPC), писать реализацию Taint Analysis для каждой из них отдельно – совершенно нерационально
Taint Analysis: проблемы и решения
• Разумно использовать промежуточное представление кода (IR, Intermediate Representation) для работы с ним
• IR представляет собой псевдо-ассемблер с упрощенной системой адресации, небольшим набором базовых инструкций и возможностью преобразования кода для любой из архитектур в обе стороны (native → IR, IR → native)
• Существующие реализации IR:
– VEX: используется в Valgrind – LLVM bytecode: используется в системе LLVM – REIL: используется в продуктах от Zynamics (RIP ) – RTL: используется в закрытом исследовательском проекте Phoenix от
Microsoft Research
Taint Analysis: проблемы и решения
• Идеальный анализатор кода для задач Taint Analysis должен отслеживать и инструментирвать весь код, исполняемый операционной системой (как режиме пользователя, так и в режиме ядра)
• Но реализация такого подхода на практике чревата неприемлемо низкой производительностью. В большинстве случаев для Taint Analysis достаточно контроля над всем кодом пользовательского режима, исполняемого в контексте исследуемого процесса
• В ряде случаев, уход вектора исполнения в режим ядра может нарушать Taint Propagation при анализе только кода пользовательского режима (пример – системные вызовы, работающие с memory mappings). Анализатор кода должен уметь корретно обрабатывать такие случаи
• Активное использование IPC при обработке данных – страшнейший кошмар для любого анализатора кода, работающего только в контексте конкретного процесса
Symbolic Execution
• Концепция динамического анализа кода, позволяющая решить основной вопрос реверс-инжинринга: «Может ли переменная X получить значение Y после исполнения определённого набора инструкций? Какие данные должна получить программа на вход, что бы это произошло?»
• Любая программа имеет конечное количество промежуточных и финальных состояний, переход между которыми и является её исполнением
• Входные данные определяют в каком именно состоянии окажется программа на том или ином шаге исполнения
• Symbolic Execution позволяет получить такой набор входных данных для исследуемой программы, обработка которых привёдет вектор исполнения в нужное исследователю промежуточное или финальное состояние программы
Symbolic Execution: применение
• В ходе тестирования (фаззинга) программы может использоваться для генерации такого набора тестовых данных, при обработке которого произойдёт исполнение всех возможных ветвей алгоритма
• Может использоваться для аудита программ на предмет недекларированных функциональных возможностей
• Может использоваться для взлома некоторых типов защит от несанкционированного копирования программы: «Invalid serial number, try again» и «Welcome, thank you for purchasing our program» - это всего лишь состояния программы, переход между которыми осуществляется путём прохода вектора исполнения через промежуточные состояния, соответствующие опеределённым входным данным
Symbolic Execution: как это работает
• Для отслеживания того, как определённый набор вхоных данных влияет на состояние программы на каждом шаге её исполнения используется Taint Analysis
• Для описания конкретного состояния программы относительно входных данных используются символьные выражения, описывающие шаги обработки данных
• Для вычисления набора входных данных, которые позволяли бы получить желаемое состояние относительно известного, используется бескванторная бит-векторная арифметика с конечной точностью (quantifier-free finite-precision bit-vector arithmetic, QF-BV), позволяющая находить точные или приближенные данные, удволтворяющие условиям символьных выражений
Symbolic Execution: примеры
Пример программы:
val = read_from_user(); val = val + 10; if (val == 42) { // state #1 sweet_delicious(); } else { // state #2 gtfo_bitch(); }
Символьное выражение для state #1:
Символьное выражение для state #2:
Symbolic Execution: примеры
• Для решения символьных выражений существуют готовые библиотеки, например SMT-LIB (Satisfiability Modulo Theories Library) – http://www.smtlib.org/
• Запись рассмотренного ранее примера символьного
выражения в формате SMT-LIB: (benchmark test :status unknown :logic QF_BV :extrafuns ((a BitVec[8])(b BitVec[8])(c BitVec[8])(d BitVec[8])(e BitVec[8])) :assumption (= a b) :assumption (= c bv10[8]) :assumption (= d (bvadd a c)) :assumption (= e d) :formula (= e bv42[8]) )
Symbolic Execution: практика
• После поверхностного ознакомления с Symbolic Execution может показаться, что при помощи данной технологии очень легко искать уязвимости нулевого дня, не прибегая к большому количеству ручной работы
• Но это не соответствует действительности: – Это достаточно молодое направление исследований, интерес которому больше
проявляют люди из академической среды, чем «крутые эксплойтеры» – Существующие в настоящий момент инструмены очень далеки от идеала и не годятся
для серьёзного промышленного применения – Само по себе Symbolic Execution упирается во можество ограничений, основными из
которых являются высокие требования к вычислительным ресурсам и сложность в реализации
• Однако, существуют интересные проекты, в рамках которых удалось
реализовать работающие (хотя бы в тесных рамках) инструменты: – Fuzzgrind (http://esec-lab.sogeti.com/pages/Fuzzgrind) – KLEE (http://klee.llvm.org/)
Symbolic Execution: практика
• Если вы хотите получать в процессе поиска уязвимостей максимально качественный и предсказуемый результат за приемлемое время прямо сейчас – Symbolic Execution вряд-ли в этом поможет
Технологии решения задач
• Возможности по отладке кода, предоставляемые процессором – Отладочные регистры и Last Branch Recording в IA-32 и Intel 64
• Отладчики и отладочный API
– ptrace(), Debugging Tools for Windows
• Dynamic Binary Instrumentation (DBI, динамическое инструментирование бинарного кода) – PIN, DynamoRIO, Valgrind
• Intermediate Representation (IR, промежуточное представление кода) – Valgrind, LLVM и инструменты на их базе (Fuzzgrind, KLEE, S²E)
• Эмуляция – BitBlaze, Ether, S²E
Инструменты: отладчики и отладочный API
• (+) В том или ином виде реализованы в любой операционной системе
• (+) Easy to code – easy to use
– Простой API – Предсказуемая эксплуатация – Почти во всех современных отладчиках есть возможность
подключения пользовательских плагинов и скриптов
• (–) Производительность недостаточная даже для задач
анализа покрытия кода
Инструменты: DBI
• Используют динамическую рекомпиляцию инструментируемого кода
• Обеспечивают высокую производительность из-за низких накладных расходов на инструментирование одной инструкции (так как нет необходимости в механизмах IPC)
• Наиболее известные представители: – PIN (http://www.pintool.org/) – DynamoRIO (http://dynamorio.org/)
• PIN используется для динамического анализа кода в инструментах BAP
(http://bap.ece.cmu.edu/) и Vera (http://www.offensivecomputing.net/vera/), хотя, как и DynamoRIO, является вполне самодостаточным
Инструменты: DBI
• (+) Из всех существующих технологий – наиболее пригодна для практического применения уже сейчас, недостатки есть, но весьма несущественные
• (+) Существующие инструменты могут работать с IA-32, IA-64 и Intel 64 под Windows, Linux и Mac OS X
• (–) Инструментальный модуль может использовать только тот API, который предоставляется самим движком (в PIN и DynamoRIO)
• (–) Инструментальный модуль вынужден работать только с машинным кодом, который, в случае IA-32, IA-64 и Intel 64, несколько сложен для анализа (а вы ожидали другого от CISC-процессоров?)
Инструменты: Valgrind
• Одина из самых мощных открытых сред для профайлинга и инструментирования кода
• Инструменты Valgrind, в отличии от инструментальных модулей PIN и DynamoRIO, работают с промежуточным представлением кода (VEX IR)
• Машинный код транслируется в VEX IR динамически, в процессе исполнения программы
• Ядро Valgrind не требует наличия исходных текстов, но они (или хотя бы отладочная информация) весьма желательны при использовании инструментов для поиска ошибок, входящих в состав Valgrind
Инструменты: Valgrind
• (+) Является серьёзным и стабильно работающим инструментом, применяется в промышленной разработке ПО
– Джулиан Сюард, первоначальный автор Valgrind, выиграл в 2006-м году
Google-O’Reilly Open Source Award за свою работу – Для поиска нашумевшей «фичи», в реализации memcpy() из glibc в Fedora
14, Линус Торвальдс запускал под Valgrind WEB-браузер с Adobe Flash плагином
• (–) Работает только в Linux и Mac OS X (зато кроме IA-32 и Intel 64 поддерживает PowerPC)
• (–) Многие инструменты выдают пригодные для анализа результаты своей работы только при наличии исходных текстов или отладочной информации
Инструменты: LLVM
• Задумывался как универсальный компилятор/транслятор чего угодно во что угодно, с возможностью реализации оптимизации кода на промежуточном уровне
• Состоит из:
– LLVM Front-end – предназначен для преобразования какого-либо кода (исходного или машинного – не важно) в байт-код LLVM
– LLVM Back-end – предназначен для трансляции байт-кода LLVM в
машинный код для заданной архитектуры (как статически, так и в JiT)
– Виртуальная машина LLVM – предназначена для исполнения, преобразования (в том числе оптимизации) и инструментирования байт-кода LLVM
Инструменты: LLVM
• Для языков C, С++ и Objective-C в качестве front-end используется компилятор Clang
• Существует версия GCC, которая использует LLVM в качестве back-end
• Реализация front-end для машинного кода IA-32 ведётся в рамках таких проектов как libcpu (http://www.libcpu.org/) и S²E
• В реальной жизни трансляция машинного кода в байт-код LLVM должным образом реализована только в статике, что делает LLVM «as is» не пригодным для работы с программами без исходных текстов
• Наиболее известные инструменты для Symbolic Execution, основанные на LLVM: – KLEE (http://klee.llvm.org/): работает только при наличии исходных текстов – S²E (https://s2e.epfl.ch/): использует модифицированный QEMU для генерации байт-кода
LLVM в процессе исполнения машинного кода
Инструменты: LLVM
• (+) Является серьёзным и стабильно работающим инструментом, применяется в промышленной разработке ПО
– Используется в таких компаниях как Apple, Adobe и Google – на LLVM основана подсистема OpenGL в Mac OS X 10.5 – iPhone SDK использует GCC с бэкэндом на LLVM
• (–) Сам по себе LLVM существует под большинство архитектур и операционных систем, однако, многие фронт-енды и инструменты, основанные на LLVM, имеют проблемы с переносимостью
– Windows-версия Clang поддерживает только Plain C – KLEE и S²E «из коробки» не работают в Windows
Инструменты: KLEE
• Пожалуй, наиболее удачная реализация идей Symbolic Execution, разрабатывается под крылом проекта LLVM
• Для инструментирования кода использует виртуальную машину LLVM, может работать с программами на любых языках, для которых есть соответствующий LLVM front-end
• Основная задача KLEE – генерация таких наборов выходных данных, которые приводили бы к исполнению всех ветвей алгоритма исследуемой программы
• Авторы KLEE успешно применяли свой инструмент для поиска ошибок в GNU Coreutils
Инструменты: KLEE
• (+) Хоть и применим для анализа далеко не всех программ – работает весьма качественно, там где может
• (+) Результат работы предоставляет в удобном виде:
– Возможность «воспроизведения» произвольного тестового кейса на native версии исследуемой программы
– Kcachegrind-совместимые графы и трассы исполнения исследуемой программы
• (–) Linux и Mac OS X only, портирование под Windows возможно только в теории
• (–) Для более-менее нормальной работы требуется компилировать в LLVM-байткод не только исследуемую программу, но и все библиотеки, которые она использует – В состав KLEE входит только LLVM-версия ucLibc, все остальные нужные билиотеки
придётся пересобирать в ручную, и это довольно рутинная работа
Инструменты: Ether
• Представляет собой достаточно простой трассировщик на базе модифицированного XEN
• Возможность сохранения полной трассы
исполнения для указанного исполняемого файла
• Возможность логирования системных вызовов отдельно от трассировки