Vim/Neovim 基于 modeline 的多个任意代码执行漏洞分析(CVE-2002-1377、CVE-2016-1248、CVE-2019-12735)

前言

Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用,和 Emacs 并列成为类 Unix 系统用戶最喜欢的文本编辑器。Neovim 是一个基于 vim 源代码的重构项目。

2019 年 06 月 04 日,Vim & neovim 被曝出任意代码执行漏洞。攻击者通过诱使受害者使用 vim 或者 neovim 打开一个精心制作的文件,可以在目标机器上执行任意命令。

该漏洞是由于启用了 modeline 模式导致的,Vim & neovim 历史上也多次曝出和 modeline 相关的漏洞。

原作者已经分析的很清楚了,本文权当总结一下,顺便对历史曝出的多个漏洞做一次完整的分析。(在 vim 环境下,neovim 类似)

modeline 详解

既然都是和 modeline 相关的漏洞,那就有必要知道 modeline 是什么。

vim 一共有 4 种模式:正常模式、插入模式、命令模式、可视模式。

在正常模式中,按下 : 键,就可以进入命令模式。在命令模式中可以执行一些输入并执行一些 vim 或插件提供的指令,就像在 shell 里一样。这些指令包括设置环境、文件操作、调用某个功能、执行命令等等。例如设置不显示行号:

如果有很多偏好设置,每次打开文件都手动设置就会显得很繁琐,这时候 .vimrc 就派上用场了,在启动 vim 时,当前用户根目录下的 .vimrc 文件会被自动加载。

.vimrc 中的设置会对打开的所有文件生效,不便于对单个文件作个性化设置,modeline 应运而生。

vim 的 modeline 可以让你针对每个文件进行文件级别的设置,这些设置是覆盖当前用户的 .vimrc 中的设置的。vim 默认关闭了 modeline,在 .vimrc 末尾追加 set modeline 即可打开。

如果 modeline 打开,vim 在打开文件时会解析文件开头及末尾符合一定格式的设置行。

格式一:

格式二:

为了安全考虑,在 modeline 的设置中只支持 set 命令。

特殊的,foldexpr,formatexpr,includeexpr,indentexpr,statusline,foldtext 等选项的值可以是一个表达式,如果选项是在 modeline 中设置,表达式在沙箱中执行。沙箱实质上就是对表达式所能实现的功能做了限制,如在沙箱中不能执行 shell 命令、不能读写文件、不能修改缓冲区等等,如下:

vim 对于沙箱的实现也很简单。

沙箱检查函数 check_secure():

在 libcall、luaeval 等危险指令的开头进行沙箱检查,如果发现在沙箱中调用,直接 return 掉。

历史曝出的几个 rce 漏洞中,CVE-2002-1377 和 CVE-2019-12735 都是由于存在部分指令没有检查沙箱,导致在 modeline 模式中被滥用从而任意命令执行。下面将一一分析。

CVE-2002-1377

2002 年曝出的 vim 任意代码执行漏洞,影响 6.0、6.1 版本。太过古老,环境难以重现,简单说下原理。PoC 如下:

1
2
/* vim:set foldmethod=expr: */
/* vim:set foldexpr=confirm(libcall("/lib/libc.so.6","system","/bin/ls"),"ms_sux"): */

利用 libcall 指令调用 libc 库中的 system 函数实现任意命令执行。

现在添加了沙箱检查,modeline 下已经用不了 libcall 了:

CVE-2016-1248

8.0.0056 之前的 vim 未正确验证 filetype、syntax 、keymap 选项的值,受害者在 modeline 开启下打开特制的文件,则可能导致执行任意代码。

从 github 克隆代码,checkout 到 v8.0.0055 分支,编译安装。.vimrc 的配置如下:

验证 PoC :

1
2
00000000: 2f2f 2076 696d 3a20 7365 7420 6674 3d00  // vim: set ft=.
00000010: 2165 6368 6f5c 2070 776e 6564 203a 200a !echo\ pwned : .

set verbose=20开启所有日志,看下调用链:

autocommand 即“自动命令”,在发生某些事件时自动执行,类似于钩子函数。

比如我们在命令模式中输入 :set syntax=python, vim 就会在相应目录中寻找和 python syntax 相关的 vmscript 并加载。

如果我们在 modeline 中设置了 filetype 或者 syntax,会执行 au! FileType * exe "set syntax=" . expand("<amatch>") 自动完成上述过程。首先删除所有和 FileType 相关联的自动命令,然后调用 exe (即 execute) 执行 set syntax=filetype。execute 用于执行一个表达式字符串,由于未对 filetype 过滤,造成了命令注入。

相关代码在 /usr/local/share/vim/vim80/syntax/syntax.vim:

patch 8.0.0056 增加了对名称的校验。

CVE-2019-12735

最近刚曝出来,影响 Vim < 8.1.1365,Neovim < 0.3.6。和 CVE-2002-1377 原理类似,找到了一个新的绕过沙箱执行命令的点。source 指定的定义如下:

:so! filepath 可以从一个文件加载 vim 命令。

构造 PoC,将待执行的命令放在 text 部分,so! % 加载当前文件。

[text]{white}{vi:|vim:|ex:}[white]{options}

补丁对 source 指令添加了沙箱检查。

总结

Windows 记事本都任意代码执行了,Vim 怎么能被比下去 … 漏洞无处不在,谨慎打开任何来历不明文件。

参考链接

https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md

https://github.com/vim/vim/commit/d0b5138ba4bccff8a744c99836041ef6322ed39a

0%