Vim/Neovim 基于 modeline 的多个任意代码执行漏洞分析(CVE-2002-1377、CVE-2016-1248、CVE-2019-12735)
作者:fenix@知道创宇 404 实验室
日期:2019 年 6 月 11 日
英文版本:https://paper.seebug.org/956/
前言
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 |
<span class="cm">/* vim:set foldmethod=expr: */</span> <span class="cm">/* vim:set foldexpr=confirm(libcall("/lib/libc.so.6","system","/bin/ls"),"ms_sux"): */</span> |
利用 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 |
<span class="m">00000000</span>: 2f2f <span class="m">2076</span> 696d 3a20 <span class="m">7365</span> <span class="m">7420</span> <span class="m">6674</span> 3d00 // vim: <span class="nb">set</span> <span class="nv">ft</span><span class="o">=</span>. <span class="m">00000010</span>: <span class="m">2165</span> <span class="m">6368</span> 6f5c <span class="m">2070</span> 776e <span class="m">6564</span> 203a 200a !echo<span class="se">\ </span>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
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/952/