Русский
Русский
English
Статистика
Реклама

Фаззинг

DevSecOps организация фаззинга исходного кода

10.09.2020 06:07:43 | Автор: admin


Узнав результаты голосования, проведённого в одной из наших прошлых статей, мы решили более подробно обсудить вопрос организации фаззинга. Кроме того, в рамках онлайн-встречи по информационной безопасности "Digital Security ON AIR" мы представили доклад, основанный на нашем опыте в DevSecOps, где также рассказали об этой интересной теме.


Записи всех докладов можно посмотреть на нашем Youtube-канале. Если же вы предпочитаете текстовый формат, добро пожаловать под кат!


  1. Фаззинг
  2. Этапы интеграции фаззинга в проект
  3. Фаззинг в облаке
  4. Заключение



Фаззинг


Введение


Относительно недавно вышла замечательная книга Building Secure and Reliable Systems от инженеров компании Google. Её главным лейтмотивом является мысль о том, что безопасность и надёжность систем и ПО тесно взаимосвязаны. Публикация подобного материала одним из крупнейших лидеров IT-рынка отлично демонстрирует, что безопасность становится неотъемлемой частью как процесса разработки, так и всего жизненного цикла системы или ПО.


Эффективно ли тестирование?


Чтобы сделать безопасную и стабильную систему, её нужно тестировать. К сожалению, писать идеальный код, не требующий доработки, не может никто. Поэтому в написанном коде априори есть ошибки и уязвимости. Как и в других сферах жизни, понимание этого приходит лишь с опытом. Аналогично в IT в целом и в разработке в частности: каждая компания или команда может находиться на своем уровне профессиональной зрелости. Какие-то подходы на определённом этапе будут просто недоступны для них. Так, если не внедрена непрерывная интеграция, то и о нормальном тестировании речи быть не может, не говоря уже о тестировании безопасности во время разработки. Так что, если вы не внедрили непрерывную интеграцию (CI), мы надеемся, что после прочтения нашей статьи вы начнёте делать шаги в нужном направлении. Далее мы предполагаем, что у вас есть настроенный и функционирующий CI, на базе которого и будут предлагаться улучшения.


CI/CD нужны не только для автоматизированных тестов, но и в глобальном плане для того, чтобы соответствовать требованиям времени. Каскадная модель разработки, согласно которой этапы проектирования, разработки и тестирования происходят строго последовательно, постепенно уходит в прошлое или остается лишь в определенных нишах. Сейчас очень важна скорость разработки. Важно, чтобы каждая часть ПО могла развиваться независимо от других частей. Таким требованиям соответствует метод непрерывной разработки. А непрерывной разработке нужно непрерывное тестирование.


Но перед тем, как продолжить, нам хотелось бы напомнить, что чем раньше найдена проблема, тем проще и менее затратно её исправить как во временном, так и в денежном эквиваленте. С точки же зрения обнаружения уязвимостей, это ещё и временные затраты на доставку патчей до ваших клиентов, а также их своевременная установка. Ведь закрытие неизвестной уязвимости нельзя заранее запланировать. А отвлекать разработчиков по малейшему поводу от их основной деятельности разработки продукта это тоже терять время и деньги. И ваши клиенты не будут рады часто прилетающим экстренным патчам. Равно как и разработчики :)



https://resources.securitycompass.com/blog/how-to-sell-training-costs-internally-2


Традиционная практика тестирования предполагает, что на вход программы подаются самые разные данные: корректные, почти корректные и граничные случаи. Это позволяет убедиться, что код работает в целом правильно и серьёзных ошибок в логике работы нет. Однако при этом качество проверки зависит от качества тестов и их количества. Фиксированное количество тестов не может покрыть все возможные варианты входных данных. Поэтому всегда есть вероятность, что какой-то важный случай был упущен, не вошёл в фиксированный набор тестов, а код, следовательно, всё-таки содержит ошибку, которую достаточно талантливый хакер сможет проэксплуатировать. Так что отсутствие падений или нестандартного поведения ПО в результате тестирования не следует рассматривать как повод его прекратить. Впрочем, и слепо перебирать все возможные значения это тоже не вариант. На это просто не хватит никаких ресурсов.



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


Что такое фаззинг?


Для начала небольшой исторический экскурс. Сам термин "фаззинг" ("fuzzing") появился впервые около 30 лет назад в работе An Empirical Study of the Reliability of UNIX Utilities. С его появлением связана одна интересная легенда. В 1988 удар молнии исказил передаваемые по линии данные, что привело к падению утилиты автора исследования. После этого уже профессор Брет Миллер вместе со своими студентами сделал полноценное исследование в данном направлении и на одном из семинаров представил программу fuzzer. Данная программа предназначалась как раз для тестирования ПО. Сам Брет Миллер так описывал свой подход: Наш подход не является заменой формальной верификации или тестирования. Скорее, это легковесный механизм для поиска ошибок в системе и повышения её надёжности. Официальное же определение фаззинга звучит так:


Фаззинг это техника автоматизированного тестирования, при которой на вход программе подаются специально подготовленные данные, которые могут привести её к аварийному состоянию или неопределённому поведению.

