Убойный текст. Выполняем произвольный код в Vim и Neovim

Сегодня поговорим об уязвимости в редакторе Vim и его побратиме Neovim. Для ее эксплуатации не нужно никаких привилегий, пользователю достаточно открыть специально сформированный файл, который выполнит произвольный код на целевой системе.

Vim — это бесконечно кастомизируемая среда, которая подходит для решения огромного перечня задач. Кто-то им просто редактирует файлы, а кто-то модифицирует, пока не получится IDE для какого-то из языков программирования. Благодаря такой гибкости Vim остается одним из самых популярных редакторов. Он предустановлен на большей части современных дистрибутивов Linux, поэтому уязвимость в нем потенциально интересна.

Уязвимость обнаружил Армин Размжоу (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

Стенд готов. Можешь запустить редакторы и проверить их работоспособность.

Готовый стенд с уязвимыми версиями Vim и Neovim

 

Детали уязвимости

Для начала давай разберемся, что такое modeline. В Vim существует четыре основных режима работы: обычный режим, режим вставки, командный режим и визуальный режим.

Отображение режима работы редактора 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 можно использовать не все настройки.

Не все опции доступны для изменения через modeline

Например, попробуем поменять кодировку, в которой работает редактор. За это отвечает опция enc.

/* vim: set enc=foo: */

Опция enc недоступна для изменения через modeline
/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).

Выражения из modeline выполняются в песочнице

В ней допускается применение только простейших «безопасных операций».

Обратимся к сорцам. Проверяет, можно ли запустить выражение в песочнице, функция check_secure.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *