Summary
Wanted to try this some time ago, and forgot about it, saw some activity around this code recently, and it came to my mind again :D
When using treesitter with injections, Neovim will try to inject the captured language by its name
|
local has_lang = language.require_language(lang, nil, true) |
Loading a language basically means searching for that name in the runtime directory and loading the shared object found in that location.
Neovim concatenates the name of the language as is, making it vulnerable to path traversal, an attacker can use this to load a malicious shared object and execute any arbitrary commands.
|
local fname = 'parser/' .. vim.fn.fnameescape(lang) .. '.*' |
|
local paths = a.nvim_get_runtime_file(fname, false) |
PoC
Create a malicious shared object to test the vulnerability
// gcc -shared -o exploit.so -fPIC exploit.c
#include<stdio.h>
#include<stdlib.h>
static void poc() __attribute__((constructor));
void poc() {
system("touch /tmp/pwn-nvim");
}
Enable treesitter with injections using arbitrary language name capture, an easy way is by using the https://github.com/nvim-treesitter/nvim-treesitter/ plugin and enabling it for the markdown filetype and the "highlight" feature.
Open a file that supports injections given this language name, for example markdown,
and set the language of the code-block to a path that points to the malicious shared object.
exploit.md
This file will load a malicious shared object.
```../../exploit
Code block with the language name pointing to the malicious shared object
```
The path can be derived from any runtime path, like the parser
directory from where nvim-treesitter is installed, and traverse the runtime directory to where the malicious shared object is.
For example, having a directory structure like:
- /tmp/exploit.so
- /tmp/nvim-tresitter/
- /exploit.md
will require the language name to be ../../exploit
References
Impact
An attacker can use this vulnerability to execute arbitrary commands.
Exploiting this vulnerability requires the user to download two files, the malicious shared object and the markdown file,
the attacker can put several paths with common destinations for downloads (like ~/Downloads
or home), and common destinations for runtime directories and/or plugins in the markdown file to guess from where to load the malicious shared object.
Summary
Wanted to try this some time ago, and forgot about it, saw some activity around this code recently, and it came to my mind again :D
When using treesitter with injections, Neovim will try to inject the captured language by its name
neovim/runtime/lua/vim/treesitter/languagetree.lua
Line 152 in f08051c
Loading a language basically means searching for that name in the runtime directory and loading the shared object found in that location.
Neovim concatenates the name of the language as is, making it vulnerable to path traversal, an attacker can use this to load a malicious shared object and execute any arbitrary commands.
neovim/runtime/lua/vim/treesitter/language.lua
Lines 19 to 20 in f08051c
PoC
Create a malicious shared object to test the vulnerability
Enable treesitter with injections using arbitrary language name capture, an easy way is by using the https://github.com/nvim-treesitter/nvim-treesitter/ plugin and enabling it for the markdown filetype and the "highlight" feature.
Open a file that supports injections given this language name, for example markdown,
and set the language of the code-block to a path that points to the malicious shared object.
The path can be derived from any runtime path, like the
parser
directory from where nvim-treesitter is installed, and traverse the runtime directory to where the malicious shared object is.For example, having a directory structure like:
will require the language name to be
../../exploit
References
Impact
An attacker can use this vulnerability to execute arbitrary commands.
Exploiting this vulnerability requires the user to download two files, the malicious shared object and the markdown file,
the attacker can put several paths with common destinations for downloads (like
~/Downloads
or home), and common destinations for runtime directories and/or plugins in the markdown file to guess from where to load the malicious shared object.