Сегодня мы сконцетрируемся на использовании фаззинга для кода на таких языках программирования, как С и С++, потому что:


  • в ПО на таких ЯП чаще находят бинарные уязвимости;
  • эффективные средства поиска уязвимостей, такие как libFuzzer и AFL, ориентированы, в первую очередь, на языки C и C++;
  • огромное количество программ и библиотек написано на C и C++.

Тем не менее все указанные наработки как минимум можно использовать без особых проблем для ПО на Rust или Go.


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



The Art, Science, and Engineering of Fuzzing:A Survey


Поэтому от работы к работе вы будете встречать одни и те же термины в разных контекстах. В рамках наших исследований безопасности мы классифицируем фаззеры следующим образом:


  • по знанию о входных данных:
    • Black-box мы ничего не знаем про формат данных. Нужно реверсить
    • Gray-box что-то о формате мы выяснили, но полная информация о нём нам всё ещё неизвестна
    • White-box у нас есть спецификация. Мы располагаем всеми сведениями о формате данных
  • по цели, которая будет подвергнута фаззингу:
    • source-based у нас есть исходники проекта
    • binary-based у нас нет исходников проекта
  • по наличию обратной реакции от тестируемого приложения:
    • feedback driven есть обратная реакция
    • not feedback driven нет обратной реакции
  • по операциям, которые будут совершаться над входными данными:
    • генерационные
    • мутационные
    • комбинированные

Стоит отметить, что часто binary-based фаззеры называют black-box, а source-based gray- или white-box. В данной статье нас в первую очередь интересует source-based фаззинг с обратной связью, поскольку мы строим DevSecOps для разработки собственного продукта или анализа opensource-проектов, а это значит, что исходный код для него у нас есть.


Фаззинг всё ещё популярен?


Кто-то может задаться вопросом: "Если фаззингу уже более 30 лет (согласно некоторым источникам и с учетом прообраза этого метода тестирования, и все 70), то почему только в последнее время он стал так популярен?".


В начале тысячелетия Microsoft в рамках описания создания Secure Development Lifecycle
выпустили несколько статей, посвящённых тематике фаззинга, но тогда речь в основном шла о black-box-фаззинге. Однако в 2013 году фаззинг получил вторую жизнь, благодаря созданию feedback-driven fuzzing, который позволил достичь новых высот в этой сфере. Важную роль также сыграло появление простых и понятных инструментов, о которых мы расскажем далее.


Feedback-driven fuzzing это вид фаззинга, при котором фаззер изменяет входные данные так, чтобы их обработка затрагивала как можно больше участков кода программы. Работа таких фаззеров возможна, благодаря их способности реагировать на отклик (feedback) программы. Обычно таким откликом является покрытие кода. Метрики покрытия кода отслеживают суммарное количество выполненных строк кода, базовых блоков, количество сделанных условных переходов. Задача фаззера генерировать данные, которые приводят к увеличению покрытия кода.


Одним из наиболее популярных feedback-driven фаззеров является American Fuzzy Lop от Michael Zalewski. AFL был создан в 2013 году и стал главным двигателем прогресса в современном фаззинге.


Про AFL было уже несколько статей на Хабре, но повторимся, как он работает:


  • в исполняемый файл добавляется инструментация для сбора информации о покрытии кода. Информация будет сохраняться в shared bitmap;
  • AFL запускает программу и доходит до функции, которую нужно профаззить. Здесь он сохраняет своё состояние через системный вызов fork;
  • далее происходит цикл "мутация данных запуск откат". При этом данные мутируются с учётом покрытия кода, получаемого от программы.

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


AFL оказался чрезвычайно эффективным при фаззинге различных программ. Это и сделало его знаменитым. Он был добавлен в Google Summer of Code (GSoC), а вокруг него сформировалось большое сообщество. Многочисленные энтузиасты предлагают собственные модификации AFL, которые по разным причинам не были приняты в основную ветку (например, существуют вариации для других ЯП). Об этом можно узнать подробнее в ещё одной нашей статье, посвященной различным вариациям AFL фаззера. Ещё хотелось бы заметить, что создатель AFL отошёл от дел и до недавнего времени проект почти не обновлялся, из-за чего многие стали использовать его активно поддерживаемый форк AFL++.


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


В итоге так и случилось. В 2015 году появился libFuzzer один из подпроектов LLVM. LibFuzzer позволяет реализовать feedback-driven in-memory фаззинг и имеет все преимущества, характерные для средств фаззинга исходного кода:


  • позволяет создавать отдельные фаззеры для каждой функции из рабочего проекта;
  • использует санитайзеры;
  • может быть интегрирован в проект через toolchain.

Статический анализ


Прежде чем обратиться непосредственно к Continuous Fuzzing, скажем пару слов об анализаторах кода и фаззерах. Для тех, кто думает, нужно ли это всё, если те или иные анализаторы кода уже используются в процессе разработки.


Code analysis Fuzzing
Встраивание в CI Да* Да
Тип Статика Динамика
Покрытие кода ~100% Зависит от тестовых данных и алгоритмов мутации
Ложные срабатывания Много Нет
Пропуск ошибок Зависит от базы знаний анализатора Зависит от тестовых данных
Типы ошибок Широкий спектр Определённый спектр ошибок
Переиспользование Нет* Уходит в тест
Ручной анализ Много Нет

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


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


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


