Содержание статьи
Vim — это бесконечно кастомизируемая среда, которая подходит для решения огромного перечня задач. Кто-то им просто редактирует файлы, а кто-то модифицирует, пока не получится IDE для какого-то из языков программирования. Благодаря такой гибкости Vim остается одним из самых популярных редакторов. Он предустановлен на большей части современных дистрибутивов Linux, поэтому уязвимость в нем потенциально интересна.
INFO
Уязвимость обнаружил Армин Размжоу (Armin Razmjou) в середине этого года. Ей присвоен номер CVE-2019-12735: «уязвимость выполнения произвольного кода в Vim и Neovim». Под угрозой оказались версии Vim, которые не содержат патча 8.1.1365, и версии Neovim ниже 0.3.6.
Причина бага в том, что функция source
обрабатывает файлы вне защищенного окружения. Это позволяет злоумышленнику выполнить любые команды, которые доступны в Vim.
Стенд
Для начала поднимем стенд с уязвимыми версиями Vim и Neovim. Будем использовать контейнер Docker с Debian.
$ docker run --rm --hostname vimrce --name vimrce --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it debian /bin/bash
Устанавливаем все необходимые зависимости.
$ apt update && apt install -y nano build-essential cmake wget unzip pkg-config libtool libtool-bin gettext git gdb libncurses5-dev libncursesw5-dev strace ltrace
Такая портянка нужна, потому что мы будем компилировать дистрибутивы редакторов из исходников. Скачиваем уязвимые версии: для Vim это все, что ниже версии 8.1.1365, а для Neovim — не выше 0.3.5.
$ cd ~
$ git clone https://github.com/vim/vim.git --depth=1 --branch=v8.1.1364
$ git clone https://github.com/neovim/neovim.git --depth=1 --branch=v0.3.5
Компилим и устанавливаем Vim. Включаем флаги для добавления отладочной информации.
$ cd ~/vim
$ sed -i 's@#STRIP = /@STRIP = /@' src/Makefile
$ CFLAGS="-g -DDEBUG" ./configure
$ make
$ make install
То же самое проделываем и для Neovim.
$ cd ~/neovim
$ make CMAKE_EXTRA_FLAGS="-g"
$ make install
Теперь нам нужно создать конфигурационные файлы, в которых надо активировать modeline. Для Vim это .vimrc
в домашней директории, а для Neovim — init.vim
.
$ echo "set modeline" > ~/.vimrc
$ mkdir -p ~/.config/nvim/
$ echo "set modeline" > ~/.config/nvim/init.vim
Стенд готов. Можешь запустить редакторы и проверить их работоспособность.
INFO
Чтобы выйти из Vim без сохранения результатов редактирования файла, нужно перейти в нормальный режим с помощью Esc
и ввести :q!
. Ура, ты спасен!
Детали уязвимости
Для начала давай разберемся, что такое modeline
. В Vim существует четыре основных режима работы: обычный режим, режим вставки, командный режим и визуальный режим.
Для перехода в обычный режим нужно нажать Esc
, а с помощью :
можно войти в командный. В нем ты можешь выполнять команды, встроенные в Vim или предоставляемые плагинами. Это могут быть самые разные действия: настройка среды и рабочих файлов, функции, связанные с обработкой текста, команды оболочки и тому подобное.
Не прописывать команды каждый раз вручную и настроить среду Vim под свои нужды помогает файл .vimrc
. По аналогии с .bashrc
он выполняется каждый раз при запуске Vim. Если такой файл находится в корневом каталоге текущего пользователя, то он будет загружен автоматически.
Это все, конечно, удобно, но что, если нужно переопределить какие-то настройки для конкретного типа файлов или вообще в пределах одного документа? Тут на помощь и приходит modeline. Этот режим позволяет определить в открываемом файле опции его редактирования. Настройки задаются напрямую в файле, по дефолту интерпретируется первая и последняя строка. Если они соответствуют шаблону, то Vim выполняет их.
В некоторых современных дистрибутивах Linux этот режим включен по умолчанию. Если версия твоего редактора в зоне риска, то набери команду :set modeline?
. Увидишь в ответ nomodeline
— считай, что ты в безопасности и уязвимость на тебя не распространяется.
Существует два формата указания опций в modeline. Первый — короткий.
[любой_текст]{пробел_или_таб}{vi:|vim:|ex:}[пробел_или_таб]{опции}
В качестве опций
указывается список необходимых настроек, разделенных пробелом или двоеточием. Каждая часть перед символом двоеточия — это аргумент для :set
. Например, часто задают кастомную ширину строки, размер табуляции и замену табов на пробелы.
vim:tw=80 ts=4 et
Второй формат — расширенный.
[любой_текст]{пробел_или_таб}{vi:|vim:|ex:}[пробел_или_таб]se[t] {опции}:[любой_текст]
В этом формате те же самые опции будут выглядеть следующим образом:
/* vim: set textwidth=80 tabstop=4 expandtab: */
Разумеется, в целях безопасности в modeline можно использовать не все настройки.
Например, попробуем поменять кодировку, в которой работает редактор. За это отвечает опция enc
.
/* vim: set enc=foo: */
/src/option.c
4544: /* Disallow changing some options from modelines. */
4545: if (opt_flags & OPT_MODELINE)
4546: {
4547: if (flags & (P_SECURE | P_NO_ML))
4548: {
4549: errmsg = _("E520: Not allowed in a modeline");
4550: goto skip;
4551: }
Помимо обычных опций, допускается использование выражений.
/* vim: set fdm=expr fde=getline(v\:lnum)=~'{'?'>1'\:'1': */
Все выражения выполняются в режиме песочницы (sandbox).
В ней допускается применение только простейших «безопасных операций».
Обратимся к сорцам. Проверяет, можно ли запустить выражение в песочнице, функция check_secure
.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»