Баг в конце марта обнаружил латвийский исследователь Янис Крустс (Jānis Krusts) из IT Centrs. Ей присвоили номер CVE-2019-3398. Баг сводится к тому, что отсутствие фильтрации имен файлов в DownloadAllAttachmentsOnPageAction
дает возможность атакующему выйти из временной директории и записать файл с произвольным содержимым в любую папку, доступную для записи.
Для проведения атаки требуется учетная запись с правами создания и редактирования записей. Уязвимы множество версий в разных ветках приложения. Все, что вышло аж с Confluence 2.0.0 вплоть до Confluence 6.6.12, уязвимо. Также проблемными оказались версии с 6.7.0 до 6.12.3, версии с 6.13.0 по 6.13.3, с 6.14.0 до 6.14.2 и с 6.15.0 по 6.15.2 включительно. Более детальный список можно посмотреть в официальной базе знаний.
Стенд
У Confluence простой инсталлятор, который работает в большинстве современных операционных систем. Для тестов я возьму Confluence 6.9.0. Если в качестве основной ОС у тебя Windows, то можно воспользоваться инсталлятором. Однако я рекомендую более универсальный метод — запустить контейнер Docker. Не забывай прокидывать необходимые порты.
$ docker run --rm --name=confluence.vh --hostname=conflvh -p 8090:8090 -p 8091:8091 atlassian/confluence-server:6.9.0
Инсталлируем приложение.
Далее создаем администратора системы. Для тестирования уязвимости нам понадобится пользователь с возможностью загружать аттачи. Создаем его, и стенд готов.
Детали уязвимости CVE-2019-3398
Как ты уже знаешь, проблема кроется в загрузке всех прикрепленных к записи файлов. Создадим произвольную запись и загрузим к ней несколько файлов.
Обрати внимание на кнопку Download All — она скачивает все файлы. Перед этим они любезно упаковываются в ZIP, который и отправляется пользователю. В моем случае ссылка имеет вид http://confluence.vh:8090/pages/downloadallattachments.action?pageId=65601
. Из названия экшена видно, что за скачивание отвечает класс DownloadAllAttachmentsOnPageAction
. Он расположен в файле confluence/WEB-INF/lib/confluence-6.9.0.jar
. Для декомпиляции я воспользуюсь утилитой JD-GUI версии 1.5.
/com/atlassian/confluence/pages/actions/DownloadAllAttachmentsOnPageAction.java
01: package com.atlassian.confluence.pages.actions;
02:
...
18: public class DownloadAllAttachmentsOnPageAction extends AbstractPageAwareAction {
19: AttachmentManager attachmentManager;
Чтобы сразу определить, где конкретно проблема, можно заглянуть в этот же файл, только в пропатченной версии, например 6.12.4.
/confluence-6.12.4/com/atlassian/confluence/pages/actions/DownloadAllAttachmentsOnPageAction.java
19: public class DownloadAllAttachmentsOnPageAction extends AbstractPageAwareAction {
20: AttachmentManager attachmentManager;
...
32: public String execute() throws Exception {
33: List<Attachment> latestAttachments = this.attachmentManager.getLatestVersionsOfAttachments(getPage());
34: for (Attachment attachment : latestAttachments) {
35: File tmpFile = new File(getTempDirectoryForZipping(), ConfluenceFileUtils.extractFileName(attachment.getFileName()));
/confluence-6.9.0/com/atlassian/confluence/pages/actions/DownloadAllAttachmentsOnPageAction.java
18: public class DownloadAllAttachmentsOnPageAction extends AbstractPageAwareAction {
19: AttachmentManager attachmentManager;
...
31: public String execute() throws Exception {
32: List<Attachment> latestAttachments = this.attachmentManager.getLatestVersionsOfAttachments(getPage());
33: for (Attachment attachment : latestAttachments) {
34: File tmpFile = new File(getTempDirectoryForZipping(), attachment.getFileName());
Как видишь, второй параметр, который отвечает за имя аттача, в новой версии метода execute
фильтруется при помощи extractFileName
.
/confluence-6.12.4/com/atlassian/confluence/util/io/ConfluenceFileUtils.java
82: public static String extractFileName(String pathname) {
83: if (pathname == null)
84: return null;
85: return (new File(pathname)).getName();
86: }
Значит, проблема в имени. Посмотрим на содержимое класса DownloadAllAttachmentsOnPageAction
, а именно метод execute
.
/com/atlassian/confluence/pages/actions/DownloadAllAttachmentsOnPageAction.java
31: public String execute() throws Exception {
32: List<Attachment> latestAttachments = this.attachmentManager.getLatestVersionsOfAttachments(getPage());
33: for (Attachment attachment : latestAttachments) {
Сначала данные всех файлов попадают в массив latestAttachments
, затем функция for
перебирает его элементы и читает содержимое каждого файла. Обрати внимание, что создается объект типа File. Он указывает на временный файл, имя которого эквивалентно имени аттача.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»