На эту тему есть отличная статья How to Prevent the next Heartbleed, в которой автор сравнил эти подходы для улучшения качества openssl на примере нашумевшей уязвимости heartbleed. И именно подход, объединяющий анализ кода и фаззинг, помог выявить проблему с heartbleed. А обычное покрытие кода тестами, пусть и 100% по всем веткам разработки, оказалось не столь эффективным.


Непрерывный фаззинг в непрерывной разработке



Что же такое Continuous Fuzzing? Continuous fuzzing это подход к организации процесса. Иными словами, его эффективность будет зависеть от правильности организации процесса разработки в вашей компании. Все инструменты для организации такого процесса уже давно созданы осталось лишь правильно встроить их в свою систему.


К сожалению, многие разработчики и QA-тестировщики не так пристально следят за тенденциями в мире информационной безопасности. Поэтому для них лучше всего сделать так, чтобы процесс тестирования безопасности был максимально простым и прозрачным. Он не должен требовать от них особых знаний в этой сфере и сильно влиять на их трудовые будни. Правильно встроенный в процесс разработки continuous fuzzing не отвлекает и работает достаточно просто: написал код, сделал тест, подготовил тестовые данные и всё. Остальное сделает система. Настроенная среда разработки и отлаженные процессы будут служить вам годами, а разработчики приходить и уходить. Таким образом, лучше всё автоматизировать и не зависеть от конкретных людей.


Крупные компании уже не первый год настраивают у себя процесс фаззинга и развивают используемые инструменты. Этот подход уже множество раз доказал свою эффективность. Одна только статистика от Google ошеломляет числом найденных за время ее работы уязвимостей. По данным на 2019-й год, за все время существования ClusterFuzz было обнаружено около 16000 ошибок в Chrome и 11000 ошибок в более чем 160 популярных проектах с открытым исходным кодом.


Для организации фаззинга большие компании используют разный инструментарий. Выбор инструментов зависит от того, какие цели преследуются фаззингом, а также насколько безболезненно возможно внедрить этот инструментарий в уже существующий процесс разработки. В основе ClusterFuzz от Google лежат LibFuzzer, AFL и HonggFuzz (создан на базе LibFuzzera). Компания Microsoft недавно сделала публичным свой проект Springfield, превратив его в полноценный SaaS. Mozilla, помимо уже упомянутых фаззеров, разработала свой фреймворк для быстрого построения фаззеров (Grizzly) и дашборд для агрегации результатов фаззинга. Github не отстает. Так, он интегрировал ossfuzz в свою систему.


Вы, конечно, можете сказать, что это всё довольно большие компании, которые могут выделить значительные "лишние" мощности на "генерацию случайных данных". Но что мы видим? Несмотря на работу ClusterFuzza, та же компания Google активно поддерживает сторонних разработчиков и исследователей безопасности, позволяя запускать написанные ими фаззеры на своих мощностях. А если в процессе были обнаружены баги, за каждый из них выплачивается денежное вознаграждение. Всё-таки предварительный анализ и творческий подход исследователя безопасности позволяют найти новые векторы и уязвимости там, где "бездушная машина" заходит в тупик.


Поэтому, чтобы начать строить процесс Continuous Fuzzing у себя, необязательно быть крупной компанией, как тот же Google. Для этого не нужно крупных кластеров или мейнфреймов стартовые требования для внедрения Continuous Fuzzing минимальны. Речь о них пойдёт ниже, так как мы в своих проектах чаще используем значительно меньшие мощности и при этом успешно находим уязвимости.


Этапы интеграции фаззинга в проект


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


Выбор инструментов для организации фаззинга


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


  • Составление поверхности атаки (Attack Surface)
    • ПО, в котором удобнее проводить аудит кода (Пример: Sourcetrail)
    • ПО для построения MindMap-схем (Пример: Xmind)
  • Фаззинг
  • Санитайзеры (Sanitizers)

Для составления поверхности атаки необходимо ПО для аудита кода. Например, это может быть sourcetrail, understand, или подойдёт какая-нибудь популярная IDE. Лучше, чтобы ПО для инспекции кода имело возможность автоматического "рисования" блок-схем. Если у разработчиков уже есть готовые блок-схемы для программ из их проекта, то это значительно облегчит процесс. Если же нет, то искренне соболезнуем. Далее из составленной блок-схемы необходимо извлечь те куски, где тем или иным способом могут попасть на вход данные из недоверенных источников. Так и определяется поверхность атаки. На практике весьма удобно оформлять поверхность атаки в виде майндмап. Поверхность атаки служит не только для составления метрик "безопасности" (code coverage), а также, можно сказать, выступает чеклистом для написания фаззеров.


