Vim/Neovim Arbitrary Code Execution via Modelines
github.com
github.com
On Android and iOS, any application is supposedly running in its own sandbox, where it can't just randomly access files used by the rest of the system (including other applications).
The web is obviously strongly based around a security model where a website ultimately can't do much in terms of information access (eg, a properly designed website's information is protected from other websites) .. as I understand it, this was not always the case (we're not using ActiveX anymore, right?).
In the desktop space, this pattern is supposedly supported by Flatpak, and I think the macOS store (though I'm not sure about this—I haven't actually used either).
Meanwhile, when a random developer decides to run `make` or `npm install` or `mvn install` or even `vim foo.txt`, they're essentially trusting their entire computer to whoever created the content in their working directory.
Will there be some revolution at some point that encourages people to somehow isolate their commandline activities to different realms, thus limiting the scope of any attack like this?
But that fails for developers (which is more or less synonymous with "CLI users"). Development is all about data and fine grained tools, not apps.
You check out a file with git, then edit it with vim, and build it with gcc, which pulls in headers generated with a python script, itself having been configured with, I dunno, cmake gadgetry. Where do you draw the boundaries here?
I mean, you can draw a big circle around all your development activities. Products have been invented that do that. They're called "IDE's", and are sort of the metaphorical opposite of the command line tools we're discussing, and not really a solution to the people choosing to use this environment.
Alternatively, you can view the whole development system as a sandbox. Do your work in a separate VM or docker instance, for example. Some people actually do that (in particular folks with windows desktops who need linux tooling will recognize this), and while it's not generally considered a security technique, it certainly could be.
None of this should not require access to things like my web browser, my ~/.config directory, some directory like ~/build/go containing an unrelated project.
> They're called "IDE's"
IDEs don't exist for security reasons, but I think you can at least use the existence of these things as evidence that there is a sensible way of scoping development activities. I imagine it wouldn't be a major hindrance if IDEs did provide this sandbox functionality, where you could still launch a shell within the IDE, and that shell would be in a mount/pid namespace controlled by the IDE.
Personally, I want something that applies not just to development, but general computer use. If you go back 15 years, it was probably fairly typical for someone to expect to download an run an "EXE" file. Now the expectation is that a website or phone app is used instead, which has fewer security implications.
Surely there must be some path for moving to something more secure when it comes to commandline usage.
It does! I want to configure git and vim globally, I want to start a development server and use my browser to access it. I want to netcat a file because IT policies restrict ssh...
I would really like some simple way to limit what build scripts can do. For example, they shouldn't be able to read and write arbitrary files in my home directory, even though they should be able to read /usr/lib and the like. They shouldn't be able to contact the Internet (or even the LAN) without explicit permission. They shouldn't be able to do anything with my display unless I permit it.
Is there some command available for Linux that lets me set up a quick sandbox so I can detect and stop trojans in build scripts? Virtual machines, chroots, containers, and Docker are all good but they don't solve this problem. SELinux has the potential to solve this but it's extremely complex. Instead of "./configure && make", I want to type something like "sandbox ./configure && sandbox make"; sandboxed build scripts should work the same as if they're outside the sandbox.
Of course, we should be aware that exploits are quite likely to exist, since much of Linux was designed without a notion of namespaces.
For me, the main issue with docker in terms of security is that it's not clear what security you're giving up when running containers (eg, various projects expect you to do `docker run -v /var/run/docker.sock:/var/run/docker.sock ...`, and things like `docker-compose` essentially specify arbitrary invocations of containers). When ways of escaping are discovered, they get fixed, since those escaping mechanisms are not really considered part of the execution model for containers.
I would guess the biggest hurdle in using such systems is the complexity and expertise required in drafting the specific rules that should apply in your threat model.
Though docker does not completely isolate the build process, it definitely limits writable local directories to those explicitly mounted into a container.
An extra bonus is that docker also installs on Windows and on MacOS and is controlled seamlessly, so our developers are free to choose whatever OS is needed for native development.
Maybe? I don't really think it's an opposite thing anymore. Most of my projects are written/browsed/debugged in an IDE but not built/configured/etc. in the IDE, and the testing, git, docker, code review, etc. work just happen in a console instance or web browser next to the IDE.
For example: I mark ~/school/operating-systems as a development directory. I place git, vim, wget, curl, and make on the blacklist (potentially globally-configured). Additionally I whitelist ~/.config/git for git and ~/.vim for vim. This allows core commands that we consider 'safe' like cd, mv to be run without restrictions - presumably they are only being used manually or from sandboxes tools like Make. But when I execute git from anywhere inside ~/school/operating-systems, it will only be able to R/W inside that directory and within the ~/.config/git directories. Meanwhile read access to system folders lets Make etc. work properly.
With a single global configuration + directory-specific overrides, it remains pretty straightforward and doesn't require a huge time investment like AppArmor/SeLinux do. Meanwhile all non-dev work remains unaffected. If you need to download and build a random program, just mark it as a dev directory and you're pretty safe. It's not perfect, but better than nothing
I don't recall the exact configuration format, but it's something really simple, like
whitelist ~/src/*Have you heard of Plash?
http://www.cs.jhu.edu/~seaborn/plash/html/
While the software itself is outdated, and the design is fundamentally limited by the goal of compatibility with existing Unix programs, I think it's a good proof-of-concept of how you could design a sandboxed command line, especially if you were building everything from scratch.
Basically, any filename argument you pass to a command is no longer just a string; it becomes a capability granting that command access to that file.
So:
- Check out with git: `git clone https://url foo` includes `foo`, so you're giving it permission to create a directory named `foo`. Other git commands don't take the repo root as an argument. If you're redesigning the world, perhaps they should. Alternately, git could have a custom sandbox that identifies the directory containing .git and grants access to the whole thing.
Note: In Plash you have to stick in `=>` to grant read-write access, but I'll ignore syntax details since I'm thinking more in terms of broad strokes of a hypothetical design.
- Edit with Vim: `vim foo.c` gives permission to `foo.c`. (Vim also supports opening new files within the editor; in theory that could be redirected to some sort of powerbox, though it's harder than with GUIs.)
- Configure with cmake: `cmake .` would grant permission to read/write the current directory.
- Run make: You'd want syntax like `make .` rather than implying the current directory. (Again, Plash has a solution for this but I don't care about the specifics.)
- Generate headers with a Python script: make would invoke the script, passing it the input/output paths as capabilities.
- Build with GCC: One of the Plash examples is `gcc -c foo.c => -o foo.o`.
Will Vim be allowed to read `~/.vimrc`?
If not, how should I configure Vim system-wide?
(If the answer is "with a daemon", then: is Vim allowed to connect to the daemon and read its configuration?)
If yes, why?
(If the answer is "because you configured the system so", then: how is this different from SELinux?)
Just kidding. :)
The thing is that access to shared state is hard. Any access is a potential security threat.
Finding a balance between functionality and security is one of those areas that I fear are in the NP-hard-equivalent area of system design.
"su - project1"
Usually I check over things like make files and am immediately concerned if I'm told to run a "build.sh" (especially as root.)
On android the security is kind of an illusion anyway, it's pretty common for apps to either find ways to escape it or trick the user into granting permissions that the apps abuse. The worst offenders are often preinstalled by the hardware vendor anyway and since you don't usually control the OS are impossible to remove.
These sound like security bugs, which should obviously be addressed, but "there are bugs" shouldn't be a reason to dismiss the entire goal of isolation-based security. When it comes to the browser, there are obviously occasional issues to do with colouring of visited links, sandbox escapes, information exposure due to speculative CPU execution. Overall though, what's important is that there is a security model and that the implementation issues are addressed.
Recently posted on HN: http://http.jameshfisher.com/2019/05/26/i-can-see-your-local...
I hope we can at least put it off until it's a real problem and not turn the world upside down for a largely theoretical one. I also doubt any sort of sandboxing will be effective and practical to run software from untrusted sources.
To a lesser degree, seccomp is similar but you don't have an easy way of applying a profile to a binary (instead the binary would need to self-apply the profile or you'd need to come up with a separate way of running programs so that a profile would be auto-applied).
The problem is that users want to be able to edit any file on the filesystem with their text editor -- if you don't allow vim to edit files in /etc/ then admins won't be able to do their job (though, please use "sudo -e" instead of "sudo vi").
It can, in two ways: the current context (say, user_dev_t) needs execute permissions on the declared type of the executable (say, git_exec_t), and additionally, you can declare a type transition to be performed per calling context.
To keep with the git example (I admit it's a bit contrived), you could label your .git directory as git_repo_t and deny access for both user_t (normal user context) and user_dev_t (user dev context). Then, you can define a type transition as above (user_dev_t -> git_exec_t => git_t), and allow only the context git_t access to git_repo_t files. With this setup in place:
- git, as called from within user_dev_t works as normally
- git, as called from user_t, has no defined type transition so will try to access git_repo_t under the user_t context, which will fail.
https://ps.uci.edu/~franklin/doc/netcat.html https://stackoverflow.com/questions/15351646/how-to-re-compi...
https://android.googlesource.com/platform/external/netcat/+/...
Thankfully I've had 'set nomodeline' in my config ever since I found out about this insane option.
Also comes in handy for working with files that have an incorrect extension, for whatever reason, or different formatting from all the other files of that type that you work with. (Per-project formatting stuff can often by solved with .dir-locals.el, but not always.)
Usually just to `set ft=zsh' to set a filetype on a file with no (or incorrect) file extensions. Which syntax highlighting and other plugins rely on to activate.
For example, I use them on a TODO.md file to set it as a `task` filetype which is a markdown-friendly todo list: https://github.com/irrationalistic/vim-tasks whereas in Github I want it to render as markdown only so I keep the file extension `.md`.
Another usecase is system/software user config files which have all sorts of file extensions (ie, `.rc` or '.conf') but use a variety of internal formatting (ie, json). These files never leave your personal machines or arrive that way, in my experience at least.
I'd imagine they'd also stick out like a sore-thumb at the bottom of most files if they were maliciously inserted but I'm sure there's some exceptions (targets who don't know what Vim modelines are).
$ cat foo.sh
#!/usr/bin/env zsh
echo $SHELL
Which most editors understand. $ file foo.sh
foo.sh: a /usr/bin/env zsh script text executable, ASCII text
$ vim --clean -c ':set filetype?' foo.sh
...
filetype=zsh
This ALSO ensures that the correct shell is invoked on your script, instead of your current or default shell. $ ./foo.sh
/usr/local/bin/zshEven when it's actual part of ZSH script I'm only using `ft=zsh` when it's a file I'm sourcing, not intended as a runnable script by itself. For example: `~/.exports` which includes my PATH and env variables, as I also import it into `.bashrc` in addition to `.zshrc`.
Moreover, they're super handy for loading the proper syntax highlighting when working with template engines (e.g. Jinja2) to produce anything other than HTML.
I'd argue the use of a modeline to convey that is poor.
# vim: set fileencoding=iso-8859-1
Trivia bit: the Python interpreter understands Vim and Emacs modelines as encoding declaration for source files: https://www.python.org/dev/peps/pep-0263/#defining-the-encod...Another use case was specifying the indentation scheme for the file for weird conventions like "2 space indentation with 4-column tabs" (first level is two spaces, second level is one tab, third level is one tab plus two spaces...)
Fortunately now everything's UTF-8 and weird indentation schemes are uncommon.
I.e. this likely only affects a small number of users and the devs have done as much as they can about a risky feature: Off by default and whoever changes the option should know about the risks.
Of course it's still a valid vulnerability and it's good that it's been found and fixed. But it's likely not a big deal.
EDIT: I probably was reading about CVE-2007-2438, another mode-line based RCE attack.
1: https://github.com/neovim/neovim/commit/4553fc5e 2: https://github.com/vim/vim/commit/5357552
I do find something endlessly interesting in these kinds of vulnerabilities, for no other reason than the fact that I can appreciate the hunt for them. I wouldn't really think to look at Vim as an attack vector, and even if I did, I wouldn't even know where to start.
Just do "set nomodeline" and move on. I've had it disabled for the past ~10 years because of previous CVEs like this (such as CVE-2007-2438).
set modelines=0
and a comment referencing CVE-2007-2438, which was another sandbox escape via modelines.Seems prudent.
modelines, modeline [off]
Read the first and last few lines of each file for ex commands.
This option will never be implemented.[0]
[0] https://netbsd.gw.com/cgi-bin/man-cgi?vi+1.i386+NetBSD-8.0
And never heard of modeline even though using vim for over 20 years.
Disclaimer: I use emacs.
I would be interested to hear the historical reason for having such generic modeline execution in the first place. It seems a little out of place in text file editing.
Your company places this as a modeline in all its source files? If so why does your place of work enforce modelines in its source?
This is in basically every source code file just below the shebang.
I'd push back on this hard if I was told to do it.
The modelines issue (this exact issue) was widely publicised well over a decade ago (long enough that I don't remember when).
I vaguely recall it showing up in an early gentoo security alert (pre-2005?) which is how I became aware of it.
How does this become a new security issue now?
I understand the security concerns (arbitrary code execution just by loading an arbitrary file into vim).
My question was: this issue was caught, diagnosed, widely publicised and the configuration fix for it was widely deployed - nearly 15 (fifteen!) years ago.
So why is it cropping up as a "new" security issue now?
I compile it every week (I use it as my IDE -- term buffers and all).
Just in case someone here is a heavy user and is worried about this.
https://www.cvedetails.com/vulnerability-list.php?vendor_id=...
https://www.gnu.org/software/emacs/manual/html_node/emacs/Sa...
Heartbreak at this announcement means you were assuming it wasn't a vector for security weaknesses.