Всем привет. Сегодня я поведаю о дыре в безопасности одного открытого проекта — от выявления до устранения, а также как решая заявку с багом, невольно стал исследователем уязвимостей.
Всё началось с заявки. Заявка на импортозамещённом рабочем месте, поэтому считаю оправданной некоторую бестолковость и нерасторопность первой и последующих линий (включая меня), которые не сразу осознали проблему и пытались решить её традиционными методами. Суть заявки — пропали принтеры в офисном пакете onlyoffice. Ребята пришли на рабочее место ногами, благо это было недалеко, и убедились — действительно, пользователь вызывает диалог печати, а там только встроенная печать в
.pdf
. Поступили просто: снесли принтер, настроили заново, перезагрузились для профилактики. Запустили офис — принтеры есть? Есть. Закрываем заявку.Потом ещё подобная заявка, а затем снова и снова, да на разных рабочих местах. В общем, добрались эти заявки и до меня.
Первым делом попытаемся локализовать проблему. Она хитрая и не всегда проявляется, однако стабильный повтор всё-таки получить удаётся. Если onlyoffice вызывается для обработки документа из одного отечественного программного продукта, при этом офис не загружен в фоне, а стартует в первый раз — то принтеры пропадают. Если офис был запущен ранее, то всё хорошо — все принтеры доступны и можно спокойно печатать. В какие места заглядывал, как прокрастинировал, что читал, какие тупиковые процедуры производил — опущу. Это неинтересно, да и не документировалось никак. В конце концов, выяснил, что наш экземпляр onlyoffice, помимо документа во внутреннем каталоге этого отечественного продукта, держит открытыми ещё пару находящихся рядом библиотек, а именно
libffi.so.6
и libcrypto.so.1.1
, все остальные библиотеки он открывает в местах дислокации, определённых сборщиком дистрибутива ОС.Теперь посмотрим в сторону отечественного программного продукта. Здесь всё правильно — симпатичный веб-интерфейс нашпигованный ajax и вдобавок небольшой агент, который прозрачно для пользователя скачивает документ с последующей передачей штатному обработчику content-type графического окружения нашего российского линукса. При сохранении/закрытии документа, этот агент заливает его обратно на сервер. А теперь причина проблемы — агент скачивает файл в свой внутренний каталог, в недрах
$HOME
пользователя и вызывает, в нашем случае, onlyoffice. Во внутреннем каталоге агент держит некоторое количество стандартных библиотек, вероятно, не полагаясь на качество имеющихся в операционной системе. В свою очередь, onlyoffice поступает нехорошо и подгружает из текущего каталога эти библиотеки, которые, видимо, из-за внутренних различий в реализации или версий, не работают должным образом. Хорошо что ещё в корку не валятся.▍ Диагноз поставлен, выбираем план лечения
Первая мысль — надо обратиться к производителю отечественного продукта в рамках техподдержки, пусть либо каталог поменяют, либо уберут эти кривые либы. Эта мысль в корне неправильная, ведь они в своём каталоге имеют право делать что угодно и сторонний софт должен игнорировать содержимое, кроме документа, переданного на обработку.
Второй вариант — можно отредактировать скрипты-запускалки
/usr/bin/onlyoffice-desktopeditors
либо /usr/bin/xdg-open
, вставив в самом начале переход в домашний каталог пользователя и откорректировав пути обрабатываемых файлов. Вроде как рабочий вариант, я даже некоторое время им пользовался, но проблема в том, что данную процедуру придётся делать всегда после каждого обновления xdg-utils или onlyoffice.Вариант третий — сделать симлинки необходимых _штатных_ библиотек в домашний каталог офиса (
/opt/onlyoffice/desktopeditors/
). Дело в том, что я уже прогнал onlyoffice через strace и обнаружил довольно необычный порядок загрузки библиотек — они сначала ищутся в его папках, потом в текущем каталоге, а затем уже и в стандартных для FHS местах. Скриншот ниже. Вариант с симлинками выглядит и ощущается как «костыль» и, по сути, им и является, учитывая, что при работе задействуется порядка сотни библиотек.Вариант четыре — (после дальнейшего изучения ситуации с путями поиска библиотек) в библиотеке libascdocumentscore.so утилитой chrpath изменить значение RPATH. Вычислил эту либу довольно быстро, проверил лекарство на стенде — всё корректно. Только этот вариант имеет те же проблемы, что и номер два — необходимость повторного «лечения» при обновлении офисного пакета.
Последний вариант, самый правильный, это исправить баг в onlyoffice руками разработчиков. Тем более что это не баг, а целая дыра в безопасности.
▍ Переквалифицируем «дело» с бага на дыру в безопасности
Все, наверное, знают, чем грозит точка в путях переменной $PATH — про это написано во всех учебниках/методичках/СТР-К и выбито на священных скрижалях самой Эви Немет, хотя, на мой взгляд, возможность эксплуатации данной уязвимости потерялась ещё в прошлом веке.
Схожая уязвимость может возникнуть, если также бездумно поступить с переменной $LD_LIBRARY_PATH. Данная переменная позволяет пользователю манипулировать путями, где загрузчик ищет разделяемые библиотеки. Таким образом, возможно, вместо стандартной библиотеки, случайно или нет, подцепить вредоносную. Это классический «Троянский конь».
Но, помимо вышеописанных методов навредить системе самостоятельно, имеется возможность получить нестандартные пути поиска библиотек в секциях
RPATH/RUNPATH
бинарных файлов с исполняемым кодом. Под данными бинарниками подразумеваются не только исполняемые файлы, но и библиотеки. Данный инструмент позволяет разработчику повлиять на поведение динамического компоновщика, разумеется, с благими целями, например, заменить стандартную библиотеку своим, доверенным или более продвинутым вариантом. Здравый смысл полагает, что это крайняя мера и надо чётко осознавать, для чего её потребовалось задействовать.Допустим, у нас затесалась точка в RPATH (а она таки затесалась), то каким образом злоумышленник может эксплуатировать данный баг? Всё довольно просто — на общем файловом ресурсе кладём рядом с ворохом
.docx/.xlsx
документов свой вариант библиотеки, заманиваем на этот ресурс пользователя и даём ему возможность открыть любой из этих офисных файлов. Пользователь открывает документ и сам того не ведая, загружает эту «троянскую» библиотеку. При задействовании каких-либо функций из этой библиотеки будет выполняться зловредный код с правами пользователя. И не обязательно это должен быть файловый сервер — вполне рабочий вариант с документами и библиотекой на флэшке или CD/DVD-диске.Ну и чтобы не быть голословным, набросаю простейший демонстрационный код с эксплуатацией уязвимости. Этот код я спрятал под спойлер, хотя функционал у него даже меньше чем у моего любимого «молдавского вируса» :)
Демонстрашка трояна
Можно взять полноценную библиотеку, например, libm, и произвести модификацию любой часто употребимой функции. После компиляции получим вполне рабочий вариант, который будет выполнять написанный разработчиками код с нашими дополнениями.
Ещё один вариант, который существенно сократит демонстрационный исходник, это вместо полноценной библиотеки написать минимальную заглушку. А вместо того, чтобы дожидаться вызова какой-либо её функции, использовать конструкторы — специальные инициализационные функции, выполняющиеся перед тем, как объект DSO будет использован в другом коде. Т.е. наша злобная функция (evilcode) будет гарантированно выполнена, как только будет подгружен наш вариант библиотеки.
исходный код — evil.c
содержимое файла vers
компиляция производится следующим образом:
На выходе получаем библиотеку
Как хорошо видно, наша библиотека успешно подхватывается и несколько раз выполняется функция evilcode.
Ещё один вариант, который существенно сократит демонстрационный исходник, это вместо полноценной библиотеки написать минимальную заглушку. А вместо того, чтобы дожидаться вызова какой-либо её функции, использовать конструкторы — специальные инициализационные функции, выполняющиеся перед тем, как объект DSO будет использован в другом коде. Т.е. наша злобная функция (evilcode) будет гарантированно выполнена, как только будет подгружен наш вариант библиотеки.
исходный код — evil.c
#include <stdio.h>
int evilcode()
{
printf("\nHello! I'm a Trojan horse :)\n\n");
return 0;
}
содержимое файла vers
GLIBC_2.5.5 {
};
компиляция производится следующим образом:
$ gcc -Wall -Werror -fPIC -Wl,-init,evilcode,-soname,libselinux.so.1,-version-script,vers -shared -o libselinux.so.1 evil.c
На выходе получаем библиотеку
libselinux.so.1
, которую размещаем рядом с документом. Производим запуск (Важно! Пробуем на версии onlyoffice ниже 7.3.3):Как хорошо видно, наша библиотека успешно подхватывается и несколько раз выполняется функция evilcode.
▍ Работа по устранению
Итак, серьёзность угрозы осознана, варианты устранения прикинули, осталось только этот баг (угрозу) устранить. Я опять вернулся к термину «баг», т.к. существование в изолированной сети и обилие других приоритетных производственных задач отодвигают меня от интересной, но непрофильной деятельности. Для избавления от бага буду, как ни странно, использовать третий вариант — создание симлинков. Хоть я и характеризовал его как «костыль», но он позволит купировать проблему пропадания принтеров, пока я буду привлекать к решению проблемы производителя (пятый вариант). Что самое важное — этот костыль выдержит прилёт любых обновлений.
Почему сразу не пойти по чистому 5-му варианту? Есть причины — проект свободный и тут либо ждать, когда у разработчиков изменятся приоритеты, либо самому погружаться и коммитить необходимое :) В любом случае временные рамки я предсказать не решусь. Есть ещё небольшой «чит» основанный на том, что этот баг влияет на безопасность — возможно, это позволит немного надавить на разработчиков и ускорить получение исправлений пакета.
Ну а теперь хронология устранения. Насколько был правильным путь, я до сих пор не имею представления. Возможно, что-то стёрлось из памяти ввиду кажущейся незначительности. Постараюсь ничего не забыть.
Примерно в декабре 2022 года захожу на форум onlyoffice и пишу сообщение с броским заголовком «Security hole — library from cwd». Прикладываю пруфы в виде дампов strace и кратенького описания. Кто-то из команды поддержки душевно благодарит, на всякий случай скрывает часть мессаги (плохо скрыли, через CSS — в исходниках html всё видно) и удаляется для изучения приложенных логов. Потом дописывают, что да, баг подтверждаем, и внутри команды мы ему присвоили номер
60244
.Через месяц я вспоминаю про свой топик и пишу повторный вопрос — как дела? чем помочь, можно уже CVE открывать? Получаю ответ, что безопасность это наш приоритет и мы работаем над этим багом в поте лица.
Подождав ещё пару месяцев и вдохновившись статьёй Получение идентификатора CVE решил, что времени прошло достаточно и уже самое время стать Хакером с большой буквы :). На удивление в MITRE информацию приняли быстро и завели заветную CVE-2022-48422. Немного удивился оперативности, т.к. вроде zero-day не принято публиковать без согласования с автором, но предположил, что хватило той переписки на форуме, где уязвимость была признана разработчиками (есть уточнение в конце статьи).
Потом эта CVE перетекла в NIST NVD CVE-2022-48422, где получила
Base Score: 7.8 HIGH
. Однако формулировки мне совсем не нравятся, и, вероятно, это мой косяк: писал самый минимум и пренебрёг заполнением каких-то метаданных в формах — положился на тамошних специалистов, которые в этой области покруче меня. Думал, что после анализа доведут описание до совершенства.Дальнейшие попытки откорректировать информацию были безуспешны. Мне кажется, ввиду огромного потока информации её успевают только регистрировать с минимальной обработкой :)
Ещё примерно через месяц, в серединке апреля я решил немного пошевелить разработчиков и запулил на форум простейший код троянской библиотеки, который приводил выше. Традиционно код убрали — в этот раз качественно. Каково же было моё удивление, когда они написали, что в версии 7.3.3 уже всё устранили — попробуй и отпишись.
Разумеется, я попробовал и подтвердил исправление уязвимости. На этом «кейс» считаю официально закрытым.
▍ Выводы
Ну что хочу здесь написать — было весьма интересно заглянуть в новое направление. Немного потешил своё самолюбие и помог сделать хорошее дело. Времени убил уйму, хотя всё могло закончиться и раньше: я порылся на гитхабе onlyoffice и нашёл крохотный коммит с исправлением, который датирован 23.02.2023 :)