Об AFL и libFuzzer мы уже рассказали ранее, а практическое применение рассмотрим дальше, поэтому не будем на них останавливаться подробно. Можно считать их стандартом индустрии на данный момент. KLEE не совсем фаззер, хотя в некотором роде его можно использовать и так. Для описания процесса работы KLEE потребуется отдельная статья (на Хабре можно найти несколько, но KLEE за это время сильно обновился). Если же кто-то хочет углубиться в тему SMT, то в одной из прошлых статей мы опубликовали список материалов, который может помочь в этом. Если вкратце, то KLEE представляет собой символьную виртуальную машину, где параллельно выполняются символьные процессы и где каждый процесс представляет собой один из путей в исследуемой программе. Для каждого уникального пройденного пути KLEE сохраняет набор входных данных, необходимых для прохождения по этому пути. Это помогает улучшить эффективность фаззинга с помощью увеличения покрытия, а следовательно он прекрасно работает в паре с самими фаззерами.


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


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


Мы не зря начали наш список инструментов с тех, что помогают составить Attack Surface. В презентации для FuzzCon, посвящённой 25-летию фаззинга, была представлена следующая схема:



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


Определение Attack Surface


Что же стоит за словосочетанием "attack surface" ("поверхность атаки")? Возможно, вы уже знаете официальное значение, так как оно так прочно обосновалось в глоссарии, что даже удостоилось отдельной страницы в словаре хакерских терминов журнала Wired. Однако повторимся: если говорить по простому, это "возможность получения некорректных данных из недоверенных источников".


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


Точки входа поверхности атаки можно разделить на следующие категории. Основные это две (можно заметить, что они очень схожи с ранее упомянутыми типами фаззеров):


  • файлы различные парсеры (XML, JSON), мультимедиакодеки (аудио, видео, графика);
  • сеть сетевые протоколы (HTTP, SMTP), криптография (SSL/TLS), браузеры (Firefox, Chrome) и архиваторы файлов (ZIP, TAR).

Опциональными категориями в некоторых случаях можно выделить ядро или код общего назначения, например gui.


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



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


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

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


Анализ целей для фаззинга


Цель для фаззинга (fuzz target) это функция, которая принимает на вход данные и обрабатывает их с использованием тестируемого API. Иными словами, это то, что нам необходимо фаззить.


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


  1. Аргументы функции, через которые передаются данные для обработки. Нужен сам буфер для данных и его длина, если её возможно определить.
  2. Тип передаваемых данных. Например, документ html, картинка png, zip-архив. От этого зависит то, как будут генерироваться и мутироваться поступающие на вход данные.
  3. Список ресурсов (память, объекты, глобальные переменные), которые должны быть инициализированы перед вызовом целевой функции.
  4. Если мы фаззим внутренние функции компонентов, а не API, то понадобится составить список ограничений, которые накладываются на данные кодом, выполненным ранее. Бывает так, что проверка данных происходит в несколько этапов это нам тоже следует учитывать.


Анализ opensource-проекта с использованием sourcetrail


Этот этап является самым кропотливым, ведь целей для фаззинга может быть очень много: сотни или даже тысячи! Как раз тут у нас появился термин "реверс исходников", поскольку времени и сил на анализ может быть потрачено примерно столько же, сколько уходит на реверс приличного бинарного файла.


Подбор входных данных


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


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


При создании набора seeds нужно учитывать, что:


  • Его элементы должны влиять на покрытие кода программы. Чем выше покрытие, тем меньше неисследованных мест остаётся в программе.
  • Размер его элементов не должен быть большим, иначе это повлияет на скорость фаззинга. Ведь чем больше длина входных данных, тем дольше функция будет их обрабатывать и меньше запусков программы сможет сделать фаззер за единицу времени.
  • Кейсы в сидах должны быть функционально различными. Сильно похожие данные будут тормозить процесс фаззинга, поскольку фаззер часто будет попадать в места, которые уже были им исследованы. Перед запуском фаззера лучше провести минимизацию данных и убрать всё лишнее.

В процессе фаззинга сиды трансформируются в корпус. Корпус (corpus) это набор тест-кейсов, которые привели к росту покрытия кода во время фаззинга целевой программы или функции. Иначе говоря, это те самые интересные инпуты, которые потенциально могут привести к крешу программы. В корпусе сначала содержатся сиды, потом их мутации, затем мутации мутаций и т.д. Здесь мы видим, что фаззинг (feedback-driven) это циклический процесс, где на каждой новой итерации у нас всё больше шансов сгенерировать набор входных данных, который позволит найти уязвимости в программе.



Сиды очень сильно влияют на эффективность фаззинга. Этап подбора входных данных также нельзя игнорировать. Однако современные фаззеры порой бывают настолько хороши, что могут буквально из ничего сгенерировать хороший корпус. Пример такого корпуса, сгенерированного фаззером AFL, показан на рисунке выше. Более подробно этот процесс автор AFL Michal Zalewski описал в своём блоге. Советуем почитать.


Написание фаззеров


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


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


#include "MyAPI.h"extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {  MyAPI_ProcessInput(Data, Size);  return 0;}

Данный код скомпилируется в отдельный исполняемый файл, где в цикле на вход функции MyAPI_ProcessInput будут подаваться случайные данные Data размером Size. Если фаззер обнаружит ошибку, то на консоль будет выведено сообщение о ней. Настанет время устранить ошибку, а после запустить фаззинг дальше.


