
Семинар по CodeQL, посвящённый CVE-2021-21380
В этом практикуме мы будем использовать синтаксические и семантические рассуждения, чтобы найти SQL-инъекцию в компоненте API рейтингов платформы XWiki, описанную в CVE-2021-21380.
Пожалуйста, по возможности выполните этот раздел до начала практикума.
Установите Visual Studio Code.
Установите расширение CodeQL для Visual Studio Code.
Вам не нужно устанавливать CodeQL CLI: расширение сделает это за вас.
Клонируйте этот репозиторий:
git clone https://github.com/advanced-security/codeql-workshop-cve-2021-21380.git
xwiki-platform-CVE-2021-21380.zip в представлении Explorer и выберите команду CodeQL: Set Current Database.Cmd/Ctrl+Shift+P) найдите и выполните команду CodeQL: Install Pack Dependencies.github в поле, чтобы отфильтровать список.cve-2021-21380-exercises, cve-2021-21380-exercises-tests, cve-2021-21380-solutions и cve-2021-21380-solutions-tests.Практикум разделён на несколько упражнений, знакомящих с поддержкой языка QL для Java, и завершается итоговым запросом для поиска известной SQL-инъекции. В этих упражнениях вы научитесь:
На этом практикуме мы будем искать известные уязвимости SQL-инъекций в компоненте API рейтингов XWiki Platform. Такие уязвимости могут возникать в приложениях, когда информация, контролируемая внешним пользователем, попадает в код приложения, который небезопасно формирует SQL-запрос и выполняет его.
Известная SQL-инъекция, рассматриваемая на этом практикуме, описана в GHSA-79rg-7mv3-jrr5 в GitHub Advisory Database. Чтобы найти SQL-инъекцию и возможные варианты, мы перейдём к следующим подзадачам:
В первых нескольких упражнениях мы будем рассуждать о синтаксической информации, используя абстрактное синтаксическое дерево (AST), чтобы определить:
Найдите все методы с именем getAverageRating и их объявляющий тип в программе, дополнив запрос exercise1.ql
java предоставляет класс Method для рассуждений о методах в программе.Method предоставляет предикаты-члены getName и hasName для рассуждений об имени метода.Method предоставляет член getDeclaringType для рассуждений о типе, который объявляет метод.Решение можно найти в запросе exercise1.ql
Решение упражнения 1 возвращает список методов. Некоторые из них определены в интерфейсе RatingsManager, а некоторые — в классах AbstractRatingsManager и RatingsScriptService.
На основе информации, возвращённой запросом, и документации по компонентам XWiki можно сделать вывод, что:
Role, и реализации, аннотированной Component.ScriptService, становится доступным вики-страницам через скрипты.Найдите все классы, аннотированные аннотацией Component, дополнив запрос exercise2.ql.
Обратите внимание, что полное имя типа аннотации — org.xwiki.component.annotation.Component.
this в /характеристическом предикате/.Class предоставляет метод getAnAnnotation для получения связанных аннотаций.Annotation предоставляет предикат-член getType для рассуждений о его типе.Type предоставляет предикаты-члены getName и hasName для рассуждений об имени типа.RefType, представляющий классы и интерфейсы, предоставляет предикаты-члены getQualifiedName и hasQualifiedName для рассуждений о полном имени ref-типа.Решение можно найти в запросе exercise2.ql
Найдите все компоненты, которые реализуют интерфейс ScriptService, дополнив запрос excercise3.ql
Class предоставляет предикат-член getASuperType для рассуждений о супертипах класса, то есть типах, которые он расширяет (extends) или реализует (implements).Решение можно найти в запросе exercise3.ql
На данный момент мы синтаксически определили методы, которые могут вызываться пользователем и чьи параметры далее на практикуме мы будем считать источниками недоверенных данных.
В следующем упражнении мы исследуем и определим возможные стоки. Из результатов упражнения 1 можно сделать вывод, что одна из реализаций вызывает метод getAverageRatingFromQuery. С помощью похожего запроса мы можем найти объявляющие типы getAverageRatingFromQuery, что позволяет установить, что реализация в классе AbstractRatingsManager формирует SQL-выражение, передаваемое методу search.
Метод search реализован в зависимости, и поэтому его реализация недоступна. В следующих упражнениях мы будем использовать доступную информацию о типах, чтобы определить этот вызов метода search и его объявляющий тип.
Найдите все вызовы метода search и определите его объявляющий тип, дополнив запрос exercise4.ql
MethodCall предоставляет нам средства для рассуждений о вызовах методов.MethodAcccess предоставляет предикат-член getMethod для рассуждений о цели вызова метода.Решение можно найти в запросе exercise4.ql
Найдите все вызовы методов, объявленных интерфейсом XWikiStorageInterface (с полным именем com.xpn.xwiki.store.XWikiStoreInterface), дополнив запрос exercise5.ql
Ключевое слово instanceof можно использовать, чтобы указать, что значение принадлежит множеству значений, представляемых типом.
Например, чтобы найти все вызовы методов интерфейса:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Решение можно найти в запросе exercise5.ql
На данный момент мы синтаксически описали возможные источники и стоки с помощью QL. Чтобы определить, передаётся ли информация между этими точками программы, мы проведём семантический анализ программы с использованием taint tracking. Стандартные библиотеки поддерживаемых нами языков предоставляют два механизма потоков данных:
DataFlow поддерживает поток информации с сохранением значений.TaintTracking поддерживает поток информации даже при изменении значений.Последний представляет интерес, поскольку при уязвимостях инъекций, таких как SQL-инъекция, недоверенные данные часто становятся частью более крупного выражения, которое затем обрабатывается.
На этом практикуме мы переиспользуем существующую конфигурацию taint tracking для SQL-инъекций и расширим её смоделированными источниками и стоками, чтобы найти SQL-инъекцию. Чтобы понять, как можно расширить конфигурацию, начнём с рассмотрения её определения ниже.
class QueryInjectionFlowConfig extends TaintTracking::Configuration {
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or
node.getType() instanceof BoxedType or
node.getType() instanceof NumberType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
}
}
Оба предиката — isSource и isSink — используемые конфигурацией для идентификации этих мест в программе, применяют ключевое слово instanceof, которое мы уже видели. Оба класса RemoteFlowSource и QueryInjectionSink являются abstract-классами.
Это распространённый паттерн, который вы встретите в стандартных библиотеках; такой паттерн позволяет расширять множество значений, представляемых классами RemoteFlowSource и QueryInjectionSink.
QL class, расширяющий abstract class, не уточняет множество значений, представляемых суперклассом, а добавляет к суперклассу значения, представляемые подклассом.
В следующих упражнениях мы реализуем эти подклассы и составим итоговый запрос.
Расширьте множество значений RemoteFlowSource параметрами публичных методов классов-компонентов, определённых в упражнении 3, дополнив запрос exercise6.ql.
Ключевое слово instanceof можно использовать, чтобы указать, что значение принадлежит множеству значений, представляемых типом.
Например, чтобы найти все вызовы методов интерфейса:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Решение можно найти в запросе exercise6.ql
Расширьте множество значений QueryInjectionSink аргументами вызовов методов интерфейса хранилища, определённого в упражнении 5, дополнив запрос exercise7.ql
Формула exists позволяет вводить временную переменную, о которой можно рассуждать в области действия exists.
В следующем примере выражение exists используется для сокращения множества методов до вызываемых методов.
from Method m
where m.hasName("foo") and exists(MethodCall ma | ma.getMethod() = m)
select m
Класс MethodCall предоставляет предикат-член getQualifer для рассуждений о квалификаторе обращения к методу.
Решение можно найти в запросе exercise7.ql
Объедините ваши решения из предыдущего упражнения в итоговое решение, дополнив запрос exercise8.ql.
Решение можно найти в запросе exercise8.ql
XWikiStoreInterface. Расширьте сток, включив в него прямое использование реализаций интерфейса. Класс Class предоставляет предикат-член extendsOrImplements, а класс Method — предикат-член overridesOrInstantiates, которые могут помочь.