Привет, Хабр! Навыки статического анализа кода в арсенале исследователя безопасности приложений фактически являются must-have скиллом. Искать ручками уязвимости в коде, не прибегая к автоматизации, для небольших проектов вполне быть может и приемлемый сценарий. Но для больших задач с миллионами строк кода — это непозволительная роскошь с точки зрения временных затрат. Решения по автоматизации всего этого процесса не заставили себя долго ждать. Именно по этой причине мы имеем такой широкий выбор инструментов статического анализа, в разнообразии которого можно утонуть. Рассказать же подробно о всех не хватит и книги.  По этой причине главными героями сегодняшней статьи стали несколько известных и развивающихся инструментов статического анализа кода, которые могут пригодиться исследователям безопасности в процессе поиска уязвимостей. Мы постарались отобрать инструменты так, чтобы они обязательно имели открытый исходный код, возможность работы с языками C/C++ (так как они являются наиболее сложными в аспекте безопасного программирования) и без каких-либо ограничений для этого (например, как в SonarQube), а также имели возможность создавать собственные запросы/правила для кастомизации анализа под конкретный проект/тип уязвимости с минимальными усилиями.  Присутствие не самых известных инструментов в обзоре может немного удивить. Тем не менее они являются интересными образцами и достойны рассмотреня. Если вы хотите рассказать о своем фаворите, будем рады почитать о вашем опыте в комментариях! Прежде чем начать обзор, сперва обратимся к истокам. Напомним, что такое SAST и за что его так все не любят. SAST (Static Application Security Testing) – это процесс тестирования, при котором применяется техники статического анализа для поиска ошибок и потенциальных проблем безопасности. Как уже отмечалось, на данный момент существует огромное количество SAST-инструментов для различных языков программирования. Пожалуй, наиболее полный перечень всевозможных инструментов расположен в github-репозитории Analysis Tools. Внушительный по объему список также был представлен OWASP Foundation – Source Code Analysis Tools. К сожалению, о SAST далеко не все отзываются лестно. Так уж вышло, что общее отношение к SAST у ряда специалистов скорее скептично-негативное ввиду большого количества ложных срабатываний. Это отбивает желание пользоваться подобными инструментами, тратя на них свое драгоценное время. Задача этой статьи вовсе не переубедить, а показать, что эти инструменты — не панацея, а лишь вспомогательный элемент в процессе поиска уязвимостей. Зная контекст исследуемого приложения/системы, можно создавать довольно эффективные правила, которые позволят найти отправные точки к возможным багам, ну и вообще облегчат поиск в огромной груде кода.  С учетом уже упомянутых критериев для обзора были выбраны следующие SAST-инструменты: Чтобы оценить их, нам понадобится проект с исходным кодом, содержащий уязвимости. Было бы очень здорово охватить все слабости, перечисленные в Weaknesses in Software Written in C и Weaknesses in Software Written in C++, но мы решили взять одну из них и сделать акцент на различных способах ее реализации, чтобы проверить возможности исследуемых инструментов. И это… double-free, она же CWE-415! Данная уязвимость, несмотря на свою простоту и почтенный возраст, до сих пор обнаруживается в ряде известных проектов. Правила для анализа будем создавать под этот тип ошибки. Помимо уязвимых сниппетов кода, наш проект будет содержать несколько простых ловушек для SAST-инструментов. Для людей эти "ловушки" могут показаться смешными, но для инструментов это целое испытание. Подобным образом можно будет выявить проблемные места инструментов, например, некоторые из них могут не уметь работать с Control Flow, не обладать межпроцедурным анализом и т.д. Так что постараемся внимательно изучать документацию, быть в курсе существующих примеров правил/запросов, и конечно же, подходить творчески и экспериментировать. Игрушечный проект расположился в github-репозитории "double-free-samples". Оформим для себя небольшой чек-лист по багам в коде игрушечного проекта, чтобы оценивать работу инструментов в будущем. В табличку запишем информацию об уязвимых участках кода и о "ловушках" для SAST-инструментов, т.е. ситуациях, при которых уязвимость не может существовать. Если они будут обнаружены в результатах, это будут ложноположительные кейсы.  Также стоит отметить, что код проекта полностью готов к сборке, если это будет необходимо инструменту статического анализа. Несомненно, на деле у исследователей безопасности не всегда имеется такая возможность. Однако это не повод не рассматривать те инструменты, которые нуждаются в кодовой базе. Как правило, в контексте их работы это является неплохим преимуществом, ведь кодовая база содержит много полезной информации для анализа, что влияет на качество результатов.  Начнем же наш обзор! Semgrep — легковесный инструмент статического анализа для поиска ошибок в коде. Был разработан командой r2c, написан в большинстве своем на OCaml. Первый релиз состоялся в начале 2020 года. Пожалуй, это один из немногих инструментов, в котором трансформация "From Zero To Hero" для пользователя является самой скоростной. Всё это благодаря тому, что язык запросов, или в данном случае паттернов, максимально похож на язык программирования исследуемого проекта.  Когда Semgrep выполняет анализ кода, он создает абстрактное синтаксическое дерево (AST), которое затем транслируется в промежуточный язык, с которым уже и проводится анализ. Это крайне удобно и позволяет за короткое время научиться создавать эффективные правила. На этот счет инструмент имеет отличную документацию, а также большое количество хвалебных отзывов от AppSec-комьюнити, в том числе в виде отдельных статей. Мы тоже не обойдем стороной его достоинства — вот они: И несколько недоработок: Как уже отмечалось, поддержка C/C++ пока что является экспериментальной, и публичных правил для этих языков не много. В сравнении с языками зрелой поддержки Java, Python, Go, Ruby, C#, имеющими впечатляющий ruleset. Эту проблему поднимал исследователь Marco Ivaldi в статьe "Semgrep ruleset for C/C++ vulnerability research". Он написал несколько десятков собственных правил для поиска уязвимостей в C/C++, их можно найти в его github-репозитории. Официальный репозиторий semgrep-rules, к сожалению, не содержит правил для C++. Если вы опытный специалист и хотели бы внести вклад в развитие "сишного" комьюнити Semgrep, вы нужны, как никогда! Под внешней ссылкой в данном контексте понимаются файлы к включению, указанные в директиве #include <> или #include "". Semgrep не умеет работать с внешними ссылками и абсолютно не знает, что там может находиться внутри. Таковы особенности инструмента. Теперь пару слов о taint-анализе. Анализ DataFlow как более обширное понятие используется для вычисления возможных значений, которые переменная может содержать в различных точках программы, определяя, как эти значения распространяются и где используются. Tainting tracking как более узкое понятие подразумевает контроль потока недоверенных данных (пользовательский ввод) по всему коду проекта с целью выявить их влияние на выполнение программы. Taint-анализ подразделяется на виды: 1) Local taint анализирует поток данных внутри одной функции. В данном случае рассматриваются только ребра между узлами графа, принадлежащие одной и той же функции.  2) Global taint исследует поток "порчи" между разными функциями по всей программе. Стандартные правила Semgrep выполняются только в отношении одного файла, а новомодный "Taint mode" выполняется только в рамках одной функции, что для больших проектов и более сложных ошибок скорее бестолково, чем полезно. На этот счет r2c выпустила новый проприетарный инструмент под названием DeepSemgrep, который должен был добавить функционал global taint. Чтобы им воспользоваться, необходимо к запуску стандартной утилиты semgrep в командной строке добавить дополнительный аргумент, вот так: а в правиле указать дополнительные поля pattern-sources и pattern-sinks. К сожалению, поддерживаемыми языками на данный момент являются только Java и Ruby. На этом обсуждение достоинств и недостатков Semgrep заканчиваем и переходим к созданию правила. Локальные правила в Semgrep бывают двух типов — эфемерные (единоразовые) и YAML-правила.  Эфемерный тип может быть полезен, если знаете, как кратко сформулировать ваше правило "вместо тысячи слов". Например, хотим примитивный double-free: Такой запрос получит достаточно много ложноположительных результатов, так как мы совсем не уточнили, что высвобожденный чанк, на который указывает наш поинтер, не должен быть переназначен перед вторым освобождением. Для реализации более подробных и сложных правил необходимо создать YAML-файл и в нём описать все необходимые условия. Для написания правила воспользуемся следующими конструкциями Semgrep: Semgrep богат на различные конструкции. С ними можно дополнительно ознакомиться в документации. Очень полезными являются, например, pattern-regex, позволяющий искать в коде по заданной PCRE-регулярке, metavariable-pattern и metavariable-regex, позволяющие гибко настроить метапеременные по заданным шаблонам/PCRE-регуляркам соответственно. Итоговое правило будет выглядеть так:  Запускаем: Что ж, в этом и есть сила Semgrep! Были найдены все кейсы двойного освобождения, ложноположительные результаты отсутствуют. Всё это потому, что мы создали соответствующие паттерны для тех ситуаций в коде проекта, где ошибка двойного освобождения могла бы существовать, и отфильтровали те случаи, когда ошибки нет. Это правило хорошо работает для игрушечного проекта и является достаточным. Запустите это правило на другом, более сложном проекте — результат вас не обрадует, поскольку правило охватывает далеко не все паттерны ситуаций, при которых в принципе может существовать ошибка double-free, а таких кейсов огромное количество. Пробуйте, дополняйте свои правила разнообразными паттернами, и их объемы будут соразмерно расти с качеством результатов.  Semgrep, несмотря на свои недостатки, оставляет о себе положительное впечатление. Инструмент продолжает развиваться благодаря отличной команде разработчиков и широкому заинтересованному AppSec комьюнити. Продолжим наш обзор и перейдем к рассмотрению следующего инструмента — CodeQL. CodeQL впервые был представлен компанией Semmle в 2018 году. Бывалые ресечеры могут помнить его как SemmleQL. Он довольно быстро был выкуплен GitHub и с 2020 года принадлежит Microsoft. Одним из основных преимуществ CodeQL является, пожалуй, лучшая реализация DataFlow-анализа/Taint tracking в рамках кодовой базы по сравнению с другими инструментами. Последняя является иерархическим представлением кода, она включает в себя абстрактное синтаксическое дерево (AST) всего проекта, CFG (Control Flow Graph) и DataFlow данные, которые требуются для taint-анализа.  О CodeQL уже бывали выступления на международных площадках (например, ZeroNights 2021 — "Company wide SAST"), о нем не раз писали отличные статьи, в том числе и на Хабре (например, CodeQL: SAST своими руками (и головой), Сканирование кода C++ с помощью GitHub Actions), так что не будем повторяться и кратко обозначим некоторые особенности инструмента. Преимущества: Недостатки: Это значит, что, если проект написан на компилируемом языке, он должен быть готов к сборке. Если проект по каким-либо причинам не может быть собран, CodeQL использовать не получится. К сожалению, как в случае с Semgrep, где можно с лету начинать писать собственные паттерны, с CodeQL придется потратить время на изучения языка запросов.  Это больше относится к taint-анализу. Если кодовая база велика, то некоторые запросы могут исполняться крайне долго, что, пожалуй, не удивительно. С базой в 4 миллиона строк кода запрос мог исполняться до полутора часов. Иногда taint-анализ окончательно замораживался и не возвращался из состояния поиска путей. Учитывая формальную сторону, Github не разрешает использовать CodeQL для создания и анализа кодовых баз в ряде случаев. Более подробная информация располагается в GitHub CodeQL Terms and Conditions. Ошибки случаются, даже у taint-движка CodeQL. Порой в результатах встречаются некоторые ноды taint-путей, которые не внушают доверия, и часто такие пути вовсе не существуют. Например:  Теперь вернемся к нашему проекту. Для начала создадим СodeQL базу данных из директории с кодом, вот так: Прикрепляем ее в VSCode в качестве основной базы и приступаем к поиску баг. В официальном репозитории CodeQL есть ряд готовых запросов для поиска некоторых типов уязвимостей. Попробуем воспользоваться одним из таких как раз для поиска ошибок типа double-free. Вот, что мы получим:  Как можно заметить, этот запрос не обнаружил double-delete, а также двойное освобождение через метод-обертку wrapper. Для новичков в CodeQL этот запрос может оказаться сложным и запутанным, поэтому попробуем создать наш собственный, поменьше и попроще, который будет обнаруживать конкретно double-delete в проекте. Общий макет CodeQL-запросов обычно следующий: Дополним его объектами FunctionCall и предикатами: Теперь сформируем наш запрос: Результат нас обрадует — CodeQL найдет примитивный double-delete, как мы и хотели:  Отлично, а теперь попробуем применить taint tracking и найти столько double-free, сколько сможем. Воспользуемся semmle.code.cpp.dataflow.TaintTracking для создания собственной taint-конфигурации, а также DataFlow::PathGraph для корректного отображения найденных путей.  Почти хорошо. Запрос успешно нашел вызов free в методе-обертке и обозначил путь требуемых данных (указателя на чанк) от источника к приемнику.  Однако CodeQL выдал один false-positive результат в кейсе с внутренним вызовом функции reassignment(char *ptr), где поинтер снова будет содержать адрес того самого чанка, который распределитель кучи достанет из корзины. К сожалению, переопределение предиката isSanitizer taint-конфигурации никак не смогло на это повлиять, так как taint path не включил в себя вызов этой функции, а значит и фильтрация будет безуспешной. Таким образом, CodeQL — неплохой инструмент статического анализа, где результат сильно зависит от качества запросов, которые мы создаем к базе. CodeQL оставляет довольно противоречивое впечатление, так как несмотря на свой потенциал (особенно для объектно-ориентированных языков программирования) он имеет множество недостатков. В статье исследователя Valtteri Rahkonen "SAST Tool Comparison Using Secure C Coding Standard Examples", где сравнивались ряд open-source и коммерческих SAST-инструментов, CodeQL показал один из самых слабых результатов. Однако не будем останавливаться на плохой ноте. CodeQL может открыть большие возможности для исследования кода, что доказывает растущее число CVE, найденных с его помощью. Weggli — инструмент семантического поиска в кодовых базах на C/C++. Разработан исследователем Felix Wilhelm из Google Project Zero и опубликован осенью 2021 года с целью помочь ресечерам находить интересные места в больших кодовых базах. "Killer feature" данного инструмента — возможность исследовать C/C++ проекты без необходимости их сборки, в чем он очень похож на Semgrep.  На данный момент о Weggli не так много информации. Одна из немногих статей о Weggli от парочки бывалых пользователей CodeQL, "Playing with Weggli", рассказывает о попытках исследователей разобраться в синтаксисе запросов Weggli и получить желаемые результаты. В следующем github-репозитории содержится пример занятного Weggli-запроса для поиска double-free в ядре Linux. Мы тоже попробуем разобраться и создать свой запрос для поиска этой ошибки в нашем проекте. Но сперва отметим несколько особенностей инструмента. Преимущества: Недостатки: Опишем конструкции Weggli, которыми воспользуемся для написания правила: Правило для поиска double-free в нашем проекте сделаем таким: Weggli, как и ожидалось, не смог обработать внутренние вызовы, и поэтому пропустил double-free через вызов wrapper, выдал false-positive с переопределением указателя во внутреннем вызове reassignment и улыбнул на находке: Составим аналогичный запрос для поиска double-delete — и Weggli хорошо справится с ним, успешно найдет этот простой кейс: Weggli — молодой инструмент и пока его функциональность не столь широка, как у Semgrep или CodeQL, но это отличное начинание. Ждем от Феликса новых идей и улучшений!  Joern — инструмент командной строки для статического анализа исходного кода и байт-кода от ShiftLeft. Реализован на языке Scala, а анализ кода выполняется с использованием языка запросов CPGQL, предметно-ориентированного языка (также основан на Scala), разработанного специально для работы с CPG (code property graph). CPG как понятие в контексте Joern является основообразующим, это описано в научной работе самих разработчиков — "Modeling and Discovering Vulnerabilities with Code Property Graphs". CPG представляет собой промежуточное представление кода, включает в себя AST проекта, Control Flow граф, информационные потоки и иные необходимые данные для дальнейшего анализа. Реализацию CPG можно найти в github-репозитории ShiftLeft. Одноименный предок Joern со схожей концепцией анализа кода на основе CPG появился еще в далеком 2012 году и был представлен в рамках международной конференции ACSAC'12 (Annual Computer Security Applications Conference) исследователем Fabian Yamaguchi и командой компьютерной безопасности Геттингенского университета. CPG прежней реализации хранился в графовой СУБД Neo4J (в новом Joern заменен на OverflowDB), а в качестве языка запросов выступал Gremlin. На своем веку Joern-old успел обжиться собственной TrophyCase из нескольких десятков CVE. Однако ряд концептуальных недостатков с каждым годом становился все более явным, поэтому разработчики решили переосмыслить текущую реализацию и создать тот Joern, который есть сейчас. Одной из главных особенностей Joern является возможность осуществления taint-анализа без необходимости сборки проекта. Эта мысль также поднимается в цикле статей о Joern, где исследователь сравнивает его работу с CodeQL на примере уже упомянутого CodeQL U-Boot challenge. Joern во многом не уступал в этом "версусе", и порой его запросы выглядели гораздо более компактными и эффективными. Посмотрим, насколько Joern хорош на деле. Но сперва отметим и другие положительные стороны Joern: Недостатки: Теперь попробуем создать собственные запросы для поиска double-free и double-delete. Первой командой, которую запустим в интерактивной оболочке, является importCode, которая создаст новый каталог проекта и сохранит в нем двоичное представление CPG: Объект cpg является корневым всего языка запросов, так что чаще всего обращаться придется именно к нему и его предикатам. Например, хотим получить все вызовы free в проекте: Обратите внимание на первый атрибут id — значение типа Long, которое точно идентифицирует каждый узел графа потока управления (cfgNode в синтаксисе CPGQL). Опробуем же taint-анализ для поиска double-free! Для этого создадим следующий запрос: Здесь мы объявили две переменные — source и sink, которые содержат единственный аргумент free — указатель, который мы хотим проследить по пути "порчи". Этим займется вызов reachableByFlows, который возвращает пути для потоков данных от источника к приемнику. filter (filter step) позволит продолжить обход для всех узлов графа, которые соответствуют его условию. В данном случае условие уберет так называемые "петли" (поток от вызова free к самому себе). Таковы особенности Joern. Выхлоп будет следующий: К сожалению, результат taint-анализа содержит ряд ошибок. Несомненно, он смог обнаружить примитивные кейсы двойного освобождения, кейсы с if-блоком и goto, но не нашел случай с оберткой над free (вызовы wrapper), выдал ложноположительный результат с внутренним переназначением указателя (вызов reassignment). Исследуя, почему произошли такие ошибки, мы выяснили: 1) У Joern нет какой-либо кастомизации taint-конфигурации, кроме назначения непосредственного источника/приемника данных. В секции "Data-Flow Steps" документации не обнаружилось каких-либо примитивов, которыми можно было бы воспользоваться и, к примеру, ограничить пути с учетом собственных требований. Да, это прямой намек на CodeQL и его предикаты isSanitizer и isBarrier, которые позволяют настраивать анализ с заданными условиями. Это могло бы помочь отфильтровать часть ложноположительных путей, не связанных с уязвимостью. 2) Ложноотрицательный результат: Joern не выдал результат с вызовом free в обертке, и было принято решение сделать еще один маленький taint-анализ от аргумента первого вызова wrapper(buf1) (funcs.cpp:56) к аргументу free: Результат оказался странным: То есть, Joern видит в контексте этой ситуации только один вызов free, а значит double-free здесь тоже не может существовать, что неверно. 3) Ложноположительный результат: Внутреннее переназначение указателя (вызов reassignment) на пути между двумя вызовами free защищает от ошибки, но Joern так не посчитал. Исследуя проблему, оказалось, что мы не единственные, кто столкнулся с ней. Было обнаружено открытое issue "Dataflow inconsistency related to reassignment and subcalls" в github-репозитории Joern, где рассматривается крайне схожая ситуация с внутренними вызовами и переназначениями. Будем надеяться, что эта ошибка будет исправлена разработчиками и улучшит качество taint-анализа. Ложноположительные результаты также могут появляться по той причине, что Joern не имеет представления о том, по какой ветке пойдет поток исполнения, и знать исход условных выражений он не способен. Именно поэтому инструмент предложил все варианты двойных освобождений в вызове conditional_dfree(). Если бы мы имели такой кейс, как: Joern бы отметил, что в одном случае уязвимость существует, а в другом нет, хотя по факту ответ однозначен. Уловка бы сработала, и такой пример оказался бы в результатах.  Несомненно, taint-анализ для поиска ошибок двойного освобождения является наиболее удобным и эффективным выбором, если таковой имеется. Но поскольку в данном случае он не отработал так, как хотелось бы, попробуем углубиться в CPGQL и создать более сложный запрос, не прибегая к taint-конструкциям Joern. В конце концов, у нас в руках сам CPG! Вручную опишем необходимые условия, в которых видим (или не видим) ошибку double-free в рамках нашего проекта. Чем больше ситуаций будет охватывать наше правило, тем качественнее будут результаты. Получилось оно следующим: В сравнении с предыдущими объем этого правила впечатляет. Оно не идеально, поскольку не обрабатывает многие другие кейсы, в которых ошибка двойного освобождения допустима, однако для нашего проекта является достаточным. Правило собирает все возможные вызовы free и чистые обертки над ними, которые удовлетворяют условию, что не содержат переназначений аргумента-указателя на всем протяжении исполнения. Далее для каждого подобного вызова делается ряд проверок. Исследуются, есть ли такие вызовы, которые имеют один и тот же аргумент (в соответствии с его объявлением) в методе. Затем проверяется, есть ли между этими вызовами переназначения указателя, в том числе и во внутренних вызовах между ними. Само переназначение, кстати, в CPG выступает как вызов <operator>.assignment: Удовлетворяющие всем условиям вызовы собираются в лист и выводятся. Результат для нашего проекта будет таким: Очень неплохо! Найдены все ошибки, ложноположительных кейсов нет. Ручная работа с CPG оказалась не напрасной и дала свои плоды.  Для поиска примитивного double-delete можно также воспользоваться reachableByFlows, но в качестве названия метода указать <operator>.delete. Данный кейс успешно найдется: Вот такие результаты! Можно сказать, что Joern — хороший инструмент статического анализа для небольших/средних по объему проектов, с интересным бэкграундом, задумкой, не без изъянов, но с крепкой научной основой. Joern позволил реализовать правила, в которых, ручками исследуя CPG/CFG, можно было учесть очень важные детали, без которых точность поиска была бы значительно ниже. Для инструмента, не требующего сборки проекта, получить подобные результаты — это очень здорово.   MATE представляет собой целый набор инструментов статического анализа кода для C/C++ проектов. В этом обзоре данный toolset является темной лошадкой, разработан командой Galois совместно с DARPA. Релиз состоялся совсем недавно, в конце лета этого года, и инструментарий еще не успел обрасти многочисленными примерами использования, набором правил, воркшопами и каким-либо фидбеком. Однако потенциал MATE достаточно велик, таким широким кругом возможностей может похвастаться не каждый инструмент. Стоит отметить, что на данный момент MATE является программным обеспечением исследовательского уровня, разработчики проводят большую работу, чтобы сделать его более надежным. Как и Joern, MATE в своем анализе формирует граф свойств кода (CPG). Для создания запросов к CPG MATE предоставляет предметно-ориентированный язык на основе Python и SQLAlchemy. Согласно документации MATE, CPG-проекта формируется из следующих позиций: Также в документации можно ознакомиться с таблицей сравнения MATE с другими SAST-инструментами, большинство из которых уже побывали в обзоре, с ограничениями MATE, которые важно учитывать при анализе. Самым важным из них, пожалуй, является обязательное требование предоставлять собираемые исходники, поскольку MATE анализирует LLVM-байткод, и для этого требуется компиляция с clang. Теперь уделим внимание некоторым составным компонентам MATE: Утилита командной строки для взаимодействия с MATE REST API. Точка старта при анализе проекта. Например, компиляция из исходников и создание проекта в среде MATE происходит следующим образом: Далее заходим в веб-интерфейс (http://localhost:3000/builds) и видим наш проект: Проводят автоматический анализ проекта. Детекторы нацелены на поиск конкретных типов уязвимостей, таких как command injection, path traversal, использование неинициализированной памяти, раскрытие указателей и других. В рамках поиска уязвимостей в нашем игрушечном проекте каких-либо ценных точек интереса детекторы MATE не обнаружили, что видно на скриншоте.  Инструмент Flowfinder визуализирует CPG проекта и позволяет взаимодействовать с ним. Играет вспомогательную функцию как при анализе результатов POI, так и во время создания запросов. Предназначен для обеспечения межпроцедурного анализа потоков данных программы на высоком уровне абстракции. Предоставляет из себя веб-интерфейс для изучения приложений с помощью механизма символьного исполнения Manticore в режиме с недостаточными ограничениями, что означает, что символьному исполнению могут подвергаться отдельные функции. Это очень крутой функционал, поскольку он позволяет оценить поведение функций с заданными аргументами обособленно от всего приложения/системы. В нашем случае встроенный Manticore будет не очень полезен по той причине, что все исследуемые методы в проекте не зависят от подаваемых в аргументы значений. В остальных случаях этот функционал может очень пригодиться. В разделе "Specifying additional symbolic constraints" подробно описаны некоторые конструкции для создания собственных ограничений. UsageFinder — автономное приложение в составе Jupyter Notebook для поиска уязвимостей, возникающих в результате неправильного использования внутренних или внешних API. Результатом его работы является ряд визуализаций тех методов, которые были выбраны для подробного анализа. Например, UsageFinder построит следующую таблицу вызовов для метода df_by_pointer(), в которой видны два вызова free с одним и тем же аргументом (зеленая ячейка):  Также инструмент способен создавать граф условий между вышеперечисленными в таблице вызовами. Например, таким он будет для метода conditional_dfree():  Интерактивный блокнот, интегрированный в среду MATE, позволяет создавать кастомные запросы к CPG проекта на уже упомянутом предметно-ориентированном языке.  Здесь мы и остановимся. С учетом документации по API создадим свой запрос для поиска примитивного кейса с double-delete. Исполним в блокноте такой код: Результат: В запросе мы использовали конструктор PathBuilder, который используется для задания необходимых условий для поиска желаемого пути в CPG-графе. Например, при помощи оператора starting_at можно задать условие, где должен начинаться путь. Аналогичный оператор stopping_at служит для указания, на каких узлах должны заканчиваться пути графа. Возможно странным может показаться название функции _ZdaPv — оно вовсе не похоже на operator delete[]. На этот счет вспоминаем, что MATE работает с LLVM IR, заглядываем в FlowFinder и убеждаемся, что вызов оператора действительно соответствует <function>:llvm-link:@_ZdaPv. Попробуем усложнить наш запрос, добавив ряд проверок и дополнений, чтобы найти все ошибки double-free в нашем мини-проекте. Для этого напишем код на python, следуя схожей логике, что и при создания правила в Joern. То есть, сперва соберем обертки над free без внутренних переназначений указателя. Оценить это нам помогут узлы STORE/LOAD графа. Далее для каждого вызова free или обертки над ним отфильтруем такие, которые имеют один и тот же аргумент, убедимся, что между ними не было переназначений, в том числе и во внутренних вызовах. Правило получилось также объемным, поэтому было решено скрыть его под спойлером. MATE справился со всеми задачами, не выдав ни одного ложноположительного кейса. Отличный результат! В очередной раз убеждаемся, что работать ручками напрямую с CPG очень интересно, и, получается, довольно результативно. MATE предоставил нам много возможностей для качественного анализа и однозначно заслуживает популяризации. В статье мы подробно описали ряд open-source инструментов статического анализа кода, оценив их работу путем создания собственных правил для поиска уязвимостей двойного освобождения. Результаты оказались довольно неплохими, а потенциал каждого из инструментов позволяет расширять и улучшать эти правила, чтобы они охватывали все больше проблемных участков в коде.  Мы не будем отдавать преференций конкретному участнику обзора, так как инструменты имеют ряд своих особенностей, преимуществ и ограничений, которые в совокупности для одних специалистов будут более, чем приемлемы, а для других могут показаться недостаточными/неподходящими. Надеемся, что этот обзор оказался полезным, и со временем к SAST будет все меньше скепсиса. И напоследок опрос мнений! У нас в Digital Security SAST является неотъемлемой частью гибридного подхода в услугах по поиску уязвимостей. Исследователь безопасности