Сообщение об ошибке от libfuzzer
<!--INFO: Seed: 2240819152INFO: Loaded 1 modules   (6 inline 8-bit counters): 6 [0x565e90, 0x565e96), INFO: Loaded 1 PC tables (6 PCs): 6 [0x541908,0x541968), INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytesINFO: A corpus is not provided, starting from an empty corpus#2  INITED cov: 3 ft: 4 corp: 1/1b lim: 4 exec/s: 0 rss: 35Mb===================================================================29562==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff2fba1ba0 at pc 0x0000004dda61 bp 0x7fff2fba1b30 sp 0x7fff2fba12d0WRITE of size 65 at 0x7fff2fba1ba0 thread T0    #0 0x4dda60 in strcpy (/home/user/Documents/tmp/main+0x4dda60)    #1 0x52540f in MyAPI_ProcessInput(char const*) (/home/user/Documents/tmp/main+0x52540f)    #2 0x52561e in LLVMFuzzerTestOneInput (/home/user/Documents/tmp/main+0x52561e)    #3 0x42fe0a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/user/Documents/tmp/main+0x42fe0a)    #4 0x42f3a5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/home/user/Documents/tmp/main+0x42f3a5)    #5 0x4310ee in fuzzer::Fuzzer::MutateAndTestOne() (/home/user/Documents/tmp/main+0x4310ee)    #6 0x431dc5 in fuzzer::Fuzzer::Loop(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, fuzzer::fuzzer_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) (/home/user/Documents/tmp/main+0x431dc5)    #7 0x427df0 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/user/Documents/tmp/main+0x427df0)    #8 0x44b402 in main (/home/user/Documents/tmp/main+0x44b402)    #9 0x7f7ee294409a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)    #10 0x421909 in _start (/home/user/Documents/tmp/main+0x421909)Address 0x7fff2fba1ba0 is located in stack of thread T0 at offset 96 in frame    #0 0x5252ef in MyAPI_ProcessInput(char const*) (/home/user/Documents/tmp/main+0x5252ef)  This frame has 1 object(s):    [32, 96) 'buf' <== Memory access at offset 96 overflows this variableHINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork      (longjmp and C++ exceptions *are* supported)SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/user/Documents/tmp/main+0x4dda60) in strcpyShadow bytes around the buggy address:  0x100065f6c320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c360: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00=>0x100065f6c370: 00 00 00 00[f3]f3 f3 f3 00 00 00 00 00 00 00 00  0x100065f6c380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c390: f1 f1 f1 f1 00 00 00 00 f2 f2 f2 f2 f8 f3 f3 f3  0x100065f6c3a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c3b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x100065f6c3c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Shadow byte legend (one shadow byte represents 8 application bytes):  Addressable:           00  Partially addressable: 01 02 03 04 05 06 07   Heap left redzone:       fa  Freed heap region:       fd  Stack left redzone:      f1  Stack mid redzone:       f2  Stack right redzone:     f3  Stack after return:      f5  Stack use after scope:   f8  Global redzone:          f9  Global init order:       f6  Poisoned by user:        f7  Container overflow:      fc  Array cookie:            ac  Intra object redzone:    bb  ASan internal:           fe  Left alloca redzone:     ca  Right alloca redzone:    cb  Shadow gap:              cc==29562==ABORTINGMS: 1 InsertRepeatedBytes-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc0xa,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,\x0a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffartifact_prefix='./'; Test unit written to ./crash-76387a0aaeb6a1d2b6b6f095ab49c927c00243e5-->

Сборка и её особенности на разных платформах


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


Платформа


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


Платформа* LibFuzzer AFL ASAN/UBSAN/TSAN
Windows -
Linux + + +
OSX + + +

Однако, если продукт работает лишь на ОС Windows, не стоит отчаиваться. Во-первых, инструментарий для фаззинга активно развивается. Во-вторых, код крупных проектов, как правило, не является платформозависимым. Можно собрать необходимую часть проекта на unixсистеме и начать его фаззить. Ошибки никуда не денутся.


Компилятор


Думая о том, каким компилятором собирать фаззеры, мы всегда вспоминаем, что libfuzzer это подпроект LLVM, а в AFL предусмотрен llvm_mode, про который в его readme написано:


Настоящая инструментация на этапе компиляции, а не просто грубые изменения на уровне ассемблера.

Возможность добавления инструментации во время компиляции и поддержка LLVM самыми популярными фаззерами делает выбор компилятора очевидным для сборки лучше всего использовать clang. Конечно, нам попадались проекты, которые не были ориентированы на сборку этим компилятором, но нас это никогда не останавливало.


Система сборки


Касательно системы сборки, отметим, что это, скорее, вопрос вкуса и, возможно, привычки использования того или иного инструмента. Конкретно наш опыт показывает, что использование cmake позволяет в разы ускорить внедрение фаззинга в проект:


