
Технический анализ и PoC-эксплойт для CVE-2021-26084 — уязвимости внедрения OGNL в Atlassian Confluence Server. Включает разбор первопричины, сопоставление уязвимых параметров и рекомендации по обнаружению.
Версии < 6.13.23
6.14.0 ≤ Версии < 7.4.11
7.5.0 ≤ Версии < 7.11.6
7.12.0 ≤ Версии < 7.12.5
Confluence использует фреймворк для сопоставления URL с Java-классами, создавая то, что называется «action». URL-адреса action заканчиваются на «.action» и определяются в файле xwork.xml в confluence-.jar, а также в файле atlassian-plugin.xml в JAR-файлах встроенных плагинов. Каждый action содержит как минимум один атрибут name, задающий имя action, атрибут class, определяющий Java-класс, реализующий action, и как минимум один элемент result, который определяет шаблон Velocity, отображаемый после вызова action, в зависимости от результата. Типичные возвращаемые значения — «error», «input», «success», но может использоваться любой результат, если он соответствует элементу result в XWork XML. Action также могут содержать атрибут method, позволяющий вызвать конкретный метод заданного Java-класса. Если method не указан, вызывается метод doDefault(). Ниже приведена запись action для createpage-entervariables:

Метод doEnter() класса com.atlassian.confluence.pages.actions.PageVariablesAction обрабатывает запросы к doenterpagevariables.action и возвращает значения «error», «input», «success». Это приводит к отображению соответствующего шаблона Velocity.
Здесь значение атрибута «name» элемента action соответствует пути /.action, а элемент определяет, какой шаблон будет отображаться в ответе в зависимости от error/success и т.д. Таким образом, в данном примере достаточно обратиться к /pages/doenterpagevariables.action, чтобы отобразить файл шаблона Velocity.

Мы видим, как шаблон Velocity отображается в HTML-странице.

Подставляем имя тега в шаблоне в качестве параметра и замечаем, что значения берутся из параметров запроса и возвращаются в ответе.

Использование #tag: #tag(“attribute1”, “attribute2”, “attribute3”).
Эти атрибуты в процессе рендеринга извлекаются методом AbstrctTagDirective.applyAttributes().

#tag ("Hidden" "name='queryString'" "value='ahihihi'")
После извлечения атрибутов методом AbstrctTagDirective.applyAttributes() они передаются в AbstrctUITag.doEndTag() → AbstractUITag.evaluateParams().

Затем они передаются в WebWorkTagSupport.findValue() → OgnlValueFinder.findValue() в виде expression.

После этого выражение попадает в SafeExpressionUtil.isSafeExpression().

Здесь expression компилируется, проверяется по чёрному списку и помещается в кеш. В процессе компиляции expression преобразуется в ASTConst, при вычислении которого возвращается ранее скомпилированная строка.

Когда в expression добавляется символ «'» из входных данных, он экранируется в HTML-сущность.

Точка экранирования находится в HtmlAnnotationEscaper.annotatedValueInsert().

Здесь вызывается метод ognl.JavaCharStream.readChar(), который обрабатывает Unicode-escape последовательности. Поэтому передача «\u0027» преобразуется в «'». Это позволяет обойти экранирование и добавить OGNL-выражение.

Однако необходимо обойти чёрный список OGNL, представленный ниже.

Обойти его можно с помощью доступа к массивам вместо использования метода «getClass» или атрибута «.class».
queryString=aaa\u0027%2b#{\u0022\u0022[\u0022class\u0022]}%2b\u0027bbb

Мониторинг всех HTTP-запросов, в которых компонент пути URI запроса содержит одну из строк из столбца «URI Path» следующей таблицы:
| URI Path | Vulnerable Parameters |
|---|---|
| /users/darkfeatures.action | featureKey |
| /users/enabledarkfeature.action | featureKey |
| /users/disabledarkfeature.action | featureKey |
| /login.action | token |
| /dologin.action | token |
| /signup.action | token |
| /dosignup.action | token |
| /pages/createpage-entervariables.action | queryString, linkCreation |
| /pages/doenterpagevariables.action | queryString |
| /pages/createpage.action | queryString |
| /pages/createpage-choosetemplate.action | queryString |
| /pages/docreatepagefromtemplate.action | queryString |
| /pages/docreatepage.action | queryString |
| /pages/createblogpost.action | queryString |
| /pages/docreateblogpost.action | queryString |
| /pages/copypage.action | queryString |
| /pages/docopypage.action | queryString |
| /plugins/editor-loader/editor.action | syncRev |
При обнаружении такого запроса необходимо проверить метод HTTP-запроса. Если метод POST, то найти соответствующие Vulnerable Parameters из таблицы в теле HTTP-запроса; если метод GET, то искать параметры в URI запроса. Проверить, содержит ли значение любого из Vulnerable Parameters строку «\u0027» или её URL-кодированную версию. Если да, то трафик считается вредоносным, и возможна атака с эксплуатацией этой уязвимости.