# Step 1add_custom_target(${FUZZ_TARGET_NAME})# Step 2function(add_fuzzer name path)    add_executable(${name} ${SRC} ${path})    target_compile_options(...)    set_target_properties(...)    add_dependencies(${FUZZ_TARGET_NAME} ${name})# Step 3set(fuzzers my_ideal_fuzzer1 my_ideal_fuzzer2 ...)foreach(fuzzer ${fuzzers})    add_fuzzer(${fuzzer} ${FUZZERS_DIR})endforeach()

Кратко интеграцию фаззеров в сборку проекта на cmake можно описать в трех шагах на примере нашего недавнего проекта:


  • Создаём цель ${FUZZ_TARGET_NAME}, к которой в зависимости будем добавлять фаззеры.
  • Пишем простую функцию, которая принимает на вход название фаззера и путь к его исходному коду. Реализация этой функции сводится к нескольким вызовам других cmake-функций. Сначала необходимо указать список исходников для сборки компонента который мы будем фаззить. Он хранится в переменной ${SRC}. Потом идут два вызова для установки опций компилятора и компоновщика. Последним вызовом добавляем новый исполняемый файл в качестве зависимости к цели, созданной ранее.
  • Остаётся дописать простой цикл который поочередно добавит все фаззеры к созданной ранее цели.

Другой пример система сборки autotools. Она является достаточно старой и не такой удобной, как cmake. Однако это не помешает внедрению фаззинга в проект. Например, здесь показан пример интеграции фаззинга в WebRTC сервер janus-gateway. Сам скрипт занимает где-то 100 строчек на bash. Конечно, это не так много, однако, как только проект начнёт меняться, эти скрипты тоже необходимо будет поддерживать в актуальном состоянии. На это может потребоваться дополнительное время.


Фаззинг в облаке


Наконец, мы добрались до финальной главы непосредственно про организацию continuous fuzzing. Сначала мы посмотрим, как с этой задачей справились в Google, а затем что получилось у нас.


Фаззинг-ферма от Google


В 2012 году компания Google анонсировала ClusterFuzz облачную инфраструктуру для поиска уязвимостей в компонентах своего браузера. В его описании приведена схема, наглядно показывающая, как должен выглядеть continuous fuzzing, интегрированный в процесс разработки.



Схема сontinious fuzzing от google


Пробежимся по элементам схемы:


  • Developer команда разработчиков проекта
  • Upstream project репозиторий с проектом
  • Oss-fuzz вспомогательный репозиторий для интеграции continuous fuzzing. Хранит build-конфиги фаззеров
  • Builder Сборщик фаззеров. Его задача скачивать исходники фаззеров из репозитория с проектом, подхватывать build-конфиги из oss-fuzz и производить сборку фаззеров. После сборки builder закачивает в хранилку (GCS bucket) файлы, необходимые для запуска фаззера
  • Clusterfuzz масштабируемая система для управления процессом фаззинга. Отвечает за планирование запусков фаззеров, обработку полученных от них данных, сбор статистики и многое другое. Пополняет свою коллекцию фаззеров из хранилки.
  • Issue tracker система отслеживания задач: youtrack, jira и подобные им программы. Сюда приходят отчёты о найденных уязвимостях

А теперь рассмотрим сам процесс интеграции. Начнём с того, что у команды разработчиков должен иметься репозиторий с кодом проекта. Здесь же должны храниться исходные коды фаззеров. Для интеграции необходимо подготовить build-конфиги для сборки и запуска фаззеров в инфраструктуре Google. Готовые build-конфиги нужно добавить в репозиторий oss-fuzz через пулл-реквест. После принятия пулл-реквеста можно радоваться: фаззеры будут автоматически собраны и запущены в Clusterfuzz. С каждым новым коммитом в oss-fuzz будут подхватываться фаззеры из репозитория с проектом. Если вдруг нашлась уязвимость, то уведомление придёт к разработчикам в issue tracker. Разработчики своевременно исправляют баги и все остаются довольными.


Почему решение от Google подходит не всем?


Отметим, что ферма для фаззинга от Google это круто. Впрочем, и в этой бочке мёда есть ложка дёгтя. Несмотря на то, что Clusterfuzz имеет открытый код, он жёстко привязан к облачным сервисам Google.


Это означает, что:


  • Для закрытых проектов такая система не подходит.
  • Изменить что-то под себя вряд ли получится.

Поэтому мы сделали свою фаззинг-ферму. Собственное решение для continuous fuzzing позволяет нам:


  • Снять ограничения на используемую инфраструктуру. Ферму можно запускать как в публичном облаке, так и у себя локально.
  • Полностью контролировать работу фермы: мониторинг, конфигурирование, добавление собственных фич и т.д.
  • Осуществлять фаззинг проприетарного программного обеспечения.

Схема нашей фермы похожа на схему от Google, однако она проще. Основная логика сосредоточена в компоненте FuzzFarm. Он выполняет те же задачи, что и Clusterfuzz. Ферма использует только наши внутренние ресурсы и может работать без доступа к интернету.



Схема работы нашей фаззинг-фермы


Интерфейс пользователя мы сделали простым и минималистичным. При этом он покрывает все необходимые задачи в continuous fuzzing.



Интерфейс пользователя нашей фаззинг-фермы


Что получаем в итоге?


В итоге мы имеем следующие преимущества использования continuous fuzzing:


  • Для разработчика это более быстрое обнаружение ошибок и готовые данные для юнит-тестов. В баг-трекер приходит уведомление, разработчик устраняет ошибку и быстро делает тест с использованием полученного от фермы кейса.
  • Ещё один шаг к оптимизации заключается в автоматическом создании юнит-тестов из полученных результатов фаззинга. Тестировщик будет рад :)
  • Наконец, можно добавить автоматический анализ крэшей. Для безопасника будет намного лучше, если система за него будет оценивать критичность найденных уязвимостей и предоставлять ему красивый отчёт. Более того, запуск фаззеров и обработка их результатов производятся автоматически. У безопасника появится время попить кофе.

Заключение


Тема фаззинга на сегодняшний день уже вышла далеко за пределы просто одного из видов тестирования. Это целое направление, объединившее единомышленников в коммьюнити. Уже проходят целые конференции и митапы, посвященные только фаззингу. Это большое и разноплановое направление со множеством перспектив развития, к примеру, Structure-Aware фаззинг, о котором мы постараемся рассказать позже. Поскольку, как вы могли заметить, развитие как отдельных инструментов, так и в целом направления не стоит на месте, существует возможность улучшать показатели по всем параметрам: скорости, качества, трудозатратам и т.п.


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


В конце хотелось бы поблагодарить за помощь в создании доклада всю нашу команду отдела Исследований и особенно других членов фаззинг-команды:


  • Дмитрий Евдокимов d1g1
  • Никита Кныжов presler

Соавтор статьи: Павел Князев poulix


Материалы


Подробнее..

Из песочницы Фаззинг тестирование веб-интерфейса. Расшифровка доклада

10.11.2020 14:15:42 | Автор: admin


В начале этого года Тензор проводил митап в городе Иваново, на котором я выступил с докладом про эксперименты с фаззинг-тестированием интерфейса. Тут расшифровка этого доклада.

Когда обезьяны заменят всех QA? Можно ли отказаться от ручного тестирования и UI автотестов, заменив их фаззингом? Как будет выглядеть полная диаграмма состояний и переходов для простого TODO приложения? Пример реализации и о том, как работает такой фаззинг далее под катом.

Всем привет! Меня зовут Докучаев Сергей. Последние 7 лет я занимаюсь тестированием во всех его проявлениях в компании Тензор.



У нас более 400 человек отвечают за качество выпускаемых продуктов. 60 из них занимаются автоматизацией, тестированием безопасности и производительности. Для того, чтобы поддерживать десятки тысяч E2E тестов, контролировать показатели производительности сотен страниц и выявлять уязвимости в промышленном масштабе нужно применять инструменты и методы, проверенные временем и ни раз испытанные в бою.



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



В фильме Матрица в одной из сцен Морфеус предлагает Нео выбрать красную или синюю таблетку. Томас Андерсон работал программистом и мы помним какой выбор он сделал. Будь он отъявленным тестировщиком слопал бы обе таблетки, чтобы посмотреть, как система поведёт себя в нестандартных условиях.

Комбинировать ручное тестирование и автотесты стало практически стандартом. Разработчики лучше всего знают как устроен их код и пишут юнит-тесты, функциональные тестировщики проверяют новый или часто меняющийся функционал, а весь регресс уходит к разнообразным автотестам.

Однако, в создании и поддержке автотестов внезапно не так много авто- и довольно много ручной работы:

  1. Нужно придумать что и как протестировать.
  2. Нужно найти элементы на странице, вбить нужные локаторы в Page Objects.
  3. Написать и отладить код.
  4. При любых изменениях актуализировать сценарий. Причём если функционал/интерфейс очень часто меняются, то автотесты оказываются не у дел, а ROI стремится к нулю.



К счастью, в мире тестирования существует ни две и не три таблетки. А целая россыпь: манки-тестирование, формальные методы, фаззинг-тестирование, решения на основе AI. И ещё больше их комбинаций.



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



Допустим сделали мы такой TODO и хотим его проверить. Берём подходящий сервис или инструмент и видим обезьянок в действии:



По такому же принципу мой кот как-то, полежав на клавиатуре, безвозвратно сломал презентацию и её пришлось делать заново:



Удобно, когда после 10 действий приложение выбрасывает исключение. Тут наша обезьянка сразу понимает, что произошла ошибка, а мы по логам можем понять хотя бы приблизительно, как она повторяется. А что если ошибка произошла после 100К случайных кликов и выглядит как валидный ответ? Единственным значимым плюсом этого подхода является максимальная простота ткнул кнопку и готово.



Противоположностью такого подхода являются формальные методы.



Это фотография Нью-Йорка в 2003 году. Одно из самых ярких и многолюдных мест на планете, Таймс-сквер, освещают только фары, проезжающих мимо машин. В тот год миллионы жителей Канады и США на три дня оказались в каменном веке из-за каскадного отключения электростанций. Одной из ключевых причин произошедшего оказалась race condition ошибка в ПО.

Критичные к ошибкам системы требуют особого подхода. Методы, которые опираются не на интуицию и навыки, а на математику называют формальными. И в отличии от тестирования они позволяют доказать, что в коде отсутствуют ошибки. Создавать модели гораздо сложнее, чем писать код, который они призваны проверить. А их использование больше похоже на доказательство теоремы на лекции по матанализу.



На слайде часть модели алгоритма двух рукопожатий, написанной на языке TLA+. Думаю для всех очевидно, что использование этих инструментов при проверке формочек на сайте сравни постройке Боинга 787 для проверки аэродинамических свойств кукурузника.



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

Фаззинг тестирование сейчас чаще всего рассматривается в контексте тестирования безопасности. И типовую схему, демонстрирующую такой подход, возьмём из OWASP гайда:



Тут у нас есть сайт, который требуется протестировать, есть БД с тестовыми данными и инструменты, при помощи которых будем отправлять указанные данные на сайт. Вектора представляют из себя обычные строки, которые были получены опытным путём. Такие строки с наибольшей вероятностью могут привести к обнаружению уязвимости. Это как та кавычка, которую многие на автомате ставят на место числа в URL из адресной строки.



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



Пользователь вводит новую дату и нажимает кнопку Сохранить. На сервер улетает запрос, с данными в json формате.



И если всё хорошо, то сервис отвечает двухсотым кодом.



С jsonами удобно работать программно и мы можем научить наш инструмент для фаззинга находить и определять даты в передаваемых данных. И он начнёт подставлять различные значения вместо них, например будет передавать несуществующий месяц.



И если мы в ответ вместо сообщения о невалидной дате получили исключение, то фиксируем ошибку.

Фаззить API несложная задача. Вот у нас передаваемые параметры в jsonе, вот мы отправляем запрос, получаем ответ и анализируем его. А как быть с GUI?

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



Если заняться декомпозицией, то мы увидим, что интерфейс это не единый монолит, он тоже состоит из отдельных элементов:



С каждым из контролов мы можем сделать не так-то и много. У нас есть мышка с двумя кнопками, колёсиком и клавиатура. Можно кликать по элементу, наводить на него курсор мыши, в текстовые поля можно вводить текст.

Если мы введём в текстовое поле какой-то текст и нажмём Enter, то наша страница перейдёт из одного состояния в другое:



Схематически это можно изобразить вот так:



Из этого состояния мы можем перейти в третье добавив ещё одну задачу в список:



А можем удалить добавленную задачу, вернувшись в первое состояние:



Или кликнуть по надписи TODOs и остаться во втором состоянии:



А теперь попробуем реализовать Proof-of-Concept такого подхода.



Для работы с браузером возьмём chromedriver, работать с диаграммой состояний и переходов будем через python библиотеку NetworkX, а рисовать будем через yEd.



Запускаем браузер, создаём инстанс графа, в котором между двумя вершинами может быть множество связей с разной направленностью. И открываем наше приложение.



Теперь мы должны описать состояние приложения. Из-за алгоритма сжатия изображения, мы можем использовать размер картинки в формате PNG как идентификатор состояния и через метод __eq__ реализовать сравнение этого состояния с другими. Через атрибут iterated мы фиксируем, что были прокликаны все кнопки, введены значения во все поля в этом состоянии, чтобы исключить повторную обработку.



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



При фаззинге текущего состояния мы должны каждый раз возвращаться в это состояние из нового. Для этого мы используем функцию nx.shortest_path, которая вернёт список элементов, которые нужно прокликать, чтобы перейти из базового состояния в текущее.
Для того, чтобы дождаться окончания реакции приложения на наши действий в функции wait используется Network Long Task API, показывающий занят ли JS какой-либо работой.

Вернёмся к нашему приложению. Исходное состояние имеет следующий вид:



После десяти итераций по приложению мы получим такую диаграмму состояний и переходов:



Через 22 итерации вот такой вид:



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



Так, с простым демонстрационным приложением мы справились. А что будет, если натравить этот скрипт на реальное веб-приложение. А будет хаос:



Мало того, что на бэкенде происходят изменения, сама по себе страница постоянно перерисывается при реакции на таймеры или события, при выполнении одних и тех же действий мы можем получить разные состояния. Но даже в таких приложениях можно найти куски функционала, с которыми наш скрипт может справиться без значительных доработок.
Возьмём для испытаний страницу аутентификации СБИС:



И для неё достаточно быстро получилось построить полную диаграмму состояний и переходов:





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

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



Рассмотрим последний паттерн а раньше было по-другому. Автотесты ведь регрессионным тестированием занимаются.

Вернёмся к графу после 10 итерации по TODO:



Сломаем код, который отвечает за открытие корзины и вновь прогоним 10 итераций:



А далее сравним два графа и найдём разницу в состояниях:



Можем подвести итог для данного подхода:



В текущем виде этот приём можно использовать для тестирования небольшого приложения и выявления очевидных или регрессионных ошибок. Для того, чтобы методика взлетела для больших приложений с нестабильным GUI потребуются значительные доработки.
Весь исходный код и список использованных материалов можно найти в репозитории: https://github.com/svdokuchaev/venom. Тем, кто хочет разобраться с применением фаззинга в тестировании, очень рекомендую The Fuzzing Book. Там в одной из частей описан такой же подход к фаззингу простых html